Browse Source

chore: 优化代码生成

喵你个旺呀 1 year ago
parent
commit
d9a7c34e65

+ 36 - 49
Admin.NET/Admin.NET.Core/Entity/SysCodeGenConfig.cs

@@ -25,6 +25,13 @@ public partial class SysCodeGenConfig : EntityBase
     [SugarColumn(ColumnDescription = "字段名称", Length = 128)]
     [Required, MaxLength(128)]
     public virtual string ColumnName { get; set; }
+    
+    /// <summary>
+    /// 主键
+    /// </summary>
+    [SugarColumn(ColumnDescription = "主键", Length = 8)]
+    [MaxLength(8)]
+    public string? ColumnKey { get; set; }
 
     /// <summary>
     /// 实体属性名
@@ -45,6 +52,13 @@ public partial class SysCodeGenConfig : EntityBase
     [SugarColumn(ColumnDescription = "字段描述", Length = 128)]
     [MaxLength(128)]
     public string? ColumnComment { get; set; }
+    
+    /// <summary>
+    /// 数据库中类型(物理类型)
+    /// </summary>
+    [SugarColumn(ColumnDescription = "数据库中类型", Length = 64)]
+    [MaxLength(64)]
+    public string? DataType { get; set; }
 
     /// <summary>
     /// .NET数据类型
@@ -86,7 +100,7 @@ public partial class SysCodeGenConfig : EntityBase
     /// </summary>
     [SugarColumn(ColumnDescription = "外键显示字段", Length = 64)]
     [MaxLength(64)]
-    public string? FkColumnName { get; set; }
+    public string? FkDisplayColumns { get; set; }
 
     /// <summary>
     /// 外键链接字段
@@ -101,6 +115,13 @@ public partial class SysCodeGenConfig : EntityBase
     [SugarColumn(ColumnDescription = "外键显示字段.NET类型", Length = 64)]
     [MaxLength(64)]
     public string? FkColumnNetType { get; set; }
+    
+    /// <summary>
+    /// 父级字段
+    /// </summary>
+    [SugarColumn(ColumnDescription = "父级字段", Length = 128)]
+    [MaxLength(128)]
+    public string? PidColumn { get; set; }
 
     /// <summary>
     /// 字典编码
@@ -108,6 +129,20 @@ public partial class SysCodeGenConfig : EntityBase
     [SugarColumn(ColumnDescription = "字典编码", Length = 64)]
     [MaxLength(64)]
     public string? DictTypeCode { get; set; }
+    
+    /// <summary>
+    /// 查询方式
+    /// </summary>
+    [SugarColumn(ColumnDescription = "查询方式", Length = 16)]
+    [MaxLength(16)]
+    public string? QueryType { get; set; }
+    
+    /// <summary>
+    /// 是否是查询条件
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否是查询条件", Length = 8)]
+    [MaxLength(8)]
+    public string? WhetherQuery { get; set; }
 
     /// <summary>
     /// 列表是否缩进(字典)
@@ -131,20 +166,6 @@ public partial class SysCodeGenConfig : EntityBase
     public string? WhetherSortable { get; set; }
 
     /// <summary>
-    /// 是否是查询条件
-    /// </summary>
-    [SugarColumn(ColumnDescription = "是否是查询条件", Length = 8)]
-    [MaxLength(8)]
-    public string? QueryWhether { get; set; }
-
-    /// <summary>
-    /// 查询方式
-    /// </summary>
-    [SugarColumn(ColumnDescription = "查询方式", Length = 16)]
-    [MaxLength(16)]
-    public string? QueryType { get; set; }
-
-    /// <summary>
     /// 列表显示
     /// </summary>
     [SugarColumn(ColumnDescription = "列表显示", Length = 8)]
@@ -164,20 +185,6 @@ public partial class SysCodeGenConfig : EntityBase
     [SugarColumn(ColumnDescription = "导入", Length = 8)]
     [MaxLength(8)]
     public string? WhetherImport { get; set; }
-    
-    /// <summary>
-    /// 主键
-    /// </summary>
-    [SugarColumn(ColumnDescription = "主键", Length = 8)]
-    [MaxLength(8)]
-    public string? ColumnKey { get; set; }
-
-    /// <summary>
-    /// 数据库中类型(物理类型)
-    /// </summary>
-    [SugarColumn(ColumnDescription = "数据库中类型", Length = 64)]
-    [MaxLength(64)]
-    public string? DataType { get; set; }
 
     /// <summary>
     /// 是否通用字段
@@ -187,26 +194,6 @@ public partial class SysCodeGenConfig : EntityBase
     public string? WhetherCommon { get; set; }
 
     /// <summary>
-    /// 显示文本字段
-    /// </summary>
-    [SugarColumn(ColumnDescription = "显示文本字段", ColumnDataType = StaticConfig.CodeFirst_BigString)]
-    public string? DisplayColumn { get; set; }
-
-    /// <summary>
-    /// 选中值字段
-    /// </summary>
-    [SugarColumn(ColumnDescription = "选中值字段", Length = 128)]
-    [MaxLength(128)]
-    public string? ValueColumn { get; set; }
-
-    /// <summary>
-    /// 父级字段
-    /// </summary>
-    [SugarColumn(ColumnDescription = "父级字段", Length = 128)]
-    [MaxLength(128)]
-    public string? PidColumn { get; set; }
-
-    /// <summary>
     /// 排序
     /// </summary>
     [SugarColumn(ColumnDescription = "排序")]

+ 79 - 66
Admin.NET/Admin.NET.Core/Service/CodeGen/CustomViewEngine.cs

@@ -6,19 +6,11 @@
 
 namespace Admin.NET.Core.Service;
 
+/// <summary>
+/// 自定义模板引擎
+/// </summary>
 public class CustomViewEngine : ViewEngineModel
 {
-    private readonly ISqlSugarClient _db;
-
-    public CustomViewEngine()
-    {
-    }
-
-    public CustomViewEngine(ISqlSugarClient db)
-    {
-        _db = db;
-    }
-
     /// <summary>
     /// 库定位器
     /// </summary>
@@ -31,72 +23,93 @@ public class CustomViewEngine : ViewEngineModel
     public string NameSpace { get; set; }
 
     public string ClassName { get; set; }
+    
+    public string LowerClassName { get; set; }
 
     public string ProjectLastName { get; set; }
 
-    public List<TableUniqueConfigItem> TableUniqueList { get; set; }
-
-    public string LowerClassName
-    {
-        get
-        {
-            return ClassName[..1].ToLower() + ClassName[1..]; // 首字母小写
-        }
-    }
-
     public string PagePath { get; set; } = "main";
 
-    public bool IsJoinTable { get; set; }
-
-    public bool IsUpload { get; set; }
-
     public string PrintType { get; set; }
 
     public string PrintName { get; set; }
-
-    public List<CodeGenConfig> QueryWhetherList { get; set; }
-
+    
+    public bool HasLikeQuery { get; set; }
+    
+    public bool HasJoinTable { get; set; }
+    
+    public bool HasSetStatus { get; set; }
+    
+    public bool HasEnumField { get; set; }
+    
+    public bool HasDictField { get; set; }
+    
+    public bool HasConstField { get; set; }
+    
     public List<CodeGenConfig> TableField { get; set; }
+    
+    public List<CodeGenConfig> ImportFieldList { get; set; }
+    
+    public List<CodeGenConfig> UploadFieldList { get; set; }
 
-    private List<ColumnOuput> ColumnList { get; set; }
-
-    public string GetColumnNetType(object tbName, object colName)
-    {
-        if (tbName == null || colName == null) return null;
-
-        var config = App.GetOptions<DbConnectionOptions>().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == ConfigId);
-        ColumnList = GetColumnListByTableName(tbName.ToString());
-        var col = ColumnList.Where(c => (config.DbSettings.EnableUnderLine
-            ? CodeGenUtil.CamelColumnName(c.ColumnName, Array.Empty<string>())
-            : c.ColumnName) == colName.ToString()).FirstOrDefault();
-        return col.NetType;
-    }
-
-    public List<ColumnOuput> GetColumnListByTableName(string tableName)
-    {
-        // 多库代码生成切换库
-        var provider = _db.AsTenant().GetConnectionScope(ConfigId != SqlSugarConst.MainConfigId ? ConfigId : SqlSugarConst.MainConfigId);
-
-        // 获取实体类型属性
-        var entityType = provider.DbMaintenance.GetTableInfoList().FirstOrDefault(u => u.Name == tableName);
-        
-        // 因为ConfigId的表通常也会用到主库的表来做连接,所以这里如果在ConfigId中找不到实体也尝试一下在主库中查找
-        if (entityType == null)
+    public List<CodeGenConfig> QueryWhetherList { get; set; }
+    
+    public List<CodeGenConfig> ApiTreeFieldList { get; set; }
+    
+    public List<CodeGenConfig> DropdownFieldList { get; set; }
+    
+    public List<CodeGenConfig> AddUpdateFieldList { get; set; }
+    
+    public List<CodeGenConfig> PrimaryKeyFieldList { get; set; }
+    
+    public List<TableUniqueConfigItem> TableUniqueConfigList { get; set; }
+
+    public List<string> PrimaryKeyNames => PrimaryKeyFieldList.Select(u => u.PropertyName).ToList();
+    
+    public string PrimaryKeysFormat(string separator, string format) => string.Join(separator, PrimaryKeyFieldList.Select(u => string.Format(format, u.PropertyName)));
+    
+    /// <summary>
+    /// 注入的服务
+    /// </summary>
+    /// <returns></returns>
+    public Dictionary<string, string> InjectServiceMap {
+        get
         {
-            if (ConfigId == SqlSugarConst.MainConfigId) return null;
-            provider = _db.AsTenant().GetConnectionScope(SqlSugarConst.MainConfigId);
-            entityType = provider.DbMaintenance.GetTableInfoList().FirstOrDefault(u => u.Name == tableName);
-            if (entityType == null) return null;
+            var text = PrimaryKeysFormat(" && ", "u.{0} == input.{0}");
+            var injectMap = new Dictionary<string, string>();
+            if (UploadFieldList.Count > 0) injectMap.Add(nameof(SysFileService), ToLowerFirstLetter(nameof(SysFileService)));
+            if (DropdownFieldList.Count > 0) injectMap.Add(nameof(ISqlSugarClient), ToLowerFirstLetter(nameof(ISqlSugarClient).TrimStart('I')));
+            if (ImportFieldList.Any(c => c.EffectType == "DictSelector")) injectMap.Add(nameof(SysDictTypeService), ToLowerFirstLetter(nameof(SysDictTypeService)));
+            return injectMap;
         }
-
-        // 按原始类型的顺序获取所有实体类型属性(不包含导航属性,会返回null)
-        return provider.DbMaintenance.GetColumnInfosByTableName(entityType.Name).Select(u => new ColumnOuput
-        {
-            ColumnName = u.DbColumnName,
-            ColumnKey = u.IsPrimarykey.ToString(),
-            DataType = u.DataType.ToString(),
-            NetType = CodeGenUtil.ConvertDataType(u, provider.CurrentConnectionConfig.DbType),
-            ColumnComment = u.ColumnDescription
-        }).ToList();
     }
+
+    /// <summary>
+    /// 服务构造参数
+    /// </summary>
+    public string InjectServiceArgs => InjectServiceMap.Count > 0 ? string.Join(", ", InjectServiceMap.Select(kv => $"{kv.Key} {kv.Value}")) : "";
+   
+    /// <summary>
+    /// 导入唯一性校验配置
+    /// </summary>
+    public List<TableUniqueConfigItem> ImportUniqueConfigList => TableUniqueConfigList.Where(c => c.Columns.All(x1 => ImportFieldList.Any(x2 => x2.PropertyName == x1))).ToList();
+    
+    /// <summary>
+    /// 增改唯一性校验配置
+    /// </summary>
+    public List<TableUniqueConfigItem> AddUpdateUniqueConfigList => TableUniqueConfigList.Where(c => c.Columns.All(x1 => UploadFieldList.Any(x2 => x2.PropertyName == x1))).ToList();
+
+    /// <summary>
+    /// 获取首字母小写字符串
+    /// </summary>
+    /// <param name="text"></param>
+    /// <returns></returns>
+    public string ToLowerFirstLetter(string text) => string.IsNullOrWhiteSpace(text) ? text : text[..1].ToLower() + text[1..];
+    
+    /// <summary>
+    /// 将基本字段类型转为可空类型
+    /// </summary>
+    /// <param name="netType"></param>
+    /// <returns></returns>
+    public string GetNullableNetType(string netType) => Regex.IsMatch(netType, "(.*?Enum|bool|char|int|long|double|float|decimal)[?]?") ? netType.TrimEnd('?') + "?" : netType;
 }

+ 41 - 44
Admin.NET/Admin.NET.Core/Service/CodeGen/Dto/CodeGenConfig.cs

@@ -25,6 +25,11 @@ public class CodeGenConfig
     /// 数据库字段名
     /// </summary>
     public string ColumnName { get; set; }
+    
+    /// <summary>
+    /// 主外键
+    /// </summary>
+    public string ColumnKey { get; set; }
 
     /// <summary>
     /// 实体属性名
@@ -50,6 +55,16 @@ public class CodeGenConfig
     /// .NET类型
     /// </summary>
     public string NetType { get; set; }
+    
+    /// <summary>
+    /// 数据库中类型(物理类型)
+    /// </summary>
+    public string DataType { get; set; }
+    
+    /// <summary>
+    /// 可空.NET类型
+    /// </summary>
+    public string NullableNetType => Regex.IsMatch(NetType ?? "", "(.*?Enum|bool|char|int|long|double|float|decimal)[?]?") ? NetType.TrimEnd('?') + "?" : NetType;
 
     /// <summary>
     /// 作用类型(字典)
@@ -86,27 +101,42 @@ public class CodeGenConfig
     /// </summary>
     [Newtonsoft.Json.JsonIgnore]
     [System.Text.Json.Serialization.JsonIgnore]
-    public string FkColumnName { get; set; }
+    public string FkDisplayColumns { get; set; }
     
     /// <summary>
     /// 外键显示字段
     /// </summary>
-    public List<string> FkColumnList { get; set; }
+    public List<string> FkDisplayColumnList { get; set; }
 
     /// <summary>
     /// 外键显示字段(首字母小写)
     /// </summary>
-    public List<string> LowerFkColumnList => FkColumnList?.Select(name => name[..1].ToLower() + name[1..]).ToList();
+    public List<string> LowerFkDisplayColumnsList => FkDisplayColumnList?.Select(name => name[..1].ToLower() + name[1..]).ToList();
 
     /// <summary>
     /// 外键显示字段.NET类型
     /// </summary>
     public string FkColumnNetType { get; set; }
-
+    
+    /// <summary>
+    /// 父级字段
+    /// </summary>
+    public string PidColumn { get; set; }
+    
     /// <summary>
     /// 字典code
     /// </summary>
     public string DictTypeCode { get; set; }
+    
+    /// <summary>
+    /// 查询方式
+    /// </summary>
+    public string QueryType { get; set; }
+
+    /// <summary>
+    /// 是否是查询条件
+    /// </summary>
+    public string WhetherQuery { get; set; }
 
     /// <summary>
     /// 列表是否缩进(字典)
@@ -124,16 +154,6 @@ public class CodeGenConfig
     public string WhetherSortable { get; set; }
 
     /// <summary>
-    /// 是否是查询条件
-    /// </summary>
-    public string QueryWhether { get; set; }
-
-    /// <summary>
-    /// 查询方式
-    /// </summary>
-    public string QueryType { get; set; }
-
-    /// <summary>
     /// 列表显示
     /// </summary>
     public string WhetherTable { get; set; }
@@ -147,16 +167,6 @@ public class CodeGenConfig
     /// 导入
     /// </summary>
     public string WhetherImport { get; set; }
-    
-    /// <summary>
-    /// 主外键
-    /// </summary>
-    public string ColumnKey { get; set; }
-
-    /// <summary>
-    /// 数据库中类型(物理类型)
-    /// </summary>
-    public string DataType { get; set; }
 
     /// <summary>
     /// 是否是通用字段
@@ -164,29 +174,16 @@ public class CodeGenConfig
     public string WhetherCommon { get; set; }
     
     /// <summary>
-    /// 外键显示字段
+    /// 排序
     /// </summary>
-    [Newtonsoft.Json.JsonIgnore]
-    [System.Text.Json.Serialization.JsonIgnore]
-    public string DisplayColumn { get; set; }
+    public int OrderNo { get; set; }
     
     /// <summary>
-    /// 显示文本字段
-    /// </summary>
-    public List<string> DisplayColumnList { get; set; }
-
-    /// <summary>
-    /// 选中值字段
-    /// </summary>
-    public string ValueColumn { get; set; }
-
-    /// <summary>
-    /// 父级字段
+    /// 获取外键显示值语句
     /// </summary>
-    public string PidColumn { get; set; }
+    /// <param name="tableAlias">表别名</param>
+    /// <param name="separator">多字段时的连接符</param>
+    /// <returns></returns>
+    public string GetDisplayColumn(string tableAlias, string separator = "-") => string.Join(separator, FkDisplayColumnList.Select(name => $"{{{tableAlias}.{name}}}"));
 
-    /// <summary>
-    /// 排序
-    /// </summary>
-    public int OrderNo { get; set; }
 }

+ 10 - 12
Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenConfigService.cs

@@ -32,9 +32,8 @@ public class SysCodeGenConfigService : IDynamicApiController, ITransient
             .Select<CodeGenConfig>()
             .Mapper(u =>
             {
-                u.NetType = (u.EffectType == "EnumSelector" || u.EffectType == "ConstSelector" ? u.DictTypeCode : u.NetType);
-                u.DisplayColumnList = u.DisplayColumn?.Split(",").ToList();
-                u.FkColumnList = u.FkColumnName?.Split(",").ToList();
+                u.NetType = (u.EffectType is "EnumSelector" or "ConstSelector" ? u.DictTypeCode : u.NetType);
+                u.FkDisplayColumnList = u.FkDisplayColumns?.Split(",").ToList();
             })
             .OrderBy(u => new { u.OrderNo, u.Id })
             .ToListAsync();
@@ -52,8 +51,7 @@ public class SysCodeGenConfigService : IDynamicApiController, ITransient
         if (inputList == null || inputList.Count < 1) return;
         inputList.ForEach(e =>
         {
-            e.DisplayColumn = e.DisplayColumnList?.Count > 0 ? string.Join(",", e.DisplayColumnList) : null;
-            e.FkColumnName = e.FkColumnList?.Count > 0 ? string.Join(",", e.FkColumnList) : null;
+            e.FkDisplayColumns = e.FkDisplayColumnList?.Count > 0 ? string.Join(",", e.FkDisplayColumnList) : null;
         });
         await _db.Updateable(inputList.Adapt<List<SysCodeGenConfig>>())
             .IgnoreColumns(u => new { u.ColumnLength, u.ColumnName, u.PropertyName })
@@ -98,16 +96,16 @@ public class SysCodeGenConfigService : IDynamicApiController, ITransient
         {
             var codeGenConfig = new SysCodeGenConfig();
 
-            var YesOrNo = YesNoEnum.Y.ToString();
+            var yesOrNo = YesNoEnum.Y.ToString();
             if (Convert.ToBoolean(tableColumn.ColumnKey))
             {
-                YesOrNo = YesNoEnum.N.ToString();
+                yesOrNo = YesNoEnum.N.ToString();
             }
 
             if (CodeGenUtil.IsCommonColumn(tableColumn.PropertyName))
             {
                 codeGenConfig.WhetherCommon = YesNoEnum.Y.ToString();
-                YesOrNo = YesNoEnum.N.ToString();
+                yesOrNo = YesNoEnum.N.ToString();
             }
             else
             {
@@ -124,10 +122,10 @@ public class SysCodeGenConfigService : IDynamicApiController, ITransient
 
             // 生成代码时,主键并不是必要输入项,故一定要排除主键字段
             codeGenConfig.WhetherRequired = (tableColumn.IsNullable || tableColumn.IsPrimarykey) ? YesNoEnum.N.ToString() : YesNoEnum.Y.ToString();
-            codeGenConfig.QueryWhether = YesOrNo;
-            codeGenConfig.WhetherImport = YesOrNo;
-            codeGenConfig.WhetherAddUpdate = YesOrNo;
-            codeGenConfig.WhetherTable = YesOrNo;
+            codeGenConfig.WhetherQuery = yesOrNo;
+            codeGenConfig.WhetherImport = yesOrNo;
+            codeGenConfig.WhetherAddUpdate = yesOrNo;
+            codeGenConfig.WhetherTable = yesOrNo;
 
             codeGenConfig.ColumnKey = tableColumn.ColumnKey;
 

+ 94 - 114
Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenService.cs

@@ -143,13 +143,13 @@ public class SysCodeGenService : IDynamicApiController, ITransient
 
         var config = App.GetOptions<DbConnectionOptions>().ConnectionConfigs.FirstOrDefault(u => configId.Equals(u.ConfigId));
 
-        var dbTableNames = dbTableInfos.Select(u => u.Name.ToLower()).ToList();
+        // var dbTableNames = dbTableInfos.Select(u => u.Name.ToLower()).ToList();
         IEnumerable<EntityInfo> entityInfos = await GetEntityInfos();
 
         var tableOutputList = new List<TableOutput>();
         foreach (var item in entityInfos)
         {
-            var table = dbTableInfos.FirstOrDefault(u => u.Name.ToLower() == (config.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(item.DbTableName) : item.DbTableName).ToLower());
+            var table = dbTableInfos.FirstOrDefault(u => string.Equals(u.Name, (config!.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(item.DbTableName) : item.DbTableName), StringComparison.CurrentCultureIgnoreCase));
             if (table == null) continue;
             tableOutputList.Add(new TableOutput
             {
@@ -180,7 +180,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
         // 按原始类型的顺序获取所有实体类型属性(不包含导航属性,会返回null)
         return provider.DbMaintenance.GetColumnInfosByTableName(entityType.Name).Select(u => new ColumnOuput
         {
-            ColumnName = config.DbSettings.EnableUnderLine ? CodeGenUtil.CamelColumnName(u.DbColumnName, entityBasePropertyNames) : u.DbColumnName,
+            ColumnName = config!.DbSettings.EnableUnderLine ? CodeGenUtil.CamelColumnName(u.DbColumnName, entityBasePropertyNames) : u.DbColumnName,
             ColumnKey = u.IsPrimarykey.ToString(),
             DataType = u.DataType.ToString(),
             NetType = CodeGenUtil.ConvertDataType(u, provider.CurrentConnectionConfig.DbType),
@@ -198,7 +198,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
         if (entityType == null)
             return null;
         var config = App.GetOptions<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;
 
         // 切库---多库代码生成用
         var provider = _db.AsTenant().GetConnectionScope(!string.IsNullOrEmpty(input.ConfigId) ? input.ConfigId : SqlSugarConst.MainConfigId);
@@ -226,14 +226,14 @@ public class SysCodeGenService : IDynamicApiController, ITransient
         {
             var columnOutput = result[i];
             // 先找自定义字段名的,如果找不到就再找自动生成字段名的(并且过滤掉没有SugarColumn的属性)
-            var propertyInfo = entityProperties.FirstOrDefault(u => (u.GetCustomAttribute<SugarColumn>()?.ColumnName ?? "").ToLower() == columnOutput.ColumnName.ToLower()) ??
+            var propertyInfo = entityProperties.FirstOrDefault(u => string.Equals((u.GetCustomAttribute<SugarColumn>()?.ColumnName ?? ""), columnOutput.ColumnName, StringComparison.CurrentCultureIgnoreCase)) ??
                 entityProperties.FirstOrDefault(u => u.GetCustomAttribute<SugarColumn>() != null && u.Name.ToLower() == (config.DbSettings.EnableUnderLine
                 ? CodeGenUtil.CamelColumnName(columnOutput.ColumnName, entityBasePropertyNames).ToLower()
                 : columnOutput.ColumnName.ToLower()));
             if (propertyInfo != null)
             {
                 columnOutput.PropertyName = propertyInfo.Name;
-                columnOutput.ColumnComment = propertyInfo.GetCustomAttribute<SugarColumn>().ColumnDescription;
+                columnOutput.ColumnComment = propertyInfo.GetCustomAttribute<SugarColumn>()!.ColumnDescription;
                 var propertyType = Nullable.GetUnderlyingType(propertyInfo.PropertyType);
                 if (propertyInfo.PropertyType.IsEnum || (propertyType?.IsEnum ?? false))
                 {
@@ -269,31 +269,22 @@ public class SysCodeGenService : IDynamicApiController, ITransient
             foreach (var assembly in assemblies)
             {
                 var assemblyName = assembly.GetName().Name;
-                if (_codeGenOptions.EntityAssemblyNames.Contains(assemblyName) || _codeGenOptions.EntityAssemblyNames.Any(name => assemblyName.Contains(name)))
+                if (!_codeGenOptions.EntityAssemblyNames.Contains(assemblyName) &&
+                    !_codeGenOptions.EntityAssemblyNames.Any(name => assemblyName!.Contains(name)))
                 {
-                    Assembly asm = Assembly.Load(assemblyName);
-                    types.AddRange(asm.GetExportedTypes().ToList());
+                    continue;
                 }
+
+                Assembly asm = Assembly.Load(assemblyName!);
+                types.AddRange(asm.GetExportedTypes().ToList());
             }
         }
-        bool IsMyAttribute(Attribute[] o)
-        {
-            foreach (Attribute a in o)
-            {
-                if (a.GetType() == type)
-                    return true;
-            }
-            return false;
-        }
-        Type[] cosType = types.Where(o =>
-        {
-            return IsMyAttribute(Attribute.GetCustomAttributes(o, true));
-        }
-        ).ToArray();
+
+        Type[] cosType = types.Where(o => IsMyAttribute(Attribute.GetCustomAttributes(o, true))).ToArray();
 
         foreach (var ct in cosType)
         {
-            var sugarAttribute = ct.GetCustomAttributes(type, true)?.FirstOrDefault();
+            var sugarAttribute = ct.GetCustomAttributes(type, true).FirstOrDefault();
 
             var des = ct.GetCustomAttributes(typeof(DescriptionAttribute), true);
             var description = "";
@@ -310,6 +301,8 @@ public class SysCodeGenService : IDynamicApiController, ITransient
             });
         }
         return await Task.FromResult(entityInfos);
+
+        bool IsMyAttribute(Attribute[] o) => o.Any(a => a.GetType() == type);
     }
 
     /// <summary>
@@ -334,74 +327,38 @@ public class SysCodeGenService : IDynamicApiController, ITransient
 
         // 先删除该表已生成的菜单列表
         List<string> targetPathList;
-        var zipPath = Path.Combine(App.WebHostEnvironment.WebRootPath, "CodeGen", input.TableName);
+        var zipPath = Path.Combine(App.WebHostEnvironment.WebRootPath, "CodeGen", input.TableName!);
         if (input.GenerateType.StartsWith('1'))
         {
             targetPathList = GetZipPathList(input);
-            if (Directory.Exists(zipPath))
-                Directory.Delete(zipPath, true);
+            if (Directory.Exists(zipPath)) Directory.Delete(zipPath, true);
         }
         else
             targetPathList = GetTargetPathList(input);
 
-        var tableFieldList = await _codeGenConfigService.GetList(new CodeGenConfig() { CodeGenId = input.Id }); // 字段集合
-        var queryWhetherList = tableFieldList.Where(u => u.QueryWhether == YesNoEnum.Y.ToString()).ToList(); // 前端查询集合
-        var joinTableList = tableFieldList.Where(u => u.EffectType == "Upload" || u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector").ToList(); // 需要连表查询的字段
-
-        var data = new CustomViewEngine(_db)
-        {
-            ConfigId = input.ConfigId,
-            AuthorName = input.AuthorName,
-            BusName = input.BusName,
-            NameSpace = input.NameSpace,
-            ClassName = input.TableName,
-            PagePath = input.PagePath,
-            TableUniqueList = input.TableUniqueList ?? new(),
-            ProjectLastName = input.NameSpace.Split('.').Last(),
-            QueryWhetherList = queryWhetherList,
-            TableField = tableFieldList,
-            IsJoinTable = joinTableList.Count > 0,
-            IsUpload = joinTableList.Where(u => u.EffectType == "Upload").Any(),
-            PrintType = input.PrintType,
-            PrintName = input.PrintName,
-        };
-        // 模板目录
+        var (tableFieldList, result) = await RenderTemplateAsync(input);
         var templatePathList = GetTemplatePathList(input);
-        var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "template");
-
         for (var i = 0; i < templatePathList.Count; i++)
         {
-            var templateFilePath = Path.Combine(templatePath, templatePathList[i]);
-            if (!File.Exists(templateFilePath)) continue;
-            var tContent = File.ReadAllText(templateFilePath);
-            var tResult = await _viewEngine.RunCompileFromCachedAsync(tContent, data, builderAction: builder =>
-            {
-                builder.AddAssemblyReferenceByName("System.Linq");
-                builder.AddAssemblyReferenceByName("System.Collections");
-                builder.AddAssemblyReferenceByName("System.Text.RegularExpressions");
-                builder.AddUsing("System.Text.RegularExpressions");
-                builder.AddUsing("System.Collections.Generic");
-                builder.AddUsing("System.Linq");
-            });
-            var dirPath = new DirectoryInfo(targetPathList[i]).Parent.FullName;
-            if (!Directory.Exists(dirPath))
-                Directory.CreateDirectory(dirPath);
-            File.WriteAllText(targetPathList[i], tResult, Encoding.UTF8);
+            var content = result.GetValueOrDefault(templatePathList[i]?.TrimEnd(".vm"));
+            if (string.IsNullOrWhiteSpace(content)) continue;
+            var dirPath = new DirectoryInfo(targetPathList[i]).Parent!.FullName;
+            if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
+            _ = File.WriteAllTextAsync(targetPathList[i], content, Encoding.UTF8);
         }
-        if (input.GenerateMenu)
-            await AddMenu(input.TableName, input.BusName, input.MenuPid ?? 0, input.MenuIcon, input.PagePath, tableFieldList);
+
+        if (input.GenerateMenu) await AddMenu(input.TableName, input.BusName, input.MenuPid ?? 0, input.MenuIcon, input.PagePath, tableFieldList);
+        
         // 非ZIP压缩返回空
-        if (!input.GenerateType.StartsWith('1'))
-            return null;
-        else
-        {
-            string downloadPath = zipPath + ".zip";
-            // 判断是否存在同名称文件
-            if (File.Exists(downloadPath))
-                File.Delete(downloadPath);
-            ZipFile.CreateFromDirectory(zipPath, downloadPath);
-            return new { url = $"{App.HttpContext.Request.Scheme}://{App.HttpContext.Request.Host.Value}/codeGen/{input.TableName}.zip" };
-        }
+        if (!input.GenerateType.StartsWith('1')) return null;
+        
+        // 判断是否存在同名称文件
+        string downloadPath = zipPath + ".zip";
+        if (File.Exists(downloadPath)) File.Delete(downloadPath);
+        
+        // 创建zip文件并返回下载地址
+        ZipFile.CreateFromDirectory(zipPath, downloadPath);
+        return new { url = $"{App.HttpContext.Request.Scheme}://{App.HttpContext.Request.Host.Value}/codeGen/{input.TableName}.zip" };
     }
 
     /// <summary>
@@ -409,28 +366,51 @@ public class SysCodeGenService : IDynamicApiController, ITransient
     /// </summary>
     /// <returns></returns>
     [DisplayName("获取代码生成预览")]
+    // ReSharper disable once MemberCanBePrivate.Global
     public async Task<Dictionary<string, string>> Preview(SysCodeGen input)
     {
-        var tableFieldList = await _codeGenConfigService.GetList(new CodeGenConfig() { CodeGenId = input.Id }); // 字段集合
-        var queryWhetherList = tableFieldList.Where(u => u.QueryWhether == YesNoEnum.Y.ToString()).ToList(); // 前端查询集合
-        var joinTableList = tableFieldList.Where(u => u.EffectType == "Upload" || u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector").ToList(); // 需要连表查询的字段
+        var (_, result) = await RenderTemplateAsync(input);
+        return result;
+    }
 
-        var data = new CustomViewEngine(_db)
+    /// <summary>
+    /// 渲染模板
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    private async Task<(List<CodeGenConfig> tableFieldList, Dictionary<string, string> result)> RenderTemplateAsync(SysCodeGen input)
+    {
+        var tableFieldList = await _codeGenConfigService.GetList(new CodeGenConfig { CodeGenId = input.Id }); // 字段集合
+        var joinTableList = tableFieldList.Where(u => u.EffectType is "Upload" or "ForeignKey" or "ApiTreeSelector").ToList(); // 需要连表查询的字段
+
+        var data = new CustomViewEngine
         {
             ConfigId = input.ConfigId,
-            AuthorName = input.AuthorName,
             BusName = input.BusName,
+            PagePath = input.PagePath,
             NameSpace = input.NameSpace,
             ClassName = input.TableName,
-            PagePath = input.PagePath,
-            TableUniqueList = input.TableUniqueList ?? new(),
-            ProjectLastName = input.NameSpace.Split('.').Last(),
-            QueryWhetherList = queryWhetherList,
-            TableField = tableFieldList,
-            IsJoinTable = joinTableList.Count > 0,
-            IsUpload = joinTableList.Where(u => u.EffectType == "Upload").Any(),
             PrintType = input.PrintType,
             PrintName = input.PrintName,
+            AuthorName = input.AuthorName,
+            ProjectLastName = input.NameSpace!.Split('.').Last(),
+            LowerClassName = input.TableName![..1].ToLower() + input.TableName[1..],
+            TableUniqueConfigList = input.TableUniqueList ?? new(),
+            
+            TableField = tableFieldList,
+            QueryWhetherList = tableFieldList.Where(u => u.WhetherQuery == "Y").ToList(),
+            ImportFieldList = tableFieldList.Where(u => u.WhetherImport == "Y").ToList(),
+            UploadFieldList = tableFieldList.Where(u => u.EffectType == "Upload").ToList(),
+            DropdownFieldList = joinTableList.Where(u => u.EffectType != "Upload").ToList(),
+            PrimaryKeyFieldList = tableFieldList.Where(c => c.ColumnKey == "True").ToList(),
+            AddUpdateFieldList = tableFieldList.Where(u => u.WhetherAddUpdate == "Y").ToList(),
+            
+            HasJoinTable = joinTableList.Count > 0,
+            HasDictField = tableFieldList.Any(u => u.EffectType == "DictSelector"),
+            HasEnumField = tableFieldList.Any(u => u.EffectType == "EnumSelector"),
+            HasConstField = tableFieldList.Any(u => u.EffectType == "ConstSelector"),
+            HasLikeQuery = tableFieldList.Any(c => c.WhetherQuery == "Y" && c.QueryType == "like"),
+            HasSetStatus = tableFieldList.Any(c => c.NetType == nameof(StatusEnum) && c.PropertyName == nameof(SysUser.Status)),
         };
 
         // 获取模板文件并替换
@@ -438,24 +418,24 @@ public class SysCodeGenService : IDynamicApiController, ITransient
         var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "template");
 
         var result = new Dictionary<string, string>();
-        for (var i = 0; i < templatePathList.Count; i++)
+        foreach (var path in templatePathList)
         {
-            var templateFilePath = Path.Combine(templatePath, templatePathList[i]);
+            var templateFilePath = Path.Combine(templatePath, path);
             if (!File.Exists(templateFilePath)) continue;
-            var tContent = File.ReadAllText(templateFilePath);
+            var tContent = await File.ReadAllTextAsync(templateFilePath);
             var tResult = await _viewEngine.RunCompileFromCachedAsync(tContent, data, builderAction: builder =>
             {
-                builder.AddAssemblyReferenceByName("System.Linq");
-                builder.AddAssemblyReferenceByName("System.Collections");
                 builder.AddAssemblyReferenceByName("System.Text.RegularExpressions");
+                builder.AddAssemblyReferenceByName("System.Collections");
+                builder.AddAssemblyReferenceByName("System.Linq");
+                
                 builder.AddUsing("System.Text.RegularExpressions");
                 builder.AddUsing("System.Collections.Generic");
                 builder.AddUsing("System.Linq");
             });
-            result.Add(templatePathList[i]?.TrimEnd(".vm"), tResult);
+            result.Add(path?.TrimEnd(".vm"), tResult);
         }
-
-        return result;
+        return (tableFieldList, result);
     }
 
     /// <summary>
@@ -489,13 +469,13 @@ public class SysCodeGenService : IDynamicApiController, ITransient
             if (menuList0.Count > 0)
             {
                 var listIds = menuList0.Select(u => u.Id).ToList();
-                var childlistIds = new List<long>();
+                var childrenIds = new List<long>();
                 foreach (var item in listIds)
                 {
-                    var childlist = await _db.Queryable<SysMenu>().ToChildListAsync(u => u.Pid, item);
-                    childlistIds.AddRange(childlist.Select(u => u.Id).ToList());
+                    var children = await _db.Queryable<SysMenu>().ToChildListAsync(u => u.Pid, item);
+                    childrenIds.AddRange(children.Select(u => u.Id).ToList());
                 }
-                listIds.AddRange(childlistIds);
+                listIds.AddRange(childrenIds);
                 await _db.Deleteable<SysMenu>().Where(u => listIds.Contains(u.Id)).ExecuteCommandAsync();
                 await _db.Deleteable<SysRoleMenu>().Where(u => listIds.Contains(u.MenuId)).ExecuteCommandAsync();
             }
@@ -649,7 +629,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
         var menuList = new List<SysMenu> { menuTypePage, menuTypeDetail, menuTypeAdd, menuTypeStatus, menuTypeDelete, menuTypeBatchDelete, menuTypeUpdate, menuTypePrint, menuTypeImport, menuTypeExport };
         // 加入ForeignKey、Upload、ApiTreeSelector 等接口的权限
         // 在生成表格时,有些字段只是查询时显示,不需要填写(WhetherAddUpdate),所以这些字段没必要生成相应接口
-        var fkTableList = tableFieldList.Where(u => u.EffectType == "ForeignKey" && (u.WhetherAddUpdate == "Y" || u.QueryWhether == "Y")).ToList();
+        var fkTableList = tableFieldList.Where(u => u.EffectType == "ForeignKey" && (u.WhetherAddUpdate == "Y" || u.WhetherQuery == "Y")).ToList();
         foreach (var @column in fkTableList)
         {
             var menuType1 = new SysMenu
@@ -700,7 +680,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
     /// <returns></returns>
     private static List<string> GetTemplatePathList(SysCodeGen input)
     {
-        if (input.GenerateType.Substring(1, 1).Contains('1'))
+        if (input.GenerateType!.Substring(1, 1).Contains('1'))
         {
             return new() { "index.vue.vm", "editDialog.vue.vm", "manage.js.vm" };
         }
@@ -728,17 +708,17 @@ public class SysCodeGenService : IDynamicApiController, ITransient
     private List<string> GetTargetPathList(SysCodeGen input)
     {
         //var backendPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName, _codeGenOptions.BackendApplicationNamespace, "Service", input.TableName);
-        var backendPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName, input.NameSpace, "Service", input.TableName);
+        var backendPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent!.FullName, input.NameSpace!, "Service", input.TableName!);
         var servicePath = Path.Combine(backendPath, input.TableName + "Service.cs");
         var inputPath = Path.Combine(backendPath, "Dto", input.TableName + "Input.cs");
         var outputPath = Path.Combine(backendPath, "Dto", input.TableName + "Output.cs");
         var viewPath = Path.Combine(backendPath, "Dto", input.TableName + "Dto.cs");
-        var frontendPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.Parent.FullName, _codeGenOptions.FrontRootPath, "src", "views", input.PagePath);
+        var frontendPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent!.Parent!.FullName, _codeGenOptions.FrontRootPath, "src", "views", input.PagePath!);
         var indexPath = Path.Combine(frontendPath, input.TableName[..1].ToLower() + input.TableName[1..], "index.vue");//
         var formModalPath = Path.Combine(frontendPath, input.TableName[..1].ToLower() + input.TableName[1..], "component", "editDialog.vue");
-        var apiJsPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.Parent.FullName, _codeGenOptions.FrontRootPath, "src", "api", input.PagePath, input.TableName[..1].ToLower() + input.TableName[1..] + ".ts");
+        var apiJsPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent!.Parent!.FullName, _codeGenOptions.FrontRootPath, "src", "api", input.PagePath, input.TableName[..1].ToLower() + input.TableName[1..] + ".ts");
 
-        if (input.GenerateType.Substring(1, 1).Contains('1'))
+        if (input.GenerateType!.Substring(1, 1).Contains('1'))
         {
             // 生成到本项目(前端)
             return new List<string>()
@@ -782,19 +762,19 @@ public class SysCodeGenService : IDynamicApiController, ITransient
     /// <returns></returns>
     private List<string> GetZipPathList(SysCodeGen input)
     {
-        var zipPath = Path.Combine(App.WebHostEnvironment.WebRootPath, "CodeGen", input.TableName);
+        var zipPath = Path.Combine(App.WebHostEnvironment.WebRootPath, "CodeGen", input.TableName!);
 
         //var backendPath = Path.Combine(zipPath, _codeGenOptions.BackendApplicationNamespace, "Service", input.TableName);
-        var backendPath = Path.Combine(zipPath, input.NameSpace, "Service", input.TableName);
+        var backendPath = Path.Combine(zipPath, input.NameSpace!, "Service", input.TableName);
         var servicePath = Path.Combine(backendPath, input.TableName + "Service.cs");
         var inputPath = Path.Combine(backendPath, "Dto", input.TableName + "Input.cs");
         var outputPath = Path.Combine(backendPath, "Dto", input.TableName + "Output.cs");
         var viewPath = Path.Combine(backendPath, "Dto", input.TableName + "Dto.cs");
-        var frontendPath = Path.Combine(zipPath, _codeGenOptions.FrontRootPath, "src", "views", input.PagePath);
+        var frontendPath = Path.Combine(zipPath, _codeGenOptions.FrontRootPath, "src", "views", input.PagePath!);
         var indexPath = Path.Combine(frontendPath, input.TableName[..1].ToLower() + input.TableName[1..], "index.vue");
         var formModalPath = Path.Combine(frontendPath, input.TableName[..1].ToLower() + input.TableName[1..], "component", "editDialog.vue");
         var apiJsPath = Path.Combine(zipPath, _codeGenOptions.FrontRootPath, "src", "api", input.PagePath, input.TableName[..1].ToLower() + input.TableName[1..] + ".ts");
-        if (input.GenerateType.StartsWith("11"))
+        if (input.GenerateType!.StartsWith("11"))
         {
             return new List<string>()
             {

+ 1 - 1
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Dto.cs.vm

@@ -12,7 +12,7 @@ namespace @(Model.NameSpace);
 public class @(Model.ClassName)Dto
 {
 @foreach (var column in Model.TableField){
-if(column.EffectType == "ForeignKey" && column.FkEntityName != "" && column.FkColumnName != ""){
+if(column.EffectType == "ForeignKey"){
     @:/// <summary>
     @:/// @column.ColumnComment
     @:/// </summary>

+ 46 - 47
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Input.cs.vm

@@ -4,11 +4,6 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
-@{
-    bool IsNetEnumType(dynamic column) => column.NetType.TrimEnd('?').EndsWith("Enum");
-    bool IsStatusEnumField(dynamic column) => column.NetType.TrimEnd('?') == "StatusEnum" && column.PropertyName == "Status";
-    string GetFinalType(string type) => Regex.IsMatch(type, "(.*?Enum|bool|char|int|long|double|float|decimal)[?]?") ? type.TrimEnd('?') + "?" : type;
-}
 using Admin.NET.Core;
 using System.ComponentModel.DataAnnotations;
 
@@ -19,12 +14,12 @@ namespace @(Model.NameSpace);
 /// </summary>
 public class @(Model.ClassName)BaseInput
 {
-@foreach (var column in Model.TableField.Where(u => u.ColumnKey != "True" && u.WhetherAddUpdate == "Y")){
+@foreach (var column in Model.PrimaryKeyFieldList.Concat(Model.AddUpdateFieldList)){
     @:/// <summary>
     @:/// @column.ColumnComment
     @:/// </summary>
-    @:public virtual @GetFinalType(column.NetType) @column.PropertyName { get; set; }
-@:
+    @:public virtual @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
+    @:
 }
 }
 
@@ -33,7 +28,7 @@ public class @(Model.ClassName)BaseInput
 /// </summary>
 public class Page@(Model.ClassName)Input : BasePageInput
 {
-@foreach (var column in Model.TableField.Where(u => u.QueryWhether == "Y")){
+@foreach (var column in Model.TableField.Where(u => u.WhetherQuery == "Y")){
     if(column.NetType?.TrimEnd('?') == "DateTime" && column.QueryType == "~"){
     @:/// <summary>
     @:/// @(column.ColumnComment)范围
@@ -43,9 +38,9 @@ public class Page@(Model.ClassName)Input : BasePageInput
     @:/// <summary>
     @:/// @column.ColumnComment
     @:/// </summary>
-    @:public @GetFinalType(column.NetType) @column.PropertyName { get; set; }
+    @:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
     }
-@:
+    @:
 }
 }
 
@@ -54,7 +49,7 @@ public class Page@(Model.ClassName)Input : BasePageInput
 /// </summary>
 public class Add@(Model.ClassName)Input
 {
-@foreach (var column in Model.TableField.Where(col => col.WhetherAddUpdate == "Y")){
+@foreach (var column in Model.AddUpdateFieldList){
     @:/// <summary>
     @:/// @column.ColumnComment
     @:/// </summary>
@@ -64,8 +59,8 @@ public class Add@(Model.ClassName)Input
     if (column.NetType.TrimEnd('?').EndsWith("string") && column.ColumnLength > 0){
     @:[MaxLength(@column.ColumnLength, ErrorMessage = "@(column.ColumnComment)字符长度不能超过@(column.ColumnLength)")]
     }
-    @:public @GetFinalType(column.NetType) @column.PropertyName { get; set; }
-@:
+    @:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
+    @:
 }
 }
 
@@ -74,28 +69,13 @@ public class Add@(Model.ClassName)Input
 /// </summary>
 public class Delete@(Model.ClassName)Input
 {
-@foreach (var column in Model.TableField.Where(u => u.ColumnKey == "True")){
+@foreach (var column in Model.PrimaryKeyFieldList) {
     @:/// <summary>
     @:/// @column.ColumnComment
     @:/// </summary>
     @:[Required(ErrorMessage = "@(column.ColumnComment)不能为空")]
-    @:public @GetFinalType(column.NetType) @column.PropertyName { get; set; }
-@:
-}
-}
-
-/// <summary>
-/// @(Model.BusName)批量删除输入参数
-/// </summary>
-public class BatchDelete@(Model.ClassName)Input
-{
-@foreach (var column in Model.TableField.Where(u => u.ColumnKey == "True")){
-    @:/// <summary>
-    @:/// @column.ColumnComment
-    @:/// </summary>
-    @:[Required(ErrorMessage = "@(column.ColumnComment)列表不能为空")]
-    @:public List<@GetFinalType(column.NetType)> @(column.PropertyName)List { get; set; }
-@:
+    @:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
+    @:
 }
 }
 
@@ -104,7 +84,7 @@ public class BatchDelete@(Model.ClassName)Input
 /// </summary>
 public class Update@(Model.ClassName)Input
 {
-@foreach (var column in Model.TableField.Where(u => u.ColumnKey == "True" || u.WhetherAddUpdate == "Y" && !IsStatusEnumField(u))){
+    @foreach (var column in Model.PrimaryKeyFieldList.Concat(Model.AddUpdateFieldList)){
     @:/// <summary>
     @:/// @column.ColumnComment
     @:/// </summary>    
@@ -114,9 +94,9 @@ public class Update@(Model.ClassName)Input
     if (column.NetType.TrimEnd('?').EndsWith("string") && column.ColumnLength > 0){
     @:[MaxLength(@column.ColumnLength, ErrorMessage = "@(column.ColumnComment)字符长度不能超过@(column.ColumnLength)")]
     }
-    @:public @GetFinalType(column.NetType) @column.PropertyName { get; set; }
-@:
-}
+    @:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
+    @:
+    }
 }
 
 /// <summary>
@@ -126,38 +106,57 @@ public class QueryById@(Model.ClassName)Input : Delete@(Model.ClassName)Input
 {
 }
 
-@if (Model.TableField.Any(x => x.WhetherImport == "Y")){
+@if (Model.DropdownFieldList.Count > 0) {
+@:/// <summary>
+@:/// 下拉数据输入参数
+@:/// </summary>
+@:public class DropdownData@(Model.ClassName)Input
+@:{
+    @:/// <summary>
+    @:/// 字段名称
+    @:/// </summary>
+    @:[Required(ErrorMessage = "字段名称不能为空")]
+    @:public string PropertyName { get; set; }
+    @:
+    @:/// <summary>
+    @:/// 是否用于分页查询
+    @:/// </summary>
+    @:public bool FromPage { get; set; }
+@:}
+}
+
+@if (Model.ImportFieldList.Count > 0){
 @:/// <summary>
 @:/// @(Model.BusName)数据导入实体
 @:/// </summary>
 @:[ExcelImporter(SheetIndex = 1, IsOnlyErrorRows = true)]
 @:public class Import@(Model.ClassName)Input : BaseImportInput
 @:{
-    foreach (var column in Model.TableField.Where(x => x.WhetherImport == "Y")){
-    var prefix = column.WhetherRequired == "Y" || column.NetType.TrimEnd('?').EndsWith("Enum") ? "*" : "";
+    foreach (var column in Model.ImportFieldList){
+    var headerName = (column.WhetherRequired == "Y" ? "*" : "") + column.ColumnComment;
     if(column.EffectType == "ForeignKey" || column.EffectType == "ApiTreeSelector") {
     @:/// <summary>
     @:/// @column.ColumnComment 关联值
     @:/// </summary>
     @:[ImporterHeader(IsIgnore = true)]
     @:[ExporterHeader(IsIgnore = true)]
-    @:public @GetFinalType(column.NetType) @column.PropertyName { get; set; }
-@:
+    @:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
+    @:
     @:/// <summary>
     @:/// @column.ColumnComment 文本
     @:/// </summary>
-    @:[ImporterHeader(Name = "@(prefix + column.ColumnComment)")]
-    @:[ExporterHeader("@(prefix + column.ColumnComment)", Format = "@", Width = 25, IsBold = true)]
+    @:[ImporterHeader(Name = "@(headerName)")]
+    @:[ExporterHeader("@(headerName)", Format = "@", Width = 25, IsBold = true)]
     @:public string @(column.PropertyName)Label { get; set; }
     } else {
     @:/// <summary>
     @:/// @column.ColumnComment
     @:/// </summary>
-    @:[ImporterHeader(Name = "@(prefix + column.ColumnComment)")]
-    @:[ExporterHeader("@(prefix + column.ColumnComment)", Format = "@", Width = 25, IsBold = true)]
-    @:public @GetFinalType(column.NetType) @column.PropertyName { get; set; }
+    @:[ImporterHeader(Name = "@(headerName)")]
+    @:[ExporterHeader("@(headerName)", Format = "@", Width = 25, IsBold = true)]
+    @:public @Model.GetNullableNetType(column.NetType) @column.PropertyName { get; set; }
     }
-@:
+    @:
     }
 @:}
 }

+ 15 - 21
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Manage.js.vm

@@ -1,8 +1,3 @@
-@{
-	var definedObjects = new Dictionary<string, object>();
-	string LowerFirstLetter(string text) => text.ToString()[..1].ToLower() + text[1..];
-	var hasSetStatus = Model.TableField.Any(col => col.NetType == "StatusEnum" && col.PropertyName == "Status");
-}
 import {useBaseApi} from '/@@/api/base';
 
 // @(Model.BusName)接口服务
@@ -17,41 +12,40 @@ export const use@(Model.ClassName)Api = () => {
 		add: baseApi.add,
 		// 更新@(Model.BusName)
 		update: baseApi.update,
-@if (hasSetStatus) {
+		@if (Model.HasSetStatus) {
 		@:// 设置@(Model.BusName)状态
 		@:setStatus: baseApi.setStatus,
-}
+		}
 		// 删除@(Model.BusName)
 		delete: baseApi.delete,
 		// 批量删除@(Model.BusName)
 		batchDelete: baseApi.batchDelete,
-@if (Model.TableField.Any(x => x.WhetherImport == "Y")) {
-		@:// 下载@(Model.BusName)数据导入模板
-		@:downloadTemplate: baseApi.downloadTemplate,
+		@if (Model.ImportFieldList.Count > 0) {
 		@:// 导入@(Model.BusName)数据
 		@:importData: baseApi.importData,
-}
-@foreach (var column in Model.TableField) {
+		@:// 下载@(Model.BusName)数据导入模板
+		@:downloadTemplate: baseApi.downloadTemplate,
+		}
+		@foreach (var column in Model.TableField) {
 		if (column.EffectType == "Upload") {
 		@:// 上传@(column.ColumnComment)
 		@:upload@(column.PropertyName): (params: any) => baseApi.uploadFile(params, baseApi.baseUrl + 'upload@(column.PropertyName)'),
-		} else if (column.EffectType == "ForeignKey" && (column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")) {
-		var dropdownName = $"{column.FkEntityName}{Regex.Replace(column.PropertyName, "[iI]d$", "")}Dropdown";
+		} else if (column.EffectType == "ForeignKey" && (column.WhetherAddUpdate == "Y" || column.WhetherQuery == "Y")) {
+		var dropdownName = $"{column.FkEntityName}{column.PropertyName.TrimEnd("Id")}Dropdown";
 		@:// 获取@(column.ColumnComment)选择数据
 		@:get@(dropdownName): (all: Boolean = false) => baseApi.request({
-			@:url: baseApi.baseUrl + '@LowerFirstLetter(dropdownName)',
+			@:url: baseApi.baseUrl + '@Model.ToLowerFirstLetter(dropdownName)',
 			@:params: { all },
 			@:method: 'get',
 		@:}),
-		} else if (column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("get@(column.FkEntityName)Tree")) {
-		definedObjects.Add("get@(column.FkEntityName)Tree", 1);
+		}
+		}
+		@foreach (var column in Model.TableField.Where(c => c.EffectType == "ApiTreeSelector").DistinctBy(c => c.FkEntityName)) {
 		@:// 获取@(column.ColumnComment)选择数据
-		@:get@(column.FkEntityName)Tree: (all: Boolean = false) => baseApi.request({
-			@:url: baseApi.baseUrl + '@LowerFirstLetter(column.FkEntityName)Tree',
-			@:params: { all },
+		@:get@(column.FkEntityName)Tree: () => baseApi.request({
+			@:url: baseApi.baseUrl + '@Model.ToLowerFirstLetter(column.FkEntityName)Tree',
 			@:method: 'get',
 		@:}),
 		}
-}
 	}
 }

+ 15 - 16
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Output.cs.vm

@@ -4,9 +4,6 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
-@{
-    string GetFinalType(string type) => Regex.IsMatch(type, "(.*?Enum|bool|char|int|long|double|float|decimal)[?]?") ? type.TrimEnd('?') + "?" : type;
-}
 namespace @(Model.NameSpace);
 
 /// <summary>
@@ -18,32 +15,35 @@ public class @(Model.ClassName)Output
     @:/// <summary>
     @:/// @column.ColumnComment
     @:/// </summary>
-if(column.EffectType == "ForeignKey")
-{
+    if(column.EffectType == "ForeignKey")
+    {
     @:public @column.NetType @column.PropertyName { get; set; } 
     @:
     @:/// <summary>
     @:/// @(column.ColumnComment) 描述
     @:/// </summary>
     @:public string @(column.PropertyName)FkColumn { get; set; } 
-}else if(column.EffectType == "Upload"){
+    }else if(column.EffectType == "Upload"){
     @:public @column.NetType @column.PropertyName { get; set; }
+    @:
+    @:/// <summary>
+    @:/// @(column.ColumnComment) 文件信息
+    @:/// </summary>
     @:public SysFile @(column.PropertyName)Attachment { get; set; }
-}else if(column.EffectType == "ApiTreeSelector"){
+    }else if(column.EffectType == "ApiTreeSelector"){
     @:public @column.NetType @column.PropertyName { get; set; } 
     @:
     @:/// <summary>
     @:/// @(column.ColumnComment) 描述 
     @:/// </summary>
-    @:public string? @(column.PropertyName)Display { get; set; } 
-}else{
-    @:public @GetFinalType(column.NetType) @(column.PropertyName) { get; set; }
-}
+    @:public string @(column.PropertyName)Display { get; set; } 
+    }else{
+    @:public @column.NetType @(column.PropertyName) { get; set; }
+    }
     @:
 }
 }
-@foreach (var column in Model.TableField){
-if (column.EffectType == "ApiTreeSelector"){
+@foreach (var column in Model.ApiTreeFieldList){
 @:
 @:/// <summary>
 @:/// @(Model.BusName)树选择器输出参数
@@ -58,7 +58,7 @@ if (column.EffectType == "ApiTreeSelector"){
     @:/// <summary>
     @:/// 选项值
     @:/// </summary>
-    @:public @(Model.GetColumnNetType(@column.FkTableName, @column.ValueColumn)) Value { get; set; }
+    @:public @(column.FkColumnNetType) Value { get; set; }
     @:
     @:/// <summary>
     @:/// 子集列表
@@ -66,8 +66,7 @@ if (column.EffectType == "ApiTreeSelector"){
     @:public List<@(column.FkEntityName)TreeOutput> Children { get; set; }
 @:}
 }
-}
-@if (Model.TableField.Any(x => x.WhetherImport == "Y")) {
+@if (Model.ImportFieldList.Count > 0) {
 @:
 @:/// <summary>
 @:/// @(Model.BusName)数据导入模板实体

+ 49 - 81
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Service.cs.vm

@@ -6,16 +6,7 @@
 
 using Admin.NET.Core.Service;
 using Microsoft.AspNetCore.Http;
-@{
-    string LowerFirstLetter(string text) => text.ToString()[..1].ToLower() + text[1..];
 
-    var primaryKey = Model.TableField.First(u => u.ColumnKey == "True");
-    var importFields = Model.TableField.Where(x => x.WhetherImport == "Y");
-
-    var injectServices = new List<string>();
-    if (Model.TableField.Any(u => u.EffectType == "Upload")) injectServices.Add("SysFileService");
-    if (Model.TableField.Any(x => x.WhetherImport == "Y" && x.EffectType == "DictSelector")) injectServices.Add("SysDictTypeService");
-}
 namespace @(Model.NameSpace);
 
 /// <summary>
@@ -25,15 +16,15 @@ namespace @(Model.NameSpace);
 public class @(Model.ClassName)Service : IDynamicApiController, ITransient
 {
     private readonly SqlSugarRepository<@(Model.ClassName)> _@(Model.LowerClassName)Rep;
-    @foreach(var name in injectServices) {
-    @:private readonly @name _@(LowerFirstLetter(name));
+    @foreach(var kv in Model.InjectServiceMap) {
+    @:private readonly @(kv.Key) _@(kv.Value);
     }
 
-    public @(Model.ClassName)Service(SqlSugarRepository<@(Model.ClassName)> @(Model.LowerClassName)Rep@(injectServices.Count > 0 ? ", " + string.Join(", ", injectServices.Select(name => $"{name} {LowerFirstLetter(name)}")) : ""))
+    public @(Model.ClassName)Service(SqlSugarRepository<@(Model.ClassName)> @(Model.LowerClassName)Rep@(Model.InjectServiceArgs))
     {
         _@(Model.LowerClassName)Rep = @(Model.LowerClassName)Rep;
-        @foreach(var name in injectServices) {
-        @:_@LowerFirstLetter(name) = @LowerFirstLetter(name);
+        @foreach(var kv in Model.InjectServiceMap) {
+        @:_@(kv.Value) = @(kv.Value);
         }
     }
 
@@ -48,9 +39,9 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
     {
         input.Keyword = input.Keyword?.Trim();
         var query = _@(Model.LowerClassName)Rep.AsQueryable()
-@{
+    @{
           string joinTableName = "u";
-          var queryFields = Model.TableField.Where(u => u.QueryWhether == "Y");
+          var queryFields = Model.TableField.Where(u => u.WhetherQuery == "Y");
           // 关键字模糊查询
           if (queryFields.Any(u => u.QueryType == "like")) {
             @:.WhereIF(!string.IsNullOrEmpty(input.Keyword), u => @string.Join(" || ", queryFields.Where(u => u.QueryType == "like").Select(col => $"u.{col.PropertyName}.Contains(input.Keyword)")))
@@ -68,11 +59,11 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
             }
           }
           // 联表
-          if (Model.IsJoinTable) {
-            @foreach (var column in Model.TableField.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector")){
+          if (Model.HasJoinTable) {
+            foreach (var column in Model.TableField.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector")){
             var joinTableAlias = Regex.Replace(column.LowerPropertyName, "[iI]d$", "");
             joinTableName += ", " + joinTableAlias;
-            @:.LeftJoin<@column.FkEntityName>((@joinTableName) => u.@(column.PropertyName) == @joinTableAlias.@(column.EffectType == "ForeignKey" ? column.FkLinkColumnName : column.ValueColumn))
+            @:.LeftJoin<@column.FkEntityName>((@joinTableName) => u.@(column.PropertyName) == @joinTableAlias.@(column.EffectType == "ForeignKey" ? column.FkLinkColumnName : column.FkLinkColumnName))
           }
             // 查询列表
             @:.Select((@joinTableName) => new @(Model.ClassName)Output
@@ -80,12 +71,12 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
             foreach (var column in Model.TableField) {
                 var joinTableAlias = Regex.Replace(column.LowerPropertyName, "[iI]d$", "");
                 if (column.EffectType == "ForeignKey") {
-                var columnList = column.FkColumnName.Split(",").Select(n => $"{{{joinTableAlias}.{n}}}").ToList();
-                @:@(column.PropertyName) = u.@(column.PropertyName), 
+                var columnList = column.FkDisplayColumnList.Select(n => $"{{{joinTableAlias}.{n}}}").ToList();
+                @:@(column.PropertyName) = u.@(column.PropertyName),
                 @:@(column.PropertyName)FkColumn = $"@(string.Join("-", columnList))",
                 } else if (column.EffectType == "ApiTreeSelector") {
-                var columnList = column.DisplayColumn.Split(",").Select(n => $"{{{joinTableAlias}.{n}}}").ToList();
-                @:@(column.PropertyName) = u.@(column.PropertyName),  
+                var columnList = column.FkDisplayColumnList.Select(n => $"{{{joinTableAlias}.{n}}}").ToList();
+                @:@(column.PropertyName) = u.@(column.PropertyName),
                 @:@(column.PropertyName)Display = $"@(string.Join("-", columnList))",
                 } else {
                 @:@(column.PropertyName) = u.@(column.PropertyName),
@@ -96,7 +87,7 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
             // 无联表
             @:.Select<@(Model.ClassName)Output>();
          }
-}
+    }
 		return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
     }
 
@@ -110,8 +101,8 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
     public async Task<long> Add(Add@(Model.ClassName)Input input)
     {
         var entity = input.Adapt<@(Model.ClassName)>();
-        @foreach (var config in Model.TableUniqueList.Where(x => x.Columns.All(x1 => Model.TableField.Any(x2 => x2.WhetherAddUpdate == "Y" && x2.PropertyName == x1)))) {
-        @:if (await _@(Model.LowerClassName)Rep.IsAnyAsync(u => @(string.Join(" && ", config.Columns.Select(x => $"u.{x} != null && u.{x} == input.{x}"))))) throw Oops.Oh("@(config.Message)已存在");
+        @foreach (var config in Model.AddUpdateUniqueConfigList) {
+        @:if (await _@(Model.LowerClassName)Rep.IsAnyAsync(u => @(string.Join(" && ", @config.Columns.Select(x => $"u.{x} != null && u.{x} == input.{x}"))))) throw Oops.Oh("@(config.Message)已存在");
         }
         return await _@(Model.LowerClassName)Rep.InsertAsync(entity) ? entity.Id : 0;
     }
@@ -125,9 +116,7 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
     [ApiDescriptionSettings(Name = "Delete"), HttpPost]
     public async Task Delete(Delete@(Model.ClassName)Input input)
     {
-        @foreach (var column in Model.TableField.Where(u => u.ColumnKey == "True")) {
-        @:var entity = await _@(Model.LowerClassName)Rep.GetFirstAsync(u => u.@(column.PropertyName) == input.@(column.PropertyName)) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
-        }
+        var entity = await _@(Model.LowerClassName)Rep.GetFirstAsync(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}")) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
         await _@(Model.LowerClassName)Rep.FakeDeleteAsync(entity);   //假删除
         //await _@(Model.LowerClassName)Rep.DeleteAsync(entity);   //真删除
     }
@@ -139,11 +128,9 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
     /// <returns></returns>
     [DisplayName("批量删除@(Model.BusName)")]
     [ApiDescriptionSettings(Name = "BatchDelete"), HttpPost]
-    public async Task<int> BatchDelete(BatchDelete@(Model.ClassName)Input input)
+    public async Task<int> BatchDelete([Required(ErrorMessage = "主键列表不能为空")]List<Delete@(Model.ClassName)Input> input)
     {
-        @foreach (var column in Model.TableField.Where(u => u.ColumnKey == "True")) {
-        @:var list = await _@(Model.LowerClassName)Rep.AsQueryable().Where(u => input.@(column.PropertyName)List.Contains(u.@(column.PropertyName))).ToListAsync() ?? throw Oops.Oh(ErrorCodeEnum.D1002);
-        }
+        var list = await _@(Model.LowerClassName)Rep.AsQueryable().In(u => new { @string.Join(", ", Model.PrimaryKeyFieldList.Select(n => $"u.{n}")) }, input).ToListAsync();
         return await _@(Model.LowerClassName)Rep.FakeDeleteAsync(list);   //假删除
         //return await _@(Model.LowerClassName)Rep.DeleteAsync(list);   //真删除
     }
@@ -157,13 +144,16 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
     [ApiDescriptionSettings(Name = "Update"), HttpPost]
     public async Task Update(Update@(Model.ClassName)Input input)
     {
-        @foreach (var config in Model.TableUniqueList.Where(x => x.Columns.All(x1 => Model.TableField.Any(x2 => x2.WhetherAddUpdate == "Y" && x2.PropertyName == x1)))) {
-        @:if (await _@(Model.LowerClassName)Rep.IsAnyAsync(u => u.@(primaryKey.PropertyName) != input.@(primaryKey.PropertyName) && @(string.Join(" && ", config.Columns.Select(x => $"u.{x} != null && u.{x} == input.{x}"))))) throw Oops.Oh("@(config.Message)已存在");
+        @{
+        var primaryKeyWhere = string.Join(" && ", Model.PrimaryKeyFieldList.Select(n => $"u.{n} == input.{n}"));
+        foreach (var config in Model.AddUpdateUniqueConfigList) {
+        @:if (await _@(Model.LowerClassName)Rep.IsAnyAsync(u => !(@primaryKeyWhere) && @(string.Join(" && ", config.Columns.Select(x => $"u.{x} != null && u.{x} == input.{x}"))))) throw Oops.Oh("@(config.Message)已存在");
+        }
         }
         var entity = input.Adapt<@(Model.ClassName)>();
         await _@(Model.LowerClassName)Rep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
     }
-    @if (Model.TableField.Any(col => col.NetType == "StatusEnum" && col.PropertyName == "Status")) {
+    @if (Model.HasSetStatus) {
     @:
     @:/// <summary>
     @:/// 设置@(Model.BusName)状态 🚫
@@ -187,7 +177,7 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
     [ApiDescriptionSettings(Name = "Detail"), HttpGet]
     public async Task<@(Model.ClassName)> Detail([FromQuery] QueryById@(Model.ClassName)Input input)
     {
-        return await _@(Model.LowerClassName)Rep.GetFirstAsync(u => u.@(primaryKey.PropertyName) == input.@(primaryKey.PropertyName));
+        return await _@(Model.LowerClassName)Rep.GetFirstAsync(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}"));
     }
 
     /// <summary>
@@ -201,9 +191,9 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
     {
         return await _@(Model.LowerClassName)Rep.AsQueryable().Select<@(Model.ClassName)Output>().ToListAsync();
     }
-@foreach (var column in Model.TableField.Where(u => u.EffectType == "ForeignKey" && (u.WhetherAddUpdate == "Y" || u.QueryWhether == "Y"))){
-@:
-    var dropdownName = $"{column.FkEntityName}{Regex.Replace(column.PropertyName, "[iI]d$", "")}Dropdown";
+    @foreach (var column in Model.TableField.Where(u => u.EffectType == "ForeignKey" && (u.WhetherAddUpdate == "Y" || u.WhetherQuery == "Y"))){
+    @:
+    var dropdownName = $"{column.FkEntityName}{column.PropertyName.TrimEnd("Id")}Dropdown";
     @:/// <summary>
     @:/// 获取@(column.ColumnComment)列表 🔖
     @:/// </summary>
@@ -212,7 +202,7 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
     @:[ApiDescriptionSettings(Name = "@(dropdownName)"), HttpGet]
     @:public async Task<dynamic> @(dropdownName)([FromQuery]bool all)
     @:{
-        var columnList = column.FkColumnName.Split(",").Select(name => $"{{u.{name}}}").ToList();
+        var columnList = column.FkDisplayColumnList.Select(name => $"{{u.{name}}}").ToList();
         @:return await _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>()
             @:.InnerJoinIF<@Model.ClassName>(!all, (u, r) => u.@(column.FkLinkColumnName) == r.@(column.PropertyName))
             @:.Select(u => new
@@ -222,31 +212,10 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
             @:}
         @:).ToListAsync();
     @:}
-}
-@{
-var definedObjects = new Dictionary<string, object>();
-@foreach (var column in Model.TableField.Where(u => u.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("@(u.FkEntityName)Tree"))){
-@:
-    definedObjects.Add("@(column.FkEntityName)Tree", 1);
-    @:/// <summary>
-    @:/// 获取@(column.ColumnComment)选择数据 🔖
-    @:/// </summary>
-    @:[DisplayName("获取@(column.ColumnComment)选择数据")]
-    @:[ApiDescriptionSettings(Name = "@(column.FkEntityName)Tree"), HttpGet]
-    @:public async Task<List<@(column.FkEntityName)TreeOutput>> @(column.FkEntityName)Tree([FromQuery]bool all)
-    @:{
-        var columnList = column.DisplayColumn.Split(",").Select(name => $"{{u.{name}}}").ToList();
-        @:return await _@(Model.LowerClassName)Rep.Context.Queryable<@column.FkEntityName>()
-          @:.InnerJoinIF<@Model.ClassName>(!all, (u, r) => u.@(column.ValueColumn) == r.@(column.PropertyName))
-          @:.Select(u => new @(column.FkEntityName)TreeOutput {
-            @:Label = $"@(string.Join("-", columnList))",
-            @:Value = u.@column.ValueColumn
-        @:}, true).ToTreeAsync(u => u.Children, u => u.@column.PidColumn, @(column.WhetherRequired == "Y" ? "0" : "null"));
-    @:}
-}
-}
-@foreach (var column in Model.TableField.Where(u => u.EffectType == "Upload")) {
-@:
+    }
+
+    @foreach (var column in Model.AddUpdateFieldList) {
+    @:
     @:/// <summary>
     @:/// 上传@(column.ColumnComment) ⬆️
     @:/// </summary>
@@ -258,9 +227,9 @@ var definedObjects = new Dictionary<string, object>();
     @:{
         @:return await _sysFileService.UploadFile(new FileUploadInput { File = file, SavePath = "upload/@(Model.ClassName)/@(column.PropertyName)" }); 
     @:}
-}
-@if (importFields.Count() > 0) {
-@:
+    }
+    @if (Model.ImportFieldList.Count > 0) {
+    @:
     @:/// <summary>
     @:/// 下载@(Model.BusName)数据导入模板 ⬇️
     @:/// </summary>
@@ -269,12 +238,12 @@ var definedObjects = new Dictionary<string, object>();
     @:[ApiDescriptionSettings(Name = "Import"), HttpGet, NonUnify]
     @:public IActionResult DownloadTemplate()
     @:{
-        var fieldsList = importFields.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector").ToList();
+        var fieldsList = Model.ImportFieldList.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector").ToList();
         if (fieldsList.Any()) {
         @:return ExcelHelper.ExportTemplate(new List<Export@(Model.ClassName)Output>(), "@(Model.BusName)导入模板", (_, info) =>
         @:{
             foreach (var column in fieldsList) {
-            var columnList = (column.EffectType == "ForeignKey" ? column.FkColumnName : column.DisplayColumn).Split(",").Select(n => $"{{u.{n}}}").ToList();
+            var columnList = column.FkDisplayColumnList.Select(n => $"{{u.{n}}}").ToList();
             @:if (nameof(Export@(Model.ClassName)Output.@(column.PropertyName)Label) == info.Name) return _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>().Select(u => $"@(string.Join("-", columnList))").Distinct().ToList();
             }
             @:return null;
@@ -283,7 +252,7 @@ var definedObjects = new Dictionary<string, object>();
         @:return ExcelHelper.ExportTemplate(new List<Export@(Model.ClassName)Output>(), "@(Model.BusName)导入模板");
         }
     @:}
-@:
+    @:
     @:/// <summary>
     @:/// 导入@(Model.BusName)记录 💾
     @:/// </summary>
@@ -303,13 +272,12 @@ var definedObjects = new Dictionary<string, object>();
             @:{
                 @:_@(Model.LowerClassName)Rep.Context.Utilities.PageEach(list, 2048, pageItems =>
                 @:{
-                    foreach (var column in importFields.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector")) {
+                    foreach (var column in Model.ImportFieldList.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector")) {
                     @:// 链接 @(column.ColumnComment)
                     @:var @(column.LowerPropertyName)LabelList = pageItems.Where(x => x.@(column.PropertyName)Label != null).Select(x => x.@(column.PropertyName)Label).Distinct().ToList();
                     @:if (@(column.LowerPropertyName)LabelList.Any()) {
-                        var valueColumn = column.EffectType == "ForeignKey" ? column.FkLinkColumnName : column.ValueColumn;
-                        var columnList = (column.EffectType == "ForeignKey" ? column.FkColumnName : column.DisplayColumn).Split(",").Select(n => $"{{u.{n}}}").ToList();
-                        @:var @(column.LowerPropertyName)LinkMap = _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>().Where(u => @(column.LowerPropertyName)LabelList.Contains($"@(string.Join("-", columnList))")).ToList().ToDictionary(u => $"@(string.Join("-", columnList))", u => u.@(valueColumn));
+                        var columnList = column.FkDisplayColumnList.Select(n => $"{{u.{n}}}").ToList();
+                        @:var @(column.LowerPropertyName)LinkMap = _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>().Where(u => @(column.LowerPropertyName)LabelList.Contains($"@(string.Join("-", columnList))")).ToList().ToDictionary(u => $"@(string.Join("-", columnList))", u => u.@(column.FkLinkColumnName));
                         @:pageItems.ForEach(e => e.@(column.PropertyName) = @(column.LowerPropertyName)LinkMap?.GetValueOrDefault(e.@(column.PropertyName)Label, default));
                     @:}
                     }
@@ -317,7 +285,7 @@ var definedObjects = new Dictionary<string, object>();
                     @:
                     @:// 校验并过滤必填基本类型为null的字段
                     @:var rows = pageItems.Where(x => {
-                        foreach (var column in importFields.Where(x => x.WhetherRequired == "Y" && Regex.IsMatch(x.NetType, "(int|long|double|float|bool|Enum[?]?)"))){
+                        foreach (var column in Model.ImportFieldList.Where(x => x.WhetherRequired == "Y" && Regex.IsMatch(x.NetType, "(int|long|double|float|bool|Enum[?]?)"))){
                         @:if (x.@(column.PropertyName) == null){
                             @:x.Error = "@(column.ColumnComment)不能为空";
                             @:return false;
@@ -338,7 +306,7 @@ var definedObjects = new Dictionary<string, object>();
 
                     @:
                     @:var storageable = _@(Model.LowerClassName)Rep.Context.Storageable(rows)
-                        foreach (var column in importFields){
+                        foreach (var column in Model.ImportFieldList){
                         if (column.WhetherRequired == "Y"){
                         if(column.NetType.TrimEnd('?') == "string"){
                         @:.SplitError(it => string.IsNullOrWhiteSpace(it.Item.@(column.PropertyName)), "@(column.ColumnComment)不能为空")
@@ -348,7 +316,7 @@ var definedObjects = new Dictionary<string, object>();
                         if (column.NetType?.TrimEnd('?') == "string"){
                         @:.SplitError(it => it.Item.@(column.PropertyName)?.Length > @(column.ColumnLength), "@(column.ColumnComment)长度不能超过@(column.ColumnLength)个字符")
                         }}
-                        @foreach (var config in Model.TableUniqueList.Where(x => x.Columns.All(x1 => importFields.Any(x2 => x2.PropertyName == x1)))) {
+                        foreach (var config in Model.ImportUniqueConfigList) {
                         @:.WhereColumns(it => new { @(string.Join(", ", config.Columns.Select(x => $"it.{x}"))) }).SplitError(it => it.Any(), "@(config.Message)已存在")
                         }
                         @:.SplitInsert(_ => true)
@@ -362,9 +330,9 @@ var definedObjects = new Dictionary<string, object>();
                     @:markerErrorAction.Invoke(storageable, pageItems, rows);
                 @:});
             @:});
-@:
+            @:
             @:return stream;
         @:}
     @:}
+    }
 }
-@}

+ 2 - 2
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/data.data.ts.vm

@@ -26,7 +26,7 @@ if(column.EffectType == "Upload"){
     @:slots: { customRender: '@(column.LowerPropertyName)' },
 }else if(column.EffectType == "ForeignKey"){
     @:customRender: ({ record }) => {
-      @:return record.fk@(column.PropertyName).@(column.LowerFkColumnName);
+      @:return record.fk@(column.PropertyName).@(column.LowerFkDisplayColumnsList?.First());
     @:},
 }else if(column.EffectType == "Switch"){
     @:customRender: ({ record }) => {
@@ -44,7 +44,7 @@ if(column.EffectType == "Upload"){
 ];
 
 export const searchFormSchema: FormSchema[] = [
-@foreach (var column in Model.QueryWhetherList){  
+@foreach (var column in Model.WhetherQueryList){  
   @:{
     @:field: '@column.LowerPropertyName',
     @:label: '@column.ColumnComment',

+ 2 - 2
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/editDialog.vue.vm

@@ -35,12 +35,12 @@
 						</el-form-item>
 					</el-col>
 					}else if(column.EffectType == "ApiTreeSelector"){
-					displayColumnList = column.DisplayColumn.Split(",").Select(u => $"${{data.{LowerFirstLetter(u)}}}").ToList();
+					displayColumnList = column.FkDisplayColumns.Split(",").Select(u => $"${{data.{LowerFirstLetter(u)}}}").ToList();
 					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
 							<el-cascader
 								@:options="@LowerFirstLetter(@column.FkEntityName)TreeData"
-								@:props="{ checkStrictly: true, emitPath: false, value: '@LowerFirstLetter(@column.ValueColumn)' }"
+								@:props="{ checkStrictly: true, emitPath: false, value: '@LowerFirstLetter(@column.FkLinkColumnName)' }"
 								placeholder="请选择@(column.ColumnComment)"
 								clearable
 								filterable

+ 6 - 6
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/index.vue.vm

@@ -11,7 +11,7 @@
 
   bool hasImport = Model.TableField.Any(x => x.WhetherImport == "Y");
 
-  bool haveLikeCdt = Model.TableField.Any(col => col.QueryWhether == "Y" && col.QueryType == "like");
+  bool haveLikeCdt = Model.TableField.Any(col => col.WhetherQuery == "Y" && col.QueryType == "like");
 
   var hasSetStatus = Model.TableField.Any(col => col.NetType == "StatusEnum" && col.PropertyName == "Status");
 
@@ -61,7 +61,7 @@
             @:<el-form-item label="@column.ColumnComment">
               @:<el-cascader
                   @::options="@LowerFirstLetter(@column.FkEntityName)TreeData"
-                  @:@:props="{ checkStrictly: true, emitPath: false, value: '@LowerFirstLetter(@column.ValueColumn)' }"
+                  @:@:props="{ checkStrictly: true, emitPath: false, value: '@LowerFirstLetter(@column.FkLinkColumnName)' }"
                   @:placeholder="请选择@(column.ColumnComment)"
                   @:clearable
                   @:filterable
@@ -76,7 +76,7 @@
             @:</el-form-item>
             }else if(column.EffectType == "DatePicker"){
             @:<el-form-item label="@column.ColumnComment">
-              if(column.QueryType == "~"){
+              if (column.QueryType == "~") {
               @:<el-date-picker type="daterange" v-model="queryParams.@(column.LowerPropertyName)Range"  value-format="YYYY-MM-DD HH:mm:ss" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" />
               } else {
               @:<el-date-picker placeholder="请选择@(column.ColumnComment)" value-format="YYYY/MM/DD"  v-model="queryParams.@(column.LowerPropertyName)" />
@@ -89,10 +89,10 @@
           <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10">
             <el-form-item @(Model.QueryWhetherList.Count > 0?"":"label-width=\"0px\"")>
               <el-button-group style="display: flex; align-items: center;">
-                <el-button type="primary"  icon="ele-Search" @@click="handleQuery" v-auth="'@(Model.LowerClassName):page'"> @(Model.QueryWhetherList.Count > 0?"查询":"刷新") </el-button>
-                @if(Model.QueryWhetherList.Count > 0){
+                <el-button type="primary"  icon="ele-Search" @@click="handleQuery" v-auth="'@(Model.LowerClassName):page'"> @(Model.QueryWhetherList.Count > 0 ? "查询" : "刷新") </el-button>
+                @if (Model.QueryWhetherList.Count > 0) {
                 @:<el-button icon="ele-Refresh" @@click="() => queryParams = {}"> 重置 </el-button>
-                @if(haveLikeCdt){
+                @if (haveLikeCdt) {
                 @:<el-button icon="ele-ZoomIn" @@click="changeAdvanceQueryUI" v-if="!showAdvanceQueryUI" style="margin-left:5px;"> 高级查询 </el-button>
                 @:<el-button icon="ele-ZoomOut" @@click="changeAdvanceQueryUI" v-if="showAdvanceQueryUI" style="margin-left:5px;"> 隐藏 </el-button>
                 }

+ 0 - 156
Web/src/views/system/codeGen/component/fkDialog.vue

@@ -1,156 +0,0 @@
-<template>
-	<div class="sys-codeGenFk-container">
-		<el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" width="700px" destroy-on-close>
-			<template #header>
-				<div style="color: #fff">
-					<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Edit /> </el-icon>
-					<span> 外键配置</span>
-				</div>
-			</template>
-			<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto">
-				<el-row :gutter="35">
-					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
-						<el-form-item label="库定位器" prop="configId" :rules="[{ required: true, message: '库不能为空', trigger: 'blur' }]">
-							<el-select clearable v-model="state.ruleForm.configId" placeholder="库名" filterable @change="DbChanged()" 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="24" :md="24" :lg="24" :xl="24" class="mb20">
-						<el-form-item label="数据库表" prop="tableName" :rules="[{ required: true, message: '数据表不能为空', trigger: 'blur' }]">
-							<el-select v-model="state.ruleForm.tableName" filterable clearable @change="TableChanged()" class="w100">
-								<el-option v-for="item in state.tableData" :key="item.entityName" :label="item.entityName + ' ( ' + item.tableName + ' )[' + item.tableComment + ']'" :value="item.tableName" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
-						<el-form-item label="显示字段" prop="fkColumnList" :rules="[{ required: true, message: '显示字段不能为空', trigger: 'blur' }]">
-							<el-select v-model="state.ruleForm.fkColumnList" multiple filterable class="w100">
-								<el-option v-for="item in state.columnData" :key="item.columnName" :label="item.columnName + ' [' + item.columnComment + ']'" :value="item.columnName" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
-						<el-form-item label="链接字段" prop="linkColumnName" :rules="[{ required: true, message: '链接字段不能为空', trigger: 'blur' }]">
-							<el-select v-model="state.ruleForm.linkColumnName" class="w100">
-								<el-option v-for="item in state.columnData" :key="item.columnName" :label="item.columnName + ' [' + item.columnComment + ']'" :value="item.columnName" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-				</el-row>
-			</el-form>
-			<template #footer>
-				<span class="dialog-footer">
-					<el-button @click="cancel">取 消</el-button>
-					<el-button type="primary" @click="submit">确 定</el-button>
-				</span>
-			</template>
-		</el-dialog>
-	</div>
-</template>
-
-<script lang="ts" setup name="sysCodeGenFk">
-import { onMounted, reactive, ref } from 'vue';
-
-import { getAPI } from '/@/utils/axios-utils';
-import { SysCodeGenApi } from '/@/api-services/api';
-
-let rowData = {} as any;
-const emits = defineEmits(['submitRefreshFk']);
-const ruleFormRef = ref();
-const state = reactive({
-	isShowDialog: false,
-	ruleForm: {} as any,
-	dbData: [] as any,
-	tableData: [] as any,
-	columnData: [] as any,
-});
-
-onMounted(async () => {
-	await getDbList();
-
-	// 默认使用第一个库
-	//state.ruleForm.configId = state.dbData[0].configId;
-	//await DbChanged();
-});
-
-const DbChanged = async () => {
-	state.tableData = [];
-	state.columnData = [];
-	await getTableInfoList();
-};
-
-const TableChanged = async () => {
-	state.columnData = [];
-	await getColumnInfoList();
-  state.ruleForm.fkColumnList = null;
-  state.ruleForm.linkColumnName = null;
-};
-
-const getDbList = async () => {
-  const res = await getAPI(SysCodeGenApi).apiSysCodeGenDatabaseListGet();
-	state.dbData = res.data.result;
-};
-
-const getTableInfoList = async () => {
-	if (state.ruleForm.configId == '') return;
-	const res = await getAPI(SysCodeGenApi).apiSysCodeGenTableListConfigIdGet(state.ruleForm.configId);
-	state.tableData = res.data.result;
-};
-
-const getColumnInfoList = async () => {
-	if (state.ruleForm.configId == '' || state.ruleForm.tableName == '') return;
-	console.log(state.ruleForm.configId, state.ruleForm.tableName);
-	const res = await getAPI(SysCodeGenApi).apiSysCodeGenColumnListByTableNameTableNameConfigIdGet(state.ruleForm.tableName, state.ruleForm.configId);
-	state.columnData = res.data.result;
-};
-
-// 打开弹窗
-const openDialog = async (row: any) => {
-	rowData = row;
-	if (rowData.fkConfigId) {
-		await getDbList();
-    state.ruleForm.configId = rowData.fkConfigId;
-		state.ruleForm.tableName = rowData.fkTableName;
-		await DbChanged();
-		await TableChanged();
-    state.ruleForm.fkColumnList = rowData.fkColumnList;
-    state.ruleForm.linkColumnName = rowData.fkLinkColumnName;
-	}
-	state.isShowDialog = true;
-};
-
-// 关闭弹窗
-const closeDialog = () => {
-  rowData.fkColumnNetType = state.columnData.find(x => x.columnName == state.ruleForm.linkColumnName)?.netType;
-  const table = state.tableData.find(x => x.tableName == state.ruleForm.tableName);
-  rowData.fkLinkColumnName = state.ruleForm.linkColumnName;
-  rowData.fkColumnList = state.ruleForm.fkColumnList;
-  rowData.fkTableName = state.ruleForm.tableName;
-  rowData.fkConfigId = state.ruleForm.configId;
-  rowData.fkEntityName = table?.entityName;
-	emits('submitRefreshFk', rowData);
-	cancel();
-};
-
-// 取消
-const cancel = () => {
-	state.isShowDialog = false;
-	ruleFormRef.value?.resetFields();
-	state.ruleForm = {};
-	state.dbData.value = [];
-	state.tableData.value = [];
-	state.columnData.value = [];
-};
-
-// 提交
-const submit = () => {
-	ruleFormRef.value.validate(async (valid: boolean) => {
-		if (!valid) return;
-		closeDialog();
-	});
-};
-
-// 导出对象
-defineExpose({ openDialog });
-</script>

+ 23 - 38
Web/src/views/system/codeGen/component/genConfigDialog.vue

@@ -19,16 +19,15 @@
 				<el-table-column prop="effectType" label="作用类型" width="150" show-overflow-tooltip>
 					<template #default="scope">
 						<div class="effect-type-container">
-							<el-select v-model="scope.row.effectType" class="m-2" placeholder="Select" :disabled="judgeColumns(scope.row)" @change="effectTypeChange(scope.row, scope.$index)">
+							<el-select v-model="scope.row.effectType" @change="effectTypeChange(scope.row, scope.$index)" :disabled="judgeColumns(scope.row)" class="m-2">
 								<el-option v-for="item in getDictDataByCode('code_gen_effect_type')" :key="item.code" :label="item.value" :value="item.code" />
 							</el-select>
-							<el-button
-								v-if="scope.row.effectType === 'ApiTreeSelector' || scope.row.effectType === 'ForeignKey'"
-								:icon="Edit"
-								title="修改"
-								link
-								@click="effectTypeChange(scope.row, scope.$index)"
-							/>
+              <el-button
+                  v-if="['ApiTreeSelector','ForeignKey'].some(x => scope.row.effectType == x)"
+                  @click="effectTypeChange(scope.row, scope.$index)"
+                  type="warning"
+                  :icon="Edit"
+                  link />
 						</div>
 					</template>
 				</el-table-column>
@@ -68,14 +67,14 @@
 						<el-checkbox v-model="scope.row.whetherSortable" true-value="Y" false-value="N" />
 					</template>
 				</el-table-column>
-				<el-table-column prop="queryWhether" label="查询" width="70" align="center" show-overflow-tooltip>
+				<el-table-column prop="whetherQuery" label="查询" width="70" align="center" show-overflow-tooltip>
 					<template #default="scope">
-						<el-switch v-model="scope.row.queryWhether" active-value="Y" inactive-value="N" />
+						<el-switch v-model="scope.row.whetherQuery" active-value="Y" inactive-value="N" />
 					</template>
 				</el-table-column>
 				<el-table-column prop="queryType" label="查询方式" width="110" align="center" show-overflow-tooltip>
 					<template #default="scope">
-						<el-select v-model="scope.row.queryType" class="m-2" placeholder="Select" :disabled="!scope.row.queryWhether">
+						<el-select v-model="scope.row.queryType" class="m-2" placeholder="Select" :disabled="!scope.row.whetherQuery">
 							<el-option v-for="item in getDictDataByCode('code_gen_query_type')" :key="item.code" :label="item.value" :value="item.code" />
 						</el-select>
 					</template>
@@ -94,32 +93,26 @@
 			</template>
 		</el-dialog>
 
-		<fkDialog ref="fkDialogRef" @submitRefreshFk="submitRefreshFk" />
-		<treeDialog ref="treeDialogRef" @submitRefreshFk="submitRefreshFk" />
+		<JoinTableDialog ref="joinTableDialogRef" @submitRefreshFk="submitRefreshFk" />
 	</div>
 </template>
 
 <script lang="ts" setup name="sysCodeGenConfig">
 import { onMounted, reactive, ref } from 'vue';
 import { Edit } from '@element-plus/icons-vue';
-
-import fkDialog from '/@/views/system/codeGen/component/fkDialog.vue';
-import treeDialog from '/@/views/system/codeGen/component/treeDialog.vue';
-
-import {useUserInfo} from "/@/stores/userInfo";
+import { useUserInfo } from "/@/stores/userInfo";
 import { getAPI } from '/@/utils/axios-utils';
 import { SysCodeGenConfigApi, SysDictTypeApi } from '/@/api-services/api';
-import { CodeGenConfig } from '/@/api-services/models/code-gen-config';
+import JoinTableDialog from '/src/views/system/codeGen/component/joinTableDialog.vue';
 
 const getDictDataByCode = useUserInfo().getDictDataByCode;
 const emits = defineEmits(['handleQuery']);
-const fkDialogRef = ref();
-const treeDialogRef = ref();
+const joinTableDialogRef = ref();
 const state = reactive({
 	isShowDialog: false,
 	loading: false,
   selectDataMap: {} as any,
-  tableData: [] as CodeGenConfig[]
+  tableData: [] as any[]
 });
 
 onMounted(async () => {
@@ -137,12 +130,9 @@ const submitRefreshFk = (data: any) => {
 
 // 控件类型改变
 const effectTypeChange = (data: any, index: number) => {
-	let value = data.effectType;
-	if (value === 'ForeignKey') {
-		openFkDialog(data, index);
-	} else if (value === 'ApiTreeSelector') {
-		openTreeDialog(data, index);
-	} else if (['DictSelector', 'ConstSelector', 'EnumSelector'].some(key => value === key)) {
+	if (['ForeignKey', 'ApiTreeSelector'].some(type => data.effectType == type)) {
+    openJoinTableDialog(data, 'ForeignKey' === data.effectType ? '外键配置' : '树选择器配置', index);
+	} else if (['DictSelector', 'ConstSelector', 'EnumSelector'].some(type => data.effectType === type)) {
 		data.dictTypeCode = '';
 	}
 };
@@ -164,20 +154,15 @@ function effectTypeEnable(data: any) {
 }
 
 // 打开弹窗
-const openDialog = (addRow: any) => {
-	handleQuery(addRow);
+const openDialog = (row: any) => {
+	handleQuery(row);
 	state.isShowDialog = true;
 };
 
 // 打开弹窗
-const openFkDialog = (addRow: any, index: number) => {
-	addRow.index = index;
-	fkDialogRef.value.openDialog(addRow);
-};
-
-const openTreeDialog = (addRow: any, index: number) => {
-	addRow.index = index;
-	treeDialogRef.value.openDialog(addRow);
+const openJoinTableDialog = (row: any, title: string, index: number) => {
+  row.index = index;
+  joinTableDialogRef.value.openDialog(row, title);
 };
 
 // 关闭弹窗

+ 39 - 58
Web/src/views/system/codeGen/component/treeDialog.vue → Web/src/views/system/codeGen/component/joinTableDialog.vue

@@ -4,42 +4,42 @@
 			<template #header>
 				<div style="color: #fff">
 					<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Edit /> </el-icon>
-					<span> 树选择配置 </span>
+					<span> {{state.dialogTitle}} </span>
 				</div>
 			</template>
 			<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto">
 				<el-row :gutter="35">
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
-						<el-form-item label="库定位器" prop="configId" :rules="[{ required: true, message: '库不能为空', trigger: 'blur' }]">
-							<el-select clearable v-model="state.ruleForm.configId" placeholder="库名" filterable @change="DbChanged()" class="w100">
+						<el-form-item label="库定位器" prop="fkConfigId" :rules="[{ required: true, message: '库不能为空', trigger: 'blur' }]">
+							<el-select v-model="state.ruleForm.fkConfigId" placeholder="库名" filterable clearable @change="DbChanged()" 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="24" :md="24" :lg="24" :xl="24" class="mb20">
-						<el-form-item label="数据库表" prop="tableName" :rules="[{ required: true, message: '数据表不能为空', trigger: 'blur' }]">
-							<el-select v-model="state.ruleForm.tableName" class="w100" filterable clearable @change="TableChanged()">
+						<el-form-item label="数据库表" prop="fkTableName" :rules="[{ required: true, message: '数据表不能为空', trigger: 'blur' }]">
+							<el-select v-model="state.ruleForm.fkTableName" class="w100" filterable clearable @change="TableChanged()">
 								<el-option v-for="item in state.tableData" :key="item.entityName" :label="item.tableName + ' [' + item.tableComment + ']'" :value="item.tableName" />
 							</el-select>
 						</el-form-item>
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
-						<el-form-item label="显示字段" prop="displayColumnList" :rules="[{ required: true, message: '显示字段不能为空', trigger: 'blur' }]">
-							<el-select v-model="state.ruleForm.displayColumnList" multiple filterable class="w100">
+						<el-form-item label="显示字段" prop="fkDisplayColumnList" :rules="[{ required: true, message: '显示字段不能为空', trigger: 'blur' }]">
+							<el-select v-model="state.ruleForm.fkDisplayColumnList" multiple filterable clearable class="w100">
 								<el-option v-for="item in state.columnData" :key="item.columnName" :label="item.columnName + ' [' + item.columnComment + ']'" :value="item.columnName" />
 							</el-select>
 						</el-form-item>
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
-						<el-form-item label="选择值字段" prop="valueColumn" :rules="[{ required: true, message: '值字段不能为空', trigger: 'blur' }]">
-							<el-select v-model="state.ruleForm.valueColumn" class="w100">
+						<el-form-item label="值&ensp;字&ensp;段" prop="fkLinkColumnName" :rules="[{ required: true, message: '值字段不能为空', trigger: 'blur' }]">
+							<el-select v-model="state.ruleForm.fkLinkColumnName" filterable clearable class="w100">
 								<el-option v-for="item in state.columnData" :key="item.columnName" :label="item.columnName + ' [' + item.columnComment + ']'" :value="item.columnName" />
 							</el-select>
 						</el-form-item>
 					</el-col>
-					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20" v-if="state.ruleForm.effectType == 'ApiTreeSelector'">
 						<el-form-item label="父级字段" prop="pidColumn" :rules="[{ required: true, message: '父级字段不能为空', trigger: 'blur' }]">
-							<el-select v-model="state.ruleForm.pidColumn" class="w100">
+							<el-select v-model="state.ruleForm.pidColumn" filterable clearable class="w100">
 								<el-option v-for="item in state.columnData" :key="item.columnName" :label="item.columnName + ' [' + item.columnComment + ']'" :value="item.columnName" />
 							</el-select>
 						</el-form-item>
@@ -57,15 +57,14 @@
 </template>
 
 <script lang="ts" setup name="sysCodeGenTree">
-import { onMounted, reactive, ref } from 'vue';
-
+import { reactive, ref } from 'vue';
 import { getAPI } from '/@/utils/axios-utils';
 import { SysCodeGenApi } from '/@/api-services/api';
 
-let rowData = {} as any;
 const emits = defineEmits(['submitRefreshFk']);
 const ruleFormRef = ref();
 const state = reactive({
+  dialogTitle: '' as string,
 	isShowDialog: false,
 	ruleForm: {} as any,
 	dbData: [] as any,
@@ -73,14 +72,6 @@ const state = reactive({
 	columnData: [] as any,
 });
 
-onMounted(async () => {
-	await getDbList();
-
-	// 默认使用第一个库
-	//state.ruleForm.configId = state.dbData[0].configId;
-	//await DbChanged();
-});
-
 const DbChanged = async () => {
 	state.tableData = [];
 	state.columnData = [];
@@ -90,66 +81,56 @@ const DbChanged = async () => {
 const TableChanged = async () => {
 	state.columnData = [];
 	await getColumnInfoList();
-	state.ruleForm.displayColumnList = undefined;
-	state.ruleForm.valueColumn = undefined;
-	state.ruleForm.pidColumn = undefined;
+  state.ruleForm.pidColumn = undefined;
+	state.ruleForm.fkDisplayColumnList = undefined;
+  state.ruleForm.fkLinkColumnName = state.columnData.find(x => "True" === x.columnKey)?.columnName;
 };
 
 const getDbList = async () => {
-	const res = await getAPI(SysCodeGenApi).apiSysCodeGenDatabaseListGet();
-	state.dbData = res.data.result;
+  state.dbData = await getAPI(SysCodeGenApi).apiSysCodeGenDatabaseListGet().then(res => res.data.result ?? []);
 };
 
 const getTableInfoList = async () => {
-	if (state.ruleForm.configId == '') return;
-  const res = await getAPI(SysCodeGenApi).apiSysCodeGenTableListConfigIdGet(state.ruleForm.configId);
-	state.tableData = res.data.result;
+	if (!state.ruleForm.fkConfigId) return;
+  state.tableData = await getAPI(SysCodeGenApi)
+      .apiSysCodeGenTableListConfigIdGet(state.ruleForm.fkConfigId)
+      .then(res => res.data.result ?? []);
 };
 
 const getColumnInfoList = async () => {
-	if (state.ruleForm.configId == '' || state.ruleForm.tableName == '') return;
-  const res = await getAPI(SysCodeGenApi).apiSysCodeGenColumnListByTableNameTableNameConfigIdGet(state.ruleForm.tableName, state.ruleForm.configId);
-	state.columnData = res.data.result;
+	if (!state.ruleForm.fkConfigId || !state.ruleForm.fkTableName) return;
+  state.columnData = await getAPI(SysCodeGenApi)
+      .apiSysCodeGenColumnListByTableNameTableNameConfigIdGet(state.ruleForm.fkTableName, state.ruleForm.fkConfigId)
+      .then(res => res.data.result ?? []);
 };
 
 // 打开弹窗
-const openDialog = async (row: any) => {
-	rowData = row;
-	state.isShowDialog = true;
-	ruleFormRef.value?.resetFields();
-	if (rowData.fkConfigId) {
-		await getDbList();
-    state.ruleForm.configId = rowData.fkConfigId;
-		state.ruleForm.tableName = rowData.fkTableName;
-
+const openDialog = async (row: any, title: string) => {
+  await getDbList();
+  state.dialogTitle = title;
+  state.isShowDialog = true;
+  state.ruleForm = JSON.parse(JSON.stringify(row));
+	if (row.fkConfigId) {
 		await DbChanged();
-		await TableChanged();
-
-    state.ruleForm.pidColumn = rowData.pidColumn;
-    state.ruleForm.valueColumn = rowData.valueColumn;
-    state.ruleForm.displayColumnList = rowData.displayColumnList;
+    await TableChanged();
+    state.ruleForm.pidColumn = row.pidColumn;
+    state.ruleForm.fkLinkColumnName = row.fkLinkColumnName;
+    state.ruleForm.fkDisplayColumnList = row.fkDisplayColumnList;
 	}
 };
 
 // 关闭弹窗
 const closeDialog = () => {
-  rowData.fkColumnNetType = state.columnData.find(x => x.columnName == state.ruleForm.valueColumn)?.netType;
-	let table = state.tableData.find(x => x.tableName == state.ruleForm.tableName);
-	rowData.displayColumn = state.ruleForm.displayColumn;
-	rowData.valueColumn = state.ruleForm.valueColumn;
-  rowData.fkTableName = state.ruleForm.tableName;
-	rowData.pidColumn = state.ruleForm.pidColumn;
-	rowData.fkConfigId = state.ruleForm.configId;
-  rowData.fkEntityName = table?.entityName;
-	emits('submitRefreshFk', rowData);
+  state.ruleForm.fkColumnNetType = state.columnData.find(x => x.columnName == state.ruleForm.fkLinkColumnName)?.netType;
+  state.ruleForm.fkEntityName = state.tableData.find(x => x.tableName == state.ruleForm.fkTableName)?.entityName;
+	if (state.ruleForm.effectType != 'ApiTreeSelector') state.ruleForm.pidColumn = null;
+  emits('submitRefreshFk', state.ruleForm);
 	cancel();
 };
 
 // 取消
 const cancel = () => {
 	state.isShowDialog = false;
-	ruleFormRef.value?.resetFields();
-	state.ruleForm = {};
 	state.dbData.value = [];
 	state.tableData.value = [];
 	state.columnData.value = [];