소스 검색

!1467 增强代码生成功能
Merge pull request !1467 from 喵你个汪/dev

zuohuaijun 1 년 전
부모
커밋
1426b8f7e2

+ 3 - 0
Admin.NET/Admin.NET.Application/GlobalUsings.cs

@@ -6,6 +6,7 @@
 
 global using Admin.NET.Core;
 global using Furion;
+global using Furion.DatabaseAccessor;
 global using Furion.DependencyInjection;
 global using Furion.DynamicApiController;
 global using Furion.FriendlyException;
@@ -13,6 +14,8 @@ global using Mapster;
 global using Microsoft.AspNetCore.Authorization;
 global using Microsoft.AspNetCore.Mvc;
 global using Microsoft.Extensions.DependencyInjection;
+global using Magicodes.ExporterAndImporter.Core;
+global using Magicodes.ExporterAndImporter.Excel;
 global using SqlSugar;
 global using System;
 global using System.Collections.Generic;

+ 8 - 1
Admin.NET/Admin.NET.Core/Entity/SysCodeGenConfig.cs

@@ -157,7 +157,14 @@ public partial class SysCodeGenConfig : EntityBase
     [SugarColumn(ColumnDescription = "增改", Length = 8)]
     [MaxLength(8)]
     public string? WhetherAddUpdate { get; set; }
-
+    
+    /// <summary>
+    /// 导入
+    /// </summary>
+    [SugarColumn(ColumnDescription = "导入", Length = 8)]
+    [MaxLength(8)]
+    public string? WhetherImport { get; set; }
+    
     /// <summary>
     /// 主键
     /// </summary>

+ 1 - 1
Admin.NET/Admin.NET.Core/SeedData/SysDictDataSeedData.cs

@@ -22,7 +22,7 @@ public class SysDictDataSeedData : ISqlSugarEntitySeedData<SysDictData>
             new SysDictData{ Id=1300000000101, DictTypeId=1300000000101, Value="输入框", Code="Input", OrderNo=100, Remark="输入框", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysDictData{ Id=1300000000102, DictTypeId=1300000000101, Value="外键", Code="fk", OrderNo=100, Remark="外键", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysDictData{ Id=1300000000103, DictTypeId=1300000000101, Value="时间选择", Code="DatePicker", OrderNo=100, Remark="时间选择", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysDictData{ Id=1300000000104, DictTypeId=1300000000101, Value="选择器", Code="Select", OrderNo=100, Remark="选择器", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysDictData{ Id=1300000000104, DictTypeId=1300000000101, Value="字典选择器", Code="Select", OrderNo=100, Remark="字典选择器", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysDictData{ Id=1300000000105, DictTypeId=1300000000101, Value="数字输入框", Code="InputNumber", OrderNo=100, Remark="数字输入框", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysDictData{ Id=1300000000106, DictTypeId=1300000000101, Value="文本域", Code="InputTextArea", OrderNo=100, Remark="文本域", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysDictData{ Id=1300000000107, DictTypeId=1300000000101, Value="上传", Code="Upload", OrderNo=100, Remark="上传", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },

+ 6 - 1
Admin.NET/Admin.NET.Core/Service/CodeGen/Dto/CodeGenConfig.cs

@@ -137,7 +137,12 @@ public class CodeGenConfig
     /// 增改
     /// </summary>
     public string WhetherAddUpdate { get; set; }
-
+    
+    /// <summary>
+    /// 导入
+    /// </summary>
+    public string WhetherImport { get; set; }
+    
     /// <summary>
     /// 主外键
     /// </summary>

+ 5 - 0
Admin.NET/Admin.NET.Core/Service/CodeGen/Dto/ColumnOuput.cs

@@ -45,6 +45,11 @@ public class ColumnOuput
     /// .NET字段类型
     /// </summary>
     public string NetType { get; set; }
+    
+    /// <summary>
+    /// 字典编码
+    /// </summary>
+    public string DictTypeCode { get; set; }
 
     /// <summary>
     /// 字段描述

+ 159 - 152
Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenConfigService.cs

@@ -1,153 +1,160 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Core.Service;
-
-/// <summary>
-/// 系统代码生成配置服务 🧩
-/// </summary>
-[ApiDescriptionSettings(Order = 260)]
-public class SysCodeGenConfigService : IDynamicApiController, ITransient
-{
-    private readonly ISqlSugarClient _db;
-
-    public SysCodeGenConfigService(ISqlSugarClient db)
-    {
-        _db = db;
-    }
-
-    /// <summary>
-    /// 获取代码生成配置列表 🔖
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [DisplayName("获取代码生成配置列表")]
-    public async Task<List<CodeGenConfig>> GetList([FromQuery] CodeGenConfig input)
-    {
-        return await _db.Queryable<SysCodeGenConfig>()
-            .Where(u => u.CodeGenId == input.CodeGenId)
-            .Select<CodeGenConfig>()
-            .Mapper(u =>
-            {
-                u.NetType = (u.EffectType == "EnumSelector" || u.EffectType == "ConstSelector" ? u.DictTypeCode : u.NetType);
-            })
-            .OrderBy(u => new { u.OrderNo, u.Id })
-            .ToListAsync();
-    }
-
-    /// <summary>
-    /// 更新代码生成配置 🔖
-    /// </summary>
-    /// <param name="inputList"></param>
-    /// <returns></returns>
-    [ApiDescriptionSettings(Name = "Update"), HttpPost]
-    [DisplayName("更新代码生成配置")]
-    public async Task UpdateCodeGenConfig(List<CodeGenConfig> inputList)
-    {
-        if (inputList == null || inputList.Count < 1) return;
-        await _db.Updateable(inputList.Adapt<List<SysCodeGenConfig>>())
-            .IgnoreColumns(u => new { u.ColumnLength, u.ColumnName, u.PropertyName })
-            .ExecuteCommandAsync();
-    }
-
-    /// <summary>
-    /// 删除代码生成配置
-    /// </summary>
-    /// <param name="codeGenId"></param>
-    /// <returns></returns>
-    [NonAction]
-    public async Task DeleteCodeGenConfig(long codeGenId)
-    {
-        await _db.Deleteable<SysCodeGenConfig>().Where(u => u.CodeGenId == codeGenId).ExecuteCommandAsync();
-    }
-
-    /// <summary>
-    /// 获取代码生成配置详情 🔖
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [DisplayName("获取代码生成配置详情")]
-    public async Task<SysCodeGenConfig> GetDetail([FromQuery] CodeGenConfig input)
-    {
-        return await _db.Queryable<SysCodeGenConfig>().FirstAsync(u => u.Id == input.Id);
-    }
-
-    /// <summary>
-    /// 批量增加代码生成配置
-    /// </summary>
-    /// <param name="tableColumnOutputList"></param>
-    /// <param name="codeGenerate"></param>
-    [NonAction]
-    public void AddList(List<ColumnOuput> tableColumnOutputList, SysCodeGen codeGenerate)
-    {
-        if (tableColumnOutputList == null) return;
-
-        var codeGenConfigs = new List<SysCodeGenConfig>();
-        var orderNo = 100;
-        foreach (var tableColumn in tableColumnOutputList)
-        {
-            var codeGenConfig = new SysCodeGenConfig();
-
-            var YesOrNo = YesNoEnum.Y.ToString();
-            if (Convert.ToBoolean(tableColumn.ColumnKey))
-            {
-                YesOrNo = YesNoEnum.N.ToString();
-            }
-
-            if (CodeGenUtil.IsCommonColumn(tableColumn.PropertyName))
-            {
-                codeGenConfig.WhetherCommon = YesNoEnum.Y.ToString();
-                YesOrNo = YesNoEnum.N.ToString();
-            }
-            else
-            {
-                codeGenConfig.WhetherCommon = YesNoEnum.N.ToString();
-            }
-
-            codeGenConfig.CodeGenId = codeGenerate.Id;
-            codeGenConfig.ColumnName = tableColumn.ColumnName; // 字段名
-            codeGenConfig.PropertyName = tableColumn.PropertyName;// 实体属性名
-            codeGenConfig.ColumnLength = tableColumn.ColumnLength;// 长度
-            codeGenConfig.ColumnComment = tableColumn.ColumnComment;
-            codeGenConfig.NetType = tableColumn.NetType;
-            codeGenConfig.WhetherRetract = YesNoEnum.N.ToString();
-
-            // 生成代码时,主键并不是必要输入项,故一定要排除主键字段
-            codeGenConfig.WhetherRequired = (tableColumn.IsNullable || tableColumn.IsPrimarykey) ? YesNoEnum.N.ToString() : YesNoEnum.Y.ToString();
-            codeGenConfig.QueryWhether = YesOrNo;
-            codeGenConfig.WhetherAddUpdate = YesOrNo;
-            codeGenConfig.WhetherTable = YesOrNo;
-
-            codeGenConfig.ColumnKey = tableColumn.ColumnKey;
-
-            codeGenConfig.DataType = tableColumn.DataType;
-            codeGenConfig.EffectType = CodeGenUtil.DataTypeToEff(codeGenConfig.NetType);
-            codeGenConfig.QueryType = GetDefaultQueryType(codeGenConfig); // QueryTypeEnum.eq.ToString();
-            codeGenConfig.OrderNo = orderNo;
-            codeGenConfigs.Add(codeGenConfig);
-
-            orderNo += 10; // 每个配置排序间隔10
-        }
-        // 多库代码生成---这里要切回主库
-        var provider = _db.AsTenant().GetConnectionScope(SqlSugarConst.MainConfigId);
-        provider.Insertable(codeGenConfigs).ExecuteCommand();
-    }
-
-    /// <summary>
-    /// 默认查询类型
-    /// </summary>
-    /// <param name="codeGenConfig"></param>
-    /// <returns></returns>
-    private static string GetDefaultQueryType(SysCodeGenConfig codeGenConfig)
-    {
-        return (codeGenConfig.NetType?.TrimEnd('?')) switch
-        {
-            "string" => "like",
-            "DateTime" => "~",
-            _ => "==",
-        };
-    }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 系统代码生成配置服务 🧩
+/// </summary>
+[ApiDescriptionSettings(Order = 260)]
+public class SysCodeGenConfigService : IDynamicApiController, ITransient
+{
+    private readonly ISqlSugarClient _db;
+
+    public SysCodeGenConfigService(ISqlSugarClient db)
+    {
+        _db = db;
+    }
+
+    /// <summary>
+    /// 获取代码生成配置列表 🔖
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("获取代码生成配置列表")]
+    public async Task<List<CodeGenConfig>> GetList([FromQuery] CodeGenConfig input)
+    {
+        return await _db.Queryable<SysCodeGenConfig>()
+            .Where(u => u.CodeGenId == input.CodeGenId)
+            .Select<CodeGenConfig>()
+            .Mapper(u =>
+            {
+                u.NetType = (u.EffectType == "EnumSelector" || u.EffectType == "ConstSelector" ? u.DictTypeCode : u.NetType);
+            })
+            .OrderBy(u => new { u.OrderNo, u.Id })
+            .ToListAsync();
+    }
+
+    /// <summary>
+    /// 更新代码生成配置 🔖
+    /// </summary>
+    /// <param name="inputList"></param>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "Update"), HttpPost]
+    [DisplayName("更新代码生成配置")]
+    public async Task UpdateCodeGenConfig(List<CodeGenConfig> inputList)
+    {
+        if (inputList == null || inputList.Count < 1) return;
+        await _db.Updateable(inputList.Adapt<List<SysCodeGenConfig>>())
+            .IgnoreColumns(u => new { u.ColumnLength, u.ColumnName, u.PropertyName })
+            .ExecuteCommandAsync();
+    }
+
+    /// <summary>
+    /// 删除代码生成配置
+    /// </summary>
+    /// <param name="codeGenId"></param>
+    /// <returns></returns>
+    [NonAction]
+    public async Task DeleteCodeGenConfig(long codeGenId)
+    {
+        await _db.Deleteable<SysCodeGenConfig>().Where(u => u.CodeGenId == codeGenId).ExecuteCommandAsync();
+    }
+
+    /// <summary>
+    /// 获取代码生成配置详情 🔖
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("获取代码生成配置详情")]
+    public async Task<SysCodeGenConfig> GetDetail([FromQuery] CodeGenConfig input)
+    {
+        return await _db.Queryable<SysCodeGenConfig>().FirstAsync(u => u.Id == input.Id);
+    }
+
+    /// <summary>
+    /// 批量增加代码生成配置
+    /// </summary>
+    /// <param name="tableColumnOutputList"></param>
+    /// <param name="codeGenerate"></param>
+    [NonAction]
+    public void AddList(List<ColumnOuput> tableColumnOutputList, SysCodeGen codeGenerate)
+    {
+        if (tableColumnOutputList == null) return;
+
+        var codeGenConfigs = new List<SysCodeGenConfig>();
+        var orderNo = 100;
+        foreach (var tableColumn in tableColumnOutputList)
+        {
+            var codeGenConfig = new SysCodeGenConfig();
+
+            var YesOrNo = YesNoEnum.Y.ToString();
+            if (Convert.ToBoolean(tableColumn.ColumnKey))
+            {
+                YesOrNo = YesNoEnum.N.ToString();
+            }
+
+            if (CodeGenUtil.IsCommonColumn(tableColumn.PropertyName))
+            {
+                codeGenConfig.WhetherCommon = YesNoEnum.Y.ToString();
+                YesOrNo = YesNoEnum.N.ToString();
+            }
+            else
+            {
+                codeGenConfig.WhetherCommon = YesNoEnum.N.ToString();
+            }
+
+            codeGenConfig.CodeGenId = codeGenerate.Id;
+            codeGenConfig.ColumnName = tableColumn.ColumnName; // 字段名
+            codeGenConfig.PropertyName = tableColumn.PropertyName;// 实体属性名
+            codeGenConfig.ColumnLength = tableColumn.ColumnLength;// 长度
+            codeGenConfig.ColumnComment = tableColumn.ColumnComment;
+            codeGenConfig.NetType = tableColumn.NetType;
+            codeGenConfig.WhetherRetract = YesNoEnum.N.ToString();
+
+            // 生成代码时,主键并不是必要输入项,故一定要排除主键字段
+            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.ColumnKey = tableColumn.ColumnKey;
+
+            codeGenConfig.DataType = tableColumn.DataType;
+            codeGenConfig.EffectType = CodeGenUtil.DataTypeToEff(codeGenConfig.NetType);
+            codeGenConfig.QueryType = GetDefaultQueryType(codeGenConfig); // QueryTypeEnum.eq.ToString();
+            codeGenConfig.OrderNo = orderNo;
+            codeGenConfigs.Add(codeGenConfig);
+            
+            if (!string.IsNullOrWhiteSpace(tableColumn.DictTypeCode))
+            {
+                codeGenConfig.DictTypeCode = tableColumn.DictTypeCode;
+                codeGenConfig.EffectType = tableColumn.DictTypeCode.EndsWith("Enum") ? "EnumSelector" : "Select";
+            }
+
+            orderNo += 10; // 每个配置排序间隔10
+        }
+        // 多库代码生成---这里要切回主库
+        var provider = _db.AsTenant().GetConnectionScope(SqlSugarConst.MainConfigId);
+        provider.Insertable(codeGenConfigs).ExecuteCommand();
+    }
+
+    /// <summary>
+    /// 默认查询类型
+    /// </summary>
+    /// <param name="codeGenConfig"></param>
+    /// <returns></returns>
+    private static string GetDefaultQueryType(SysCodeGenConfig codeGenConfig)
+    {
+        return (codeGenConfig.NetType?.TrimEnd('?')) switch
+        {
+            "string" => "like",
+            "DateTime" => "~",
+            _ => "==",
+        };
+    }
 }

+ 13 - 0
Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenService.cs

@@ -233,6 +233,15 @@ public class SysCodeGenService : IDynamicApiController, ITransient
             {
                 columnOutput.PropertyName = propertyInfo.Name;
                 columnOutput.ColumnComment = propertyInfo.GetCustomAttribute<SugarColumn>().ColumnDescription;
+                if (propertyInfo.PropertyType.IsEnum)
+                {
+                    columnOutput.DictTypeCode = propertyInfo.PropertyType.Name;
+                }
+                else
+                {
+                    var dict = propertyInfo.GetCustomAttribute<DictAttribute>();
+                    if (dict != null) columnOutput.DictTypeCode = dict.DictTypeCode;
+                }
             }
             else
             {
@@ -367,6 +376,8 @@ public class SysCodeGenService : IDynamicApiController, ITransient
             {
                 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");
             });
@@ -434,6 +445,8 @@ public class SysCodeGenService : IDynamicApiController, ITransient
             {
                 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");
             });

+ 18 - 3
Admin.NET/Admin.NET.Core/Utils/ExcelHelper.cs

@@ -33,7 +33,7 @@ public class ExcelHelper
                         for (int i = 0; i < rows.Count; i++) pageItems[i].Id = rows[i].Id;
 
                         for (int i = 0; i < storageable.TotalList.Count; i++)
-                            pageItems[i].Error = storageable.TotalList[i].StorageMessage;
+                            pageItems[i].Error ??= storageable.TotalList[i].StorageMessage;
                     }
                 }));
             });
@@ -98,9 +98,24 @@ public class ExcelHelper
                 if (++columnIndex > 0 && item.Text.Equals(headerAttr.DisplayName)) break;
             if (columnIndex <= 0) continue;
 
-            // 优先从代理函数中获取下列列表,若为空且字段为枚举型,则填充枚举项为下列列表,否则不设置下列列表
+            // 优先从代理函数中获取下列列表,若为空且字段为枚举型,则填充枚举项为下列列表,若为字典字段,则填充字典值value列表为下列列表
             var dataList = addListValidationFun?.Invoke(worksheet, prop)?.ToList();
-            if (dataList == null && propType.IsEnum()) dataList = propType.EnumToList()?.Select(it => it.Describe).ToList();
+            if (dataList == null)
+            {
+                if (propType.IsEnum())
+                {// 填充枚举项为下列列表
+                    dataList = propType.EnumToList()?.Select(it => it.Describe).ToList();
+                }
+                else
+                {// 获取字段上的字典特性
+                    var dict = prop.GetCustomAttribute<DictAttribute>();
+                    if (dict != null)
+                    {// 填充字典值value为下列列表
+                        dataList = App.GetService<SysDictTypeService>().GetDataList(new GetDataDictTypeInput
+                            { Code = dict.DictTypeCode }).Result?.Select(x => x.Value).ToList();
+                    }
+                }
+            }
             if (dataList != null) AddListValidation(columnIndex, dataList);
         }
 

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

@@ -6,25 +6,25 @@
 
 namespace @Model.NameSpace;
 
-    /// <summary>
-    /// @(@Model.BusName)输出参数
-    /// </summary>
-    public class @(@Model.ClassName)Dto
-    {
+/// <summary>
+/// @(@Model.BusName)输出参数
+/// </summary>
+public class @(@Model.ClassName)Dto
+{
 @foreach (var column in Model.TableField){
 if(@column.EffectType == "fk" && @column.FkEntityName != "" && @column.FkColumnName != ""){
-        @:/// <summary>
-        @:/// @column.ColumnComment
-        @:/// </summary>
-        @:public @(@column.FkColumnNetType) @(@column.PropertyName)@(@column.FkColumnName) { get; set; }
-        @:
+    @:/// <summary>
+    @:/// @column.ColumnComment
+    @:/// </summary>
+    @:public @(@column.FkColumnNetType) @(@column.PropertyName)@(@column.FkColumnName) { get; set; }
+    @:
 }
 }
 @foreach (var column in Model.TableField){
-        @:/// <summary>
-        @:/// @column.ColumnComment
-        @:/// </summary>
-        @:public @column.NetType @column.PropertyName { get; set; }
-        @:
+    @:/// <summary>
+    @:/// @column.ColumnComment
+    @:/// </summary>
+    @:public @column.NetType @column.PropertyName { get; set; }
+    @:
+}
 }
-    }

+ 101 - 72
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Input.cs.vm

@@ -9,51 +9,51 @@ using System.ComponentModel.DataAnnotations;
 
 namespace @Model.NameSpace;
 
-    /// <summary>
-    /// @(@Model.BusName)基础输入参数
-    /// </summary>
-    public class @(@Model.ClassName)BaseInput
-    {
+/// <summary>
+/// @(@Model.BusName)基础输入参数
+/// </summary>
+public class @(@Model.ClassName)BaseInput
+{
 @foreach (var column in Model.TableField){
 if (@column.ColumnKey != "True"){
 
-        @:/// <summary>
-        @:/// @column.ColumnComment
-        @:/// </summary>
-        @:public virtual @column.NetType @column.PropertyName { get; set; }
-        @:
+    @:/// <summary>
+    @:/// @column.ColumnComment
+    @:/// </summary>
+    @:public virtual @column.NetType @column.PropertyName { get; set; }
+    @:
+}
 }
 }
-    }
 
+/// <summary>
+/// @(@Model.BusName)分页查询输入参数
+/// </summary>
+public class Page@(@Model.ClassName)Input : BasePageInput
+{
     /// <summary>
-    /// @(@Model.BusName)分页查询输入参数
+    /// 关键字查询
     /// </summary>
-    public class Page@(@Model.ClassName)Input : BasePageInput
-    {
-        /// <summary>
-        /// 关键字查询
-        /// </summary>
-        public string? SearchKey { get; set; }
+    public string? SearchKey { get; set; }
 
 @foreach (var column in Model.TableField){
     if (@column.QueryWhether == "Y"){
 
-        @:/// <summary>
-        @:/// @column.ColumnComment
-        @:/// </summary>
-        if(@column.NetType?.EndsWith("?") == true){
-        @:public @column.NetType @column.PropertyName { get; set; }
-        }else {
-        @:public @(@column.NetType)? @column.PropertyName { get; set; }
-        }
-        @:
+    @:/// <summary>
+    @:/// @column.ColumnComment
+    @:/// </summary>
+    if(@column.NetType?.EndsWith("?") == true){
+    @:public @column.NetType @column.PropertyName { get; set; }
+    }else {
+    @:public @(@column.NetType)? @column.PropertyName { get; set; }
+    }
+    @:
 
     if(@column.NetType?.TrimEnd('?') == "DateTime" && @column.QueryType == "~"){
-        @:/// <summary>
-        @: /// @(@column.ColumnComment)范围
-        @: /// </summary>
-        @: public DateTime?[] @(@column.PropertyName)Range { get; set; } 
+    @:/// <summary>
+    @: /// @(@column.ColumnComment)范围
+    @: /// </summary>
+    @: public DateTime?[] @(@column.PropertyName)Range { get; set; } 
 
 }
 
@@ -61,61 +61,90 @@ if (@column.ColumnKey != "True"){
 }
     }
 
-    /// <summary>
-    /// @(@Model.BusName)增加输入参数
-    /// </summary>
-    public class Add@(@Model.ClassName)Input : @(@Model.ClassName)BaseInput
-    {
+/// <summary>
+/// @(@Model.BusName)增加输入参数
+/// </summary>
+public class Add@(@Model.ClassName)Input : @(@Model.ClassName)BaseInput
+{
 @foreach (var column in Model.TableField){
 if (@column.WhetherRequired == "Y"){
-        @:/// <summary>
-        @:/// @column.ColumnComment
-        @:/// </summary>
-        @:[Required(ErrorMessage = "@(@column.ColumnComment)不能为空")]
-        @:public override @column.NetType @column.PropertyName { get; set; }
-        @:
+    @:/// <summary>
+    @:/// @column.ColumnComment
+    @:/// </summary>
+    @:[Required(ErrorMessage = "@(@column.ColumnComment)不能为空")]
+    @:public override @column.NetType @column.PropertyName { get; set; }
+    @:
+}
 }
 }
-    }
 
-    /// <summary>
-    /// @(@Model.BusName)删除输入参数
-    /// </summary>
-    public class Delete@(@Model.ClassName)Input : BaseIdInput
-    {
+/// <summary>
+/// @(@Model.BusName)删除输入参数
+/// </summary>
+public class Delete@(@Model.ClassName)Input : BaseIdInput
+{
 @foreach (var column in Model.TableField){
 if (@column.ColumnKey == "True" && @column.PropertyName != "Id"){
-        @:/// <summary>
-        @:/// @column.ColumnComment
-        @:/// </summary>
-        @:[Required(ErrorMessage = "@(@column.ColumnComment)不能为空")]
-        @:public @column.NetType @column.PropertyName { get; set; }
-        @:
+    @:/// <summary>
+    @:/// @column.ColumnComment
+    @:/// </summary>
+    @:[Required(ErrorMessage = "@(@column.ColumnComment)不能为空")]
+    @:public @column.NetType @column.PropertyName { get; set; }
+    @:
+}
 }
 }
-    }
 
-    /// <summary>
-    /// @(@Model.BusName)更新输入参数
-    /// </summary>
-    public class Update@(@Model.ClassName)Input : @(@Model.ClassName)BaseInput
-    {
+/// <summary>
+/// @(@Model.BusName)更新输入参数
+/// </summary>
+public class Update@(@Model.ClassName)Input : @(@Model.ClassName)BaseInput
+{
 @foreach (var column in Model.TableField){
 if (@column.ColumnKey == "True"){
-        @:/// <summary>
-        @:/// @column.ColumnComment
-        @:/// </summary>
-        @:[Required(ErrorMessage = "@(@column.ColumnComment)不能为空")]
-        @:public @column.NetType @column.PropertyName { get; set; }
-        @:
+    @:/// <summary>
+    @:/// @column.ColumnComment
+    @:/// </summary>
+    @:[Required(ErrorMessage = "@(@column.ColumnComment)不能为空")]
+    @:public @column.NetType @column.PropertyName { get; set; }
+    @:
+}
 }
 }
-    }
 
-    /// <summary>
-    /// @(@Model.BusName)主键查询输入参数
-    /// </summary>
-    public class QueryById@(@Model.ClassName)Input : Delete@(@Model.ClassName)Input
-    {
+/// <summary>
+/// @(@Model.BusName)主键查询输入参数
+/// </summary>
+public class QueryById@(@Model.ClassName)Input : Delete@(@Model.ClassName)Input
+{
 
+}
+
+@if (Model.TableField.Any(x => x.WhetherImport == "Y")){
+@:/// <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")){
+    if (@column.WhetherAddUpdate == "Y") {
+    @:/// <summary>
+    @:/// @column.ColumnComment
+    @:/// </summary>
+    if (@column.WhetherRequired == "Y" || @column.NetType.TrimEnd('?').EndsWith("Enum")){
+    @:[ImporterHeader(Name = "*@(@column.ColumnComment)")]
+    @:[ExporterHeader("*@(@column.ColumnComment)", Format = "@", Width = 25, IsBold = true)]
+    }else{
+    @:[ImporterHeader(Name = "@(@column.ColumnComment)")]
+    @:[ExporterHeader("@(@column.ColumnComment)", Format = "@", Width = 25, IsBold = true)]
+    }
+    if (Regex.IsMatch(@column.NetType, "(.*?Enum|int|long|double|float|bool)[?]?")){
+    @:public @column.NetType.TrimEnd('?')? @column.PropertyName { get; set; }
+    }else{
+    @:public @column.NetType @column.PropertyName { get; set; }
     }
+    @:
+    }}
+@:}
+}

+ 27 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Manage.js.vm

@@ -6,6 +6,10 @@ enum Api {
   Update@(@Model.ClassName) = '/api/@(@Model.LowerClassName)/update',
   Page@(@Model.ClassName) = '/api/@(@Model.LowerClassName)/page',
   Detail@(@Model.ClassName) = '/api/@(@Model.LowerClassName)/detail',
+@if (Model.TableField.Any(x => x.WhetherImport == "Y")) {
+  @:Import@(@Model.ClassName)Data = '/api/@(@Model.LowerClassName)/import',
+  @:Download@(@Model.ClassName)Template = '/api/@(@Model.LowerClassName)/import',
+}
   @foreach (var column in Model.TableField){
 if(@column.EffectType == "fk" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){
   @:Get@(@column.FkEntityName)@(@column.PropertyName)Dropdown = '/api/@(@Model.LowerClassName)/@(@column.FkEntityName)@(@column.PropertyName)Dropdown',
@@ -58,6 +62,29 @@ export const detail@(@Model.ClassName) = (id: any) =>
 			data: { id },
 		});
 
+@if (Model.TableField.Any(x => x.WhetherImport == "Y")) {
+@:// 下载@(@Model.BusName)数据导入模板
+@:export const download@(@Model.ClassName)Template = () =>
+	@:request({
+		@:url: Api.Download@(@Model.ClassName)Template,
+		@:responseType: 'arraybuffer',
+		@:method: 'get',
+	@:});
+@:
+@:// 导入@(@Model.BusName)记录
+@:export const import@(@Model.ClassName)Data = (file: any) => {
+	@:const formData = new FormData();
+	@:formData.append('file', file);
+	@:return request({
+		@:headers: { 'Content-Type': 'multipart/form-data' },
+		@:url: Api.Import@(@Model.ClassName)Data,
+		@:responseType: 'arraybuffer',
+		@:method: 'post',
+		@:data: formData,
+	@:});
+@:}
+}
+
 @foreach (var column in Model.TableField){
 	if(@column.EffectType == "fk" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){
 @:export const get@(@column.FkEntityName)@(@column.PropertyName)Dropdown = () =>

+ 14 - 3
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Output.cs.vm

@@ -39,11 +39,10 @@ if(column.EffectType == "fk")
 }
     @:
 }
-    }
- 
-
+}
 @foreach (var column in Model.TableField){
 if (@column.EffectType == "ApiTreeSelect"){
+@:
     @:// 使用实际实体@(@column.FkTableName),所以这里就删了
     @:/*
     @:[SugarTable("@(@column.FkTableName)")]
@@ -62,3 +61,15 @@ if (@column.EffectType == "ApiTreeSelect"){
     @:*/
 }
 }
+@if (Model.TableField.Any(x => x.WhetherImport == "Y")) {
+@:
+@:/// <summary>
+@:/// @(@Model.BusName)数据导入模板实体
+@:/// </summary>
+@:public class Export@(@Model.ClassName)Output : Import@(@Model.ClassName)Input
+@:{
+@:    [ImporterHeader(IsIgnore = true)]
+@:    [ExporterHeader(IsIgnore = true)]
+@:    public virtual string Error { get; set; }
+@:}
+}

+ 102 - 8
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Service.cs.vm

@@ -15,6 +15,9 @@ using Microsoft.AspNetCore.Http;
             haveLikeCdt = true;
         }
     }
+    var dictTableField = Model.TableField.Where(x => x.WhetherImport == "Y" && x.EffectType == "Select") ?? default;
+    var hasdictService = dictTableField.Count() > 0;
+    var importField = Model.TableField.Where(x => x.WhetherImport == "Y");
 }
 namespace @Model.NameSpace;
 
@@ -25,9 +28,18 @@ namespace @Model.NameSpace;
 public class @(@Model.ClassName)Service : IDynamicApiController, ITransient
 {
     private readonly SqlSugarRepository<@(@Model.ClassName)> _@(@Model.LowerClassName)Rep;
-    public @(@Model.ClassName)Service(SqlSugarRepository<@(@Model.ClassName)> @(@Model.LowerClassName)Rep)
-    {
+@if (hasdictService) {
+    @:private readonly SysDictTypeService _sysDictTypeService;
+}
+    public @(@Model.ClassName)Service(SqlSugarRepository<@(@Model.ClassName)> @(@Model.LowerClassName)Rep
+@if (hasdictService) {
+        @:,SysDictTypeService sysDictTypeService
+}
+    ){
         _@(@Model.LowerClassName)Rep = @(@Model.LowerClassName)Rep;
+@if (hasdictService) {
+        @:_sysDictTypeService = sysDictTypeService;
+}
     }
 
     /// <summary>
@@ -189,9 +201,9 @@ if (@column.ColumnKey == "True"){
     {
         return await _@(@Model.LowerClassName)Rep.AsQueryable().Select<@(@Model.ClassName)Output>().ToListAsync();
     }
-
 @foreach (var column in Model.TableField){
-if(@column.EffectType == "fk" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){  
+if(@column.EffectType == "fk" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){
+@:
     @:/// <summary>
     @:/// 获取@(@column.ColumnComment)列表
     @:/// </summary>
@@ -210,9 +222,9 @@ if(@column.EffectType == "fk" && (@column.WhetherAddUpdate == "Y" || column.Quer
     @:}
 }
 }
-
 @foreach (var column in Model.TableField){
-if(@column.EffectType == "Upload"){  
+if(@column.EffectType == "Upload"){
+@:
     @:/// <summary>
     @:/// 上传@(@column.ColumnComment)
     @:/// </summary>
@@ -227,10 +239,9 @@ if(@column.EffectType == "Upload"){
     @:} 
 }
 }
-
-
 @foreach (var column in Model.TableField){
 if(@column.EffectType == "ApiTreeSelect" && !definedObjects.ContainsKey("@(@column.FkEntityName)Tree")){
+@:
     @{definedObjects.Add("@(@column.FkEntityName)Tree", 1);}
     @:[HttpGet("@(@column.FkEntityName)Tree")]
     @:[DisplayName("获取@(@column.FkEntityName)Tree")]
@@ -240,5 +251,88 @@ if(@column.EffectType == "ApiTreeSelect" && !definedObjects.ContainsKey("@(@colu
     @:}
 }
 }
+@if (hasdictService) {
+@:
+    @:/// <summary>
+    @:/// 获取字典文本列表
+    @:/// </summary>
+    @:/// <param name="dictTypeCode"></param>
+    @:/// <returns></returns>
+    @:private List<string> GetDictDataTextList(string dictTypeCode)
+    @:{
+    @:    return _sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = dictTypeCode }).Result?.Select(x => x.Value).ToList();
+    @:}
+}
+@if (importField?.Count() > 0) {
+@:
+    @:/// <summary>
+    @:/// 下载@(@Model.BusName)数据导入模板
+    @:/// </summary>
+    @:/// <returns></returns>
+    @:[DisplayName("下载@(@Model.BusName)数据导入模板")]
+    @:[ApiDescriptionSettings(Name = "Import"), HttpGet, NonUnify]
+    @:public IActionResult DownloadTemplate()
+    @:{
+        @:return ExcelHelper.ExportTemplate(new List<Export@(@Model.ClassName)Output>(), "@(@Model.BusName)导入模板");
+    @:}
+@:
+    @:/// <summary>
+    @:/// 导入@(@Model.BusName)记录 📃
+    @:/// </summary>
+    @:/// <returns></returns>
+    @:[DisplayName("导入@(@Model.BusName)记录")]
+    @:[ApiDescriptionSettings(Name = "Import"), HttpPost, NonUnify, UnitOfWork]
+    @:public IActionResult ImportData([Required] IFormFile file)
+    @:{
+        @:lock (this)
+        @:{
+            foreach (var column in dictTableField){
+            @:var @(@column.LowerPropertyName)DictMap = _sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = "@(@column.DictTypeCode)" }).Result.ToDictionary(x => x.Value, x => x.Code);
+            }
+            @:var stream = ExcelHelper.ImportData<Import@(@Model.ClassName)Input, @(@Model.ClassName)>(file, (list, markerErrorAction) =>
+            @:{
+                @:_@(@Model.LowerClassName)Rep.Context.Utilities.PageEach(list, 2048, pageItems =>
+                @:{
+                    @:// 校验并过滤必填基本类型为null的字段
+                    @:var rows = pageItems.Where(x => {
+                        foreach (var column in importField.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;
+                        @:}
+                        }
+                        @:return true;
+                    @:}).Adapt<List<@(@Model.ClassName)>>();
+                    if (hasdictService){
+                    @:// 映射字典值
+                    @:foreach(var row in rows){
+                        foreach (var column in dictTableField){
+                        @:row.@(@column.PropertyName) = @(@column.LowerPropertyName)DictMap.GetValueOrDefault(row.@(@column.PropertyName) ?? "");
+                        }
+                    @:}
+                    }
+                    @:var storageable = _@(@Model.LowerClassName)Rep.Context.Storageable(rows)
+                        foreach (var column in importField){
+                        if (@column.WhetherRequired == "Y"){
+                        if(@column.NetType.TrimEnd('?') == "string"){
+                        @:.SplitError(it => string.IsNullOrWhiteSpace(it.Item.@(@column.PropertyName)), "@(@column.ColumnComment)不能为空")
+                        } else if(@column.NetType.EndsWith('?') == true){
+                        @:.SplitError(it => it.Item.@(@column.PropertyName) == null, "@(@column.ColumnComment)不能为空")
+                        }}
+                        if (@column.NetType?.TrimEnd('?') == "string"){
+                        @:.SplitError(it => it.Item.@(@column.PropertyName)?.Length > @column.ColumnLength, "@(@column.ColumnComment)长度不能超过@(@column.ColumnLength)个字符")
+                        }}
+                        @:.ToStorage();
 
+                    @:storageable.BulkCopy();
+                    @:storageable.BulkUpdate();
+                    
+                    @:markerErrorAction.Invoke(storageable, pageItems, rows);
+                @:});
+            @:});
+@:
+            @:return stream;
+        @:}
+    @:}
 }
+@}

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

@@ -12,13 +12,14 @@
           haveLikeCdt = true;
       }
   }
+  bool hasImport = Model.TableField.Any(x => x.WhetherImport == "Y");
 }
 <template>
   <div class="@(@Model.LowerClassName)-container">
     <el-card shadow="hover" :body-style="{ paddingBottom: '0' }"> 
       @<el-form :model="queryParams" ref="queryForm" labelWidth="90">
         @<el-row>
-              @if(Model.QueryWhetherList.Count > 0){
+          @if(Model.QueryWhetherList.Count > 0){
           @if(haveLikeCdt){
           @:<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10">
             @:<el-form-item label="关键字">
@@ -95,6 +96,9 @@
                     }
                 @<el-button type="primary" style="margin-left:5px;" icon="ele-Plus" @@click="openAdd@(@Model.ClassName)" v-auth="'@(@Model.LowerClassName):add'"> 新增 </el-button>
                 @
+                @if (hasImport) {
+                @:<el-button type="warning" icon="ele-MostlyCloudy" @@click="importDataRef.openDialog" v-auth="'@(@Model.LowerClassName):import'"> 导入 </el-button>
+                }
               </el-button-group>
             </el-form-item>
             @
@@ -158,14 +162,14 @@
         else if(@column.EffectType == "Select"){
           @:<el-table-column prop="@column.LowerPropertyName" label="@column.ColumnComment" @(column.WhetherSortable == "Y" ? "sortable='custom'" : "") show-overflow-tooltip="" >
             @:<template #default="scope">
-              @:<el-tag :type="di('@(@column.DictTypeCode)', scope.row.@(@column.LowerPropertyName))?.tagType"> {{di("@(@column.DictTypeCode)", scope.row.@(@column.LowerPropertyName))?.value}} </el-tag>
+              @:<dict-label :value="scope.row.@column.LowerPropertyName" code="@column.DictTypeCode" prop-label="value" prop-value="code" />
             @:</template>
           @:</el-table-column>
         }
         else if(@column.EffectType == "EnumSelector"){
           @:<el-table-column prop="@column.LowerPropertyName" label="@column.ColumnComment" @(column.WhetherSortable == "Y" ? "sortable='custom'" : "") show-overflow-tooltip="" >
             @:<template #default="scope">
-              @:<el-tag :type="dv('@(@column.DictTypeCode)', scope.row.@(@column.LowerPropertyName))?.tagType"> {{dv('@(@column.DictTypeCode)', scope.row.@column.LowerPropertyName)?.name}}</el-tag>
+              @:<dict-label :value="scope.row.@column.LowerPropertyName" code="@column.DictTypeCode"/>
             @:</template>
           @:</el-table-column>
         }
@@ -209,6 +213,9 @@
       />
     </el-card>
   </div>
+  @if (hasImport) {
+  @:<ImportData ref="importDataRef" :import="import@(@Model.ClassName)Data" :download="download@(@Model.ClassName)Template" v-auth="'@(@Model.LowerClassName):import'" @@refresh="handleQuery" />
+  }
 </template>
 
 <script lang="ts" setup="" name="@(@Model.LowerClassName)">
@@ -221,10 +228,7 @@
   @:import { codeToName, getConstType } from "/@@/utils/constHelper";
   } 
   @if(@Model.TableField.Any(x=>x.EffectType == "Select") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
-  @:import { getDictDataItem as di, getDictDataList as dl } from '/@@/utils/dict-utils';
-  }
-  @if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
-  @:import { getDictLabelByVal as dv } from '/@@/utils/dict-utils';
+  @:import { getDictDataList as dl } from '/@@/utils/dict-utils';
   }
   @if(@Model.TableField.Any(x=>x.EffectType == "DatePicker")){
   @:import { formatDate } from '/@@/utils/formatTime';
@@ -237,18 +241,21 @@
   @:import { SysPrint } from '/@@/api-services/models';
   }
 
+  @if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector" || x.EffectType == "Select")){
+  @:import { DictLabel } from "/@@/components/table/dictLabel.vue";
+  }
   import printDialog from '/@@/views/system/print/component/hiprint/preview.vue'
   import editDialog from '/@@/views/@(@Model.PagePath)/@(@Model.LowerClassName)/component/editDialog.vue'
   import { page@(@Model.ClassName), delete@(@Model.ClassName) } from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)';
+  @if (hasImport) {
+  @:import { ImportData } from "/@@/components/table/importData.vue";
+  @:import { download@(@Model.ClassName)Template, import@(@Model.ClassName)Data } from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)';
+  }
   @foreach (var column in Model.QueryWhetherList){
   if(@column.EffectType == "fk"){
   @:import { get@(@column.FkEntityName)@(@column.PropertyName)Dropdown } from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)';
   }
   }
-  @if(@Model.QueryWhetherList.Any(x=>x.EffectType == "EnumSelector")){
-	@:import { SysEnumApi } from '/@@/api-services/api';
-  @:import commonFunction from '/@@/utils/commonFunction';
-	}
 
   @if(haveLikeCdt){
   @:const showAdvanceQueryUI = ref(false);
@@ -257,6 +264,9 @@
   }
   const printDialogRef = ref();
   const editDialogRef = ref();
+  @if (hasImport) {
+  @:const importDataRef = ref();
+  }
   const loading = ref(false);
   const tableData = ref<any>([]);
   const queryParams = ref<any>({});
@@ -270,7 +280,7 @@
   });
 
   const print@(@Model.ClassName)Title = ref("");
-  const edit@(@Model.ClassName)Title = ref("");
+  const edit@(@Model.ClassName)Title = ref("")
 
   // 改变高级查询的控件显示状态
   const changeAdvanceQueryUI = () => {

+ 101 - 0
Web/src/components/table/importData.vue

@@ -0,0 +1,101 @@
+<template v-loading="state.loading">
+	<div class="sys-import-data-container" v-loading="state.loading">
+		<el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" width="300px" v-loading="state.loading">
+			<template #header>
+				<div style="color: #fff">
+					<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-UploadFilled /> </el-icon>
+					<span> 数据导入 </span>
+				</div>
+			</template>
+			
+			<el-row :gutter="15">
+				<el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
+					<el-button class="ml10" type="info" icon="ele-Download" @click="() => download()">模板</el-button>
+				</el-col>
+				<el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
+					<el-upload
+						:limit="1"
+						:show-file-list="false"
+						:on-exceed="handleExceed"
+						:http-request="handleImportData"
+						ref="uploadRef"
+					>
+						<template #trigger>
+							<el-button type="primary" icon="ele-MostlyCloudy">导入</el-button>
+						</template>
+					</el-upload>
+				</el-col>
+			</el-row>
+
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="() => state.isShowDialog = false">取 消</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup name="sysImportData">
+import type {UploadInstance, UploadProps, UploadRawFile, UploadRequestOptions} from 'element-plus'
+import { ElUpload, ElMessage, genFileId } from 'element-plus';
+import { downloadFile } from '/@/utils/downloadFile';
+import { reactive, ref } from 'vue';
+
+const uploadRef = ref<UploadInstance>();
+const state = reactive({
+	isShowDialog: false,
+	loading: false,
+});
+
+// 定义子组件向父组件传值/事件
+const props = defineProps(['import', 'download']);
+const emit = defineEmits(['refresh']);
+
+// 打开弹窗
+const openDialog = () => {
+	state.isShowDialog = true;
+};
+
+// 选择文件超出上限事件
+const handleExceed: UploadProps['onExceed'] = (files) => {
+  uploadRef.value!.clearFiles();
+  const file = files[0] as UploadRawFile;
+  file.uid = genFileId();
+  uploadRef.value!.handleStart(file);
+}
+
+// 数据导入
+const handleImportData = (opt: UploadRequestOptions) => {
+  state.loading = true;
+  props.import(opt.file).then((res: any) => {
+    try {
+      handleFileStream(res);
+	  state.isShowDialog = false;
+      emit('refresh');
+    } catch (err) {
+      ElMessage.error(res.data.message || '上传失败');
+    }
+  }).finally(() => {
+    state.loading = false;
+    uploadRef.value?.clearFiles();
+  });
+}
+
+// 下载模板
+const download = () => {
+	props.download().then((res: any) => handleFileStream(res)).catch((res: any) => ElMessage.error('下载错误: ' + res));
+}
+
+// 下载文件流
+const handleFileStream = (res: any) => {
+  const contentType = res.headers['content-type'];
+  const contentDisposition = res.headers['content-disposition'];
+  const filename = decodeURIComponent(contentDisposition.split('; ')[1].split('=')[1])
+  const blob = res.data instanceof Blob ? res.data : new Blob([res.data], { type: contentType });
+  downloadFile(window.URL.createObjectURL(blob), filename);
+}
+
+// 导出对象
+defineExpose({ openDialog });
+</script>

+ 19 - 0
Web/src/utils/downloadFile.ts

@@ -0,0 +1,19 @@
+/**
+ * 下载文件
+ * @param url 下载链接
+ * @param filename 文件名
+ */
+export function downloadFile(url: string, filename: string | undefined = undefined) {
+    const urlSplit = url.split('/');
+    filename = filename || urlSplit[urlSplit.length - 1];
+
+    const link = document.createElement('a');
+    link.setAttribute('download', filename);
+    link.href = url;
+
+    document.body.appendChild(link);
+    link.click();
+
+    document.body.removeChild(link);
+    window.URL.revokeObjectURL(url);
+}

+ 16 - 4
Web/src/views/system/codeGen/component/genConfigDialog.vue

@@ -36,7 +36,14 @@
 				<el-table-column prop="dictTypeCode" label="字典" width="180" show-overflow-tooltip>
 					<template #default="scope">
 						<el-select v-model="scope.row.dictTypeCode" class="m-2" :disabled="effectTypeEnable(scope.row)">
-							<el-option v-for="item in state.dictTypeCodeList" :key="item.code" :label="item.name" :value="item.code" />
+							<el-option
+							v-for="item in scope.row.effectType == 'Select' ? state.dictDataAll :
+								scope.row.effectType == 'EnumSelector' ? state.allEnumSelector :
+								scope.row.effectType == 'ConstSelector' ? allConstSelector :
+								state.dictTypeCodeList" 
+							:key="item.code"
+							:label="item.name" 
+							:value="item.code" />
 						</el-select>
 					</template>
 				</el-table-column>
@@ -51,6 +58,11 @@
 						<el-checkbox v-model="scope.row.whetherAddUpdate" :disabled="judgeColumns(scope.row)" />
 					</template>
 				</el-table-column>
+				<el-table-column prop="whetherImport" label="导入" width="80" align="center" show-overflow-tooltip>
+					<template #default="scope">
+						<el-checkbox v-model="scope.row.whetherImport" :disabled="judgeColumns(scope.row)" />
+					</template>
+				</el-table-column>
 				<el-table-column prop="whetherRequired" label="必填" width="80" align="center" show-overflow-tooltip>
 					<template #default="scope">
 						<el-checkbox v-model="scope.row.whetherRequired" :disabled="judgeColumns(scope.row)" />
@@ -125,8 +137,8 @@ onMounted(async () => {
 	state.effectTypeList = res.data.result;
 
 	var res1 = await getAPI(SysDictTypeApi).apiSysDictTypeListGet();
-	state.dictTypeCodeList = res1.data.result;
-	state.dictDataAll = res1.data.result;
+	state.dictTypeCodeList = res1.data.result.filter(x => !x.code.endsWith("Enum"));
+	state.dictDataAll = state.dictTypeCodeList;
 
 	var res2 = await getAPI(SysDictDataApi).apiSysDictDataDataListCodeGet('code_gen_query_type');
 	state.queryTypeList = res2.data.result;
@@ -175,7 +187,7 @@ const handleQuery = async (row: any) => {
 	state.loading = true;
 	var res = await getAPI(SysCodeGenConfigApi).apiSysCodeGenConfigListGet(undefined, row.id);
 	var data = res.data.result ?? [];
-	let lstWhetherColumn = ['whetherTable', 'whetherAddUpdate', 'whetherRequired', 'whetherSortable']; //列表显示的checkbox
+	let lstWhetherColumn = ['whetherTable', 'whetherAddUpdate', 'whetherImport', 'whetherRequired', 'whetherSortable']; //列表显示的checkbox
 	data.forEach((item: any) => {
 		for (const key in item) {
 			if (item[key] === 'Y') {