ソースを参照

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

zuohuaijun 1 年間 前
コミット
ac3135edcd

+ 1 - 1
Admin.NET/Admin.NET.Core/Attribute/DictAttribute.cs

@@ -44,7 +44,7 @@ public class DictAttribute : ValidationAttribute, ITransient
         var dictDataList = sysDictDataServiceProvider.GetDataList(DictTypeCode).Result;
 
         // 使用HashSet来提高查找效率
-        var dictCodes = new HashSet<string>(dictDataList.Select(u => u.Code));
+        var dictCodes = new HashSet<string>(dictDataList.Select(u => u.Name));
 
         if (!dictCodes.Contains(valueAsString))
             return new ValidationResult($"提示:{ErrorMessage}|字典【{DictTypeCode}】不包含【{valueAsString}】!");

+ 15 - 4
Admin.NET/Admin.NET.Core/Service/CodeGen/Dto/CodeGenConfig.cs

@@ -178,6 +178,16 @@ public class CodeGenConfig
     /// </summary>
     public int OrderNo { get; set; }
 
+    /// <summary>
+    /// 是否是选择器控件
+    /// </summary>
+    public bool IsSelectorEffectType => Regex.IsMatch(EffectType ?? "", "Selector$|ForeignKey", RegexOptions.IgnoreCase);
+    
+    /// <summary>
+    /// 去掉尾部Id的属性名
+    /// </summary>
+    public string PropertyNameTrimEndId => PropertyName.TrimEnd("Id");
+
     /// <summary>
     /// 去掉尾部Id的属性名
     /// </summary>
@@ -188,9 +198,10 @@ public class CodeGenConfig
     /// </summary>
     public string ExtendedPropertyName => EffectType switch
     {
-        "ForeignKey" => $"{LowerPropertyNameTrimEndId}FkDisplayName",
-        "ApiTreeSelector" => $"{LowerPropertyNameTrimEndId}DisplayName",
-        "Upload" => $"{LowerPropertyName}Attachment",
+        "ForeignKey" => $"{PropertyName.TrimEnd("Id")}FkDisplayName",
+        "ApiTreeSelector" => $"{PropertyName.TrimEnd("Id")}DisplayName",
+        "DictSelector" => $"{PropertyName.TrimEnd("Id")}DictLabel",
+        "Upload" => $"{PropertyName.TrimEnd("Id")}Attachment",
         _ => PropertyName
     };
 
@@ -213,5 +224,5 @@ public class CodeGenConfig
     /// <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}}}")) + "\"";
+    public string GetDisplayColumn(string tableAlias, string separator = "-") => "$\"" + string.Join(separator, FkDisplayColumnList?.Select(name => $"{{{tableAlias}.{name}}}") ?? new List<string>()) + "\"";
 }

+ 2 - 1
Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenService.cs

@@ -401,9 +401,10 @@ public class SysCodeGenService : IDynamicApiController, ITransient
             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(),
+            ApiTreeFieldList = tableFieldList.Where(u => u.EffectType == "ApiTreeSelector").ToList(),
+            DropdownFieldList = tableFieldList.Where(u => u.EffectType is "ForeignKey" or "ApiTreeSelector").ToList(),
             
             HasJoinTable = joinTableList.Count > 0,
             HasDictField = tableFieldList.Any(u => u.EffectType == "DictSelector"),

+ 74 - 48
Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs

@@ -4,8 +4,6 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
 using Npgsql;
 
 namespace Admin.NET.Core.Service;
@@ -303,7 +301,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
             NameSpace = $"{input.Position}.Entity",
             input.TableName,
             input.EntityName,
-            BaseClassName = string.IsNullOrWhiteSpace(input.BaseClassName) ? "" : $" : {input.BaseClassName}",
+            BaseClassName = string.IsNullOrWhiteSpace(input.BaseClassName) ? "" : $": {input.BaseClassName}",
             input.ConfigId,
             dbTableInfo.Description,
             TableField = dbColumnInfos
@@ -338,25 +336,19 @@ public class SysDatabaseService : IDynamicApiController, ITransient
 
         input.EntityName = entityType.Name;
         input.SeedDataName = entityType.Name + "SeedData";
-        if (!string.IsNullOrWhiteSpace(input.Suffix))
-            input.SeedDataName += input.Suffix;
-        var targetPath = GetSeedDataTargetPath(input);
+        if (!string.IsNullOrWhiteSpace(input.Suffix)) input.SeedDataName += input.Suffix;
 
         // 查询所有数据
         var query = db.QueryableByObject(entityType);
-        DbColumnInfo orderField = null; // 排序字段
         // 优先用创建时间排序
-        orderField = dbColumnInfos.Where(u => u.DbColumnName.ToLower() == "create_time" || u.DbColumnName.ToLower() == "createtime").FirstOrDefault();
-        if (orderField != null)
-            query.OrderBy(orderField.DbColumnName);
-        // 其次用Id排序
-        orderField = dbColumnInfos.Where(u => u.DbColumnName.ToLower() == "id").FirstOrDefault();
-        if (orderField != null)
-            query.OrderBy(orderField.DbColumnName);
-        IEnumerable recordsTmp = (IEnumerable)query.ToList();
-        List<dynamic> records = recordsTmp.ToDynamicList();
+        DbColumnInfo orderField = dbColumnInfos.FirstOrDefault(u => u.DbColumnName.ToLower() == "create_time" || u.DbColumnName.ToLower() == "createtime");
+        if (orderField != null) query = query.OrderBy(orderField.DbColumnName);
+        // 再使用第一个主键排序
+        query = query.OrderBy(dbColumnInfos.First(u => u.IsPrimarykey).DbColumnName);
+        var records = ((IEnumerable)await query.ToListAsync()).ToDynamicList();
+        
         // 过滤已存在的数据
-        if (input.FilterExistingData && records.Count() > 0)
+        if (input.FilterExistingData && records.Any())
         {
             // 获取实体类型-所有种数据数据类型
             var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.FullName.EndsWith("." + input.EntityName))
@@ -413,39 +405,71 @@ public class SysDatabaseService : IDynamicApiController, ITransient
                 }
             }
         }
-        var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
-        var recordsJSON = JsonConvert.SerializeObject(records, Formatting.Indented, timeConverter);
 
         // 检查有没有 System.Text.Json.Serialization.JsonIgnore 的属性
-        var jsonIgnoreProperties = entityType.GetProperties().Where(p => (p.GetAttribute<System.Text.Json.Serialization.JsonIgnoreAttribute>() != null ||
-            p.GetAttribute<JsonIgnoreAttribute>() != null) && p.GetAttribute<SugarColumn>() != null).ToList();
-        var jsonIgnoreInfo = new List<List<JsonIgnoredPropertyData>>();
-        if (jsonIgnoreProperties.Count > 0)
+        // var jsonIgnoreProperties = entityType.GetProperties().Where(p => (p.GetAttribute<System.Text.Json.Serialization.JsonIgnoreAttribute>() != null ||
+        //     p.GetAttribute<JsonIgnoreAttribute>() != null) && p.GetAttribute<SugarColumn>() != null).ToList();
+        // var jsonIgnoreInfo = new List<List<JsonIgnoredPropertyData>>();
+        // if (jsonIgnoreProperties.Count > 0)
+        // {
+        //     int recordIndex = 0;
+        //     foreach (var r in (IEnumerable)records)
+        //     {
+        //         List<JsonIgnoredPropertyData> record = new();
+        //         foreach (var item in jsonIgnoreProperties)
+        //         {
+        //             object v = item.GetValue(r);
+        //             string strValue = "null";
+        //             if (v != null)
+        //             {
+        //                 strValue = v.ToString();
+        //                 if (v.GetType() == typeof(string))
+        //                     strValue = "\"" + strValue + "\"";
+        //                 else if (v.GetType() == typeof(DateTime))
+        //                     strValue = "DateTime.Parse(\"" + ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") + "\")";
+        //             }
+        //             record.Add(new JsonIgnoredPropertyData { RecordIndex = recordIndex, Name = item.Name, Value = strValue });
+        //         }
+        //         recordIndex++;
+        //         jsonIgnoreInfo.Add(record);
+        //     }
+        // }
+        
+        // 获取所有字段信息
+        var propertyList = entityType.GetProperties().Where(x => false == (x.GetCustomAttribute<SugarColumn>()?.IsIgnore ?? false)).ToList();
+        for (var i = 0; i < propertyList.Count; i++)
+        {
+            if (propertyList[i].Name != nameof(EntityBaseId.Id) || !(propertyList[i].GetCustomAttribute<SugarColumn>()?.IsPrimaryKey ?? true)) continue;
+            var temp = propertyList[i];
+            for (var j = i; j > 0; j--) propertyList[j] = propertyList[j - 1];
+            propertyList[0] = temp;
+        }
+        // 拼接数据
+        var recordList = records.Select(obj => string.Join(", ", propertyList.Select(prop =>
         {
-            int recordIndex = 0;
-            foreach (var r in (IEnumerable)records)
+            var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
+            object value = prop.GetValue(obj);
+            if (value == null) value = "null";
+            else if (propType == typeof(string))
             {
-                List<JsonIgnoredPropertyData> record = new();
-                foreach (var item in jsonIgnoreProperties)
-                {
-                    object v = item.GetValue(r);
-                    string strValue = "null";
-                    if (v != null)
-                    {
-                        strValue = v.ToString();
-                        if (v.GetType() == typeof(string))
-                            strValue = "\"" + strValue + "\"";
-                        else if (v.GetType() == typeof(DateTime))
-                            strValue = "DateTime.Parse(\"" + ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") + "\")";
-                    }
-                    record.Add(new JsonIgnoredPropertyData { RecordIndex = recordIndex, Name = item.Name, Value = strValue });
-                }
-                recordIndex++;
-                jsonIgnoreInfo.Add(record);
+                value = $"\"{value}\"";
             }
-        }
+            else if (propType.IsEnum)
+            {
+                value = $"{propType.Name}.{value}";
+            }
+            else if (propType == typeof(bool))
+            {
+                value = (bool)value ? "true" : "false";
+            }
+            else if (propType == typeof(DateTime))
+            {
+                value = $"DateTime.Parse(\"{((DateTime)value):yyyy-MM-dd HH:mm:ss.fff}\")";
+            }
+            return $"{prop.Name}={value}";
+        }))).ToList();
 
-        var tContent = File.ReadAllText(templatePath);
+        var tContent = await File.ReadAllTextAsync(templatePath);
         var data = new
         {
             NameSpace = $"{input.Position}.SeedData",
@@ -455,17 +479,19 @@ public class SysDatabaseService : IDynamicApiController, ITransient
             input.SeedDataName,
             input.ConfigId,
             tableInfo.Description,
-            JsonIgnoreInfo = jsonIgnoreInfo,
-            RecordsJSON = recordsJSON
+            // JsonIgnoreInfo = jsonIgnoreInfo,
+            RecordList = recordList
         };
-        var tResult = _viewEngine.RunCompile(tContent, data, builderAction: builder =>
+        var tResult = await _viewEngine.RunCompileAsync(tContent, data, builderAction: builder =>
         {
             builder.AddAssemblyReferenceByName("System.Linq");
             builder.AddAssemblyReferenceByName("System.Collections");
             builder.AddUsing("System.Collections.Generic");
             builder.AddUsing("System.Linq");
         });
-        File.WriteAllText(targetPath, tResult, Encoding.UTF8);
+        
+        var targetPath = GetSeedDataTargetPath(input);
+        await File.WriteAllTextAsync(targetPath, tResult, Encoding.UTF8);
     }
 
     /// <summary>

+ 1 - 1
Admin.NET/Admin.NET.Core/Utils/ExcelHelper.cs

@@ -78,7 +78,7 @@ public class ExcelHelper
     /// <param name="filename"></param>
     /// <param name="addListValidationFun"></param>
     /// <returns></returns>
-    public static IActionResult ExportTemplate<T>(List<T> list, string filename = "导入模板", Func<ExcelWorksheet, PropertyInfo, IEnumerable<string>> addListValidationFun = null)
+    public static IActionResult ExportTemplate<T>(IEnumerable<T> list, string filename = "导入模板", Func<ExcelWorksheet, PropertyInfo, IEnumerable<string>> addListValidationFun = null)
     {
         using var package = new ExcelPackage((ExportData(list, filename) as XlsxFileResult)!.Stream);
         var worksheet = package.Workbook.Worksheets[0];

+ 13 - 58
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Entity.cs.vm

@@ -12,75 +12,30 @@ namespace @(Model.NameSpace);
 /// <summary>
 /// @(Model.Description)
 /// </summary>
-[SugarTable("@(Model.TableName)","@(Model.Description)")]
 [Tenant("@(Model.ConfigId)")]
+[SugarTable(null, "@(Model.Description)")]
 public class @(Model.EntityName) @Model.BaseClassName
 {
-@foreach (var column in Model.TableField){
-if(Model.BaseClassName=="" && column.IsPrimarykey){
-    @:/// <summary>
-    @:/// @column.ColumnDescription
-    @:/// </summary>
-    if(!column.IsNullable){
-    @:[Required]
-    }
-    if(column.DataType.TrimEnd('?') == "string"){
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true, Length = @column.Length)]
-    } else if (column.DataType.TrimEnd('?') == "decimal"){
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true, Length = @column.Length, DecimalDigits=@column.DecimalDigits )]
-    } else {
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true)]
-    }
-    @:public @column.DataType @column.PropertyName { get; set; }
-    @:
-} else if(Model.BaseClassName == "" && !column.IsPrimarykey){
-    @:/// <summary>
-    @:/// @column.ColumnDescription
-    @:/// </summary>
-    if(!column.IsNullable){
-    @:[Required]
+@foreach (var column in Model.TableField) {
+    var propSuffix = "";
+    if (column.IsPrimarykey && (Model.BaseClassName == "" || Model.BaseClassName != "" && column.DbColumnName.ToLower() != "id")) {
+        propSuffix = $", IsPrimaryKey = true, IsIdentity = {column.IsIdentity.ToString().ToLower()}";
     }
-    if(column.DataType.TrimEnd('?') == "string"){
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription", Length = @column.Length)]
-    } else if (column.DataType.TrimEnd('?') == "decimal"){
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription", Length = @column.Length, DecimalDigits=@column.DecimalDigits )]
-    } else {
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription")]
-    }
-    @:public @column.DataType @column.PropertyName { get; set; }
-    @:
-} else if(Model.BaseClassName != "" && column.IsPrimarykey && column.DbColumnName.ToLower() != "id"){
-    @:/// <summary>
-    @:/// @column.ColumnDescription
-    @:/// </summary>
-    if(!column.IsNullable){
-    @:[Required]
-    }
-    if(column.DataType.TrimEnd('?') == "string"){
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true, Length = @column.Length)]
-    } else if (column.DataType.TrimEnd('?') == "decimal"){
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true, Length = @column.Length, DecimalDigits=@column.DecimalDigits )]
-    } else {
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true)]
+
+    if (column.DataType.TrimEnd('?') == "string") {
+        propSuffix += $", Length = {column.Length}";
+    } else if (column.DataType.TrimEnd('?') == "decimal") {
+        propSuffix += $", Length = {column.Length}, DecimalDigits={column.DecimalDigits}";
     }
-    @:public @column.DataType @column.PropertyName { get; set; }
-    @:
-} else if(Model.BaseClassName != "" && !column.IsPrimarykey && column.DbColumnName.ToLower() != "id"){
+    
     @:/// <summary>
     @:/// @column.ColumnDescription
     @:/// </summary>
     if(!column.IsNullable){
     @:[Required]
     }
-    if(column.DataType.TrimEnd('?') == "string"){
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription", Length = @column.Length)]
-    } else if (column.DataType.TrimEnd('?') == "decimal"){
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription", Length = @column.Length, DecimalDigits=@column.DecimalDigits )]
-    } else {
-    @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription")]
-    }
-    @:public @column.DataType @column.PropertyName { get; set; }
+    @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription"@propSuffix)]
+    @:public virtual @column.DataType @column.PropertyName { get; set; }
     @:
 }
 }
-}

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

@@ -145,7 +145,7 @@ public class QueryById@(Model.ClassName)Input : Delete@(Model.ClassName)Input
 @:{
     foreach (var column in Model.ImportFieldList){
     var headerName = (column.WhetherRequired == "Y" ? "*" : "") + column.ColumnComment;
-    if(column.EffectType == "ForeignKey" || column.EffectType == "ApiTreeSelector") {
+    if(column.EffectType == "ForeignKey" || column.EffectType == "ApiTreeSelector" || column.EffectType == "DictSelector") {
     @:/// <summary>
     @:/// @column.ColumnComment 关联值
     @:/// </summary>

+ 25 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Output.cs.vm

@@ -32,6 +32,31 @@ public class @(Model.ClassName)Output
     @:
     }
 }
+@if (Model.ApiTreeFieldList.Count > 0) {
+foreach(var column in Model.ApiTreeFieldList) {
+@:/// <summary>
+@:/// @column.ColumnComment 树选择器输出参数
+@:/// </summary>
+@:public class Tree@(column.PropertyNameTrimEndId)Output : @(Model.ClassName)
+@:{
+    @:/// <summary>
+    @:/// 节点值
+    @:/// </summary>
+    @:public @column.NetType Value { get; set; }
+    @:
+    @:/// <summary>
+    @:/// 节点文本
+    @:/// </summary>
+    @:public string Label { get; set; }
+    @:
+    @:/// <summary>
+    @:/// 子集数据
+    @:/// </summary>
+    @:public List<Tree@(column.PropertyNameTrimEndId)Output> Children { get; set; }
+@:}
+@:
+}
+}
 @if (Model.ImportFieldList.Count > 0) {
 @:
 @:/// <summary>

+ 4 - 20
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/SeedData.cs.vm

@@ -20,26 +20,10 @@ public class @(Model.SeedDataName) : ISqlSugarEntitySeedData<@(Model.EntityName)
     /// <returns></returns>
     public IEnumerable<@(Model.EntityName)> HasData()
     {
-        string recordsJSON = @@"
-            @(Model.RecordsJSON.Replace("\"","\"\"").Replace("\n", "\n\t\t\t"))
-        ";
-        List<@(Model.EntityName)> records = Newtonsoft.Json.JsonConvert.DeserializeObject<List<@(Model.EntityName)>>(recordsJSON);
-        @if (Model.JsonIgnoreInfo.Count>0) {
-        @:
-        @:#region 处理 JsonIgnore 的Property
-        @:
-            @foreach (var jii in Model.JsonIgnoreInfo){
-                @foreach (var j in jii){
-        @:records[@j.RecordIndex].@(j.Name) = @(j.Value);
-                }
-                @:
+        return new List<@(Model.EntityName)> {
+            @foreach (var record in Model.RecordList) {
+            @:new() { @record },
             }
-        @:#endregion
-        }
-        
-        // 后处理数据的特殊字段
-		//for (int i = 0; i < records.Count; i++) { }
-
-        return records;
+        };
     }
 }

+ 30 - 29
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Service.cs.vm

@@ -82,18 +82,6 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
 		return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
     }
 
-    /// <summary>
-    /// 获取@(Model.BusName)列表 🔖
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [DisplayName("获取@(Model.BusName)列表")]
-    [ApiDescriptionSettings(Name = "List"), HttpGet]
-    public async Task<List<@(Model.ClassName)Output>> List([FromQuery] Page@(Model.ClassName)Input input)
-    {
-        return await _@(Model.LowerClassName)Rep.AsQueryable().Select<@(Model.ClassName)Output>().ToListAsync();
-    }
-
     /// <summary>
     /// 获取@(Model.BusName)详情 ℹ️
     /// </summary>
@@ -217,16 +205,16 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
             if (column.EffectType != "ApiTreeSelector") {
             @:.InnerJoinIF<@Model.ClassName>(input.FromPage, (u, r) => u.@(column.FkLinkColumnName) == r.@(column.PropertyName))
             }
+            if (column.EffectType != "ApiTreeSelector") {
             @:.Select(u => new {
                 @:Value = u.@(column.FkLinkColumnName),
                 @:Label = @column.GetDisplayColumn("u")
-            if (column.EffectType != "ApiTreeSelector") {
             @:}).ToListAsync();
             } else {
-                @:, Id = u.Id,
-                @:ParentId = u.@(column.PidColumn),
-                @:Children = new List<dynamic>()
-            @:}).ToTreeAsync(u => u.Children, u => u.ParentId, @(column.WhetherRequired == "Y" ? "0" : "null"));
+            @:.Select(u => new Tree@(column.PropertyNameTrimEndId)Output {
+                @:Value = u.@(column.FkLinkColumnName),
+                @:Label = @column.GetDisplayColumn("u")
+            @:}, true).ToTreeAsync(u => u.Children, u => u.@(column.PidColumn), @(column.WhetherRequired == "Y" ? "0" : "null"));
             }
         }
         @:return new Dictionary<string, dynamic>
@@ -240,6 +228,19 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
     @if (Model.ImportFieldList.Count > 0) {
     @:
     @:/// <summary>
+    @:/// 导出@(Model.BusName)记录 🔖
+    @:/// </summary>
+    @:/// <param name="input"></param>
+    @:/// <returns></returns>
+    @:[DisplayName("导出@(Model.BusName)记录")]
+    @:[ApiDescriptionSettings(Name = "Export"), HttpPost, NonUnify]
+    @:public async Task<IActionResult> Export(Page@(Model.ClassName)Input input)
+    @:{
+        @:var list = (await Page(input)).Items?.Adapt<List<Export@(Model.ClassName)Output>>() ?? new();
+        @:return ExcelHelper.ExportTemplate(list, "@(Model.BusName)导出记录");
+    @:}
+    @:
+    @:/// <summary>
     @:/// 下载@(Model.BusName)数据导入模板 ⬇️
     @:/// </summary>
     @:/// <returns></returns>
@@ -294,6 +295,18 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
                     @:}
                     }
 
+                    if (dictTableField.Any()) {
+                    @:
+                    @:// 映射字典值
+                    @:foreach(var item in pageItems) {
+                        foreach (var column in dictTableField) {
+                        @:if (item.@(column.PropertyName) == null) continue;
+                        @:item.@(column.PropertyName) = @(column.LowerPropertyName)DictMap.GetValueOrDefault(item.@(column.ExtendedPropertyName));
+                        @:if (item.@(column.PropertyName) == null) item.Error = "@(column.ColumnComment)字典映射失败";
+                        }
+                    @:}
+                    }
+
                     @:
                     @:// 校验并过滤必填基本类型为null的字段
                     @:var rows = pageItems.Where(x => {
@@ -307,18 +320,6 @@ public class @(Model.ClassName)Service : IDynamicApiController, ITransient
                         @:return true;
                     @:}).Adapt<List<@(Model.ClassName)>>();
 
-                    if (dictTableField.Any()) {
-                    @:
-                    @:// 映射字典值
-                    @:foreach(var row in rows) {
-                        foreach (var column in dictTableField){
-                        @:if (row.@(column.PropertyName) == null) continue;
-                        @:row.@(column.PropertyName) = @(column.LowerPropertyName)DictMap.GetValueOrDefault(row.@(column.PropertyName));
-                        @:if (row.@(column.PropertyName) == null) row.Error = "@(column.ColumnComment)字典匹配失败";
-                        }
-                    @:}
-                    }
-
                     @:
                     @:var storageable = _sqlSugarClient.Storageable(rows)
                         foreach (var column in Model.ImportFieldList){

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

@@ -6,8 +6,6 @@ export const use@(Model.ClassName)Api = () => {
 	return {
 		// 分页查询@(Model.BusName)
 		page: baseApi.page,
-		// 获取@(Model.BusName)导出数据
-		list: baseApi.list,
 		// 查看@(Model.BusName)详细
 		detail: baseApi.detail,
 		// 新增@(Model.BusName)
@@ -27,6 +25,8 @@ export const use@(Model.ClassName)Api = () => {
 		@:upload@(column.PropertyName): (params: any) => baseApi.uploadFile(params, baseApi.baseUrl + 'upload@(column.PropertyName)'),
 		}
 		@if (Model.ImportFieldList.Count > 0) {
+		@:// 导出@(Model.BusName)数据
+		@:exportData: baseApi.exportData,
 		@:// 导入@(Model.BusName)数据
 		@:importData: baseApi.importData,
 		@:// 下载@(Model.BusName)数据导入模板

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

@@ -1,125 +1,173 @@
+<script lang="ts" name="@(Model.LowerClassName)" setup>
+import { ref, reactive, onMounted } from "vue";
+import { ElMessage } from "element-plus";
+import type { FormRules } from "element-plus";
+@if(Model.TableField.Any(x => x.EffectType == "DatePicker")) {
+@:import { formatDate } from '/@@/utils/formatTime';
+}
+@if(Model.UploadFieldList.Count > 0) {
+@:import { Plus } from "@@element-plus/icons-vue";
+@:import { UploadRequestOptions } from "element-plus";
+}
+@if(Model.HasDictField || Model.HasEnumField || Model.HasConstField) {
+@:import { useUserInfo } from "/@@/stores/userInfo";
+}
+import { use@(Model.ClassName)Api } from '/@@/api/@(Model.PagePath)/@(Model.LowerClassName)';
+
+//父级传递来的函数,用于回调
+const emit = defineEmits(["reloadTable"]);
+const @(Model.LowerClassName)Api = use@(Model.ClassName)Api();
+const ruleFormRef = ref();
+
+const state = reactive({
+	title: '',
+	loading: false,
+	showDialog: false,
+	ruleForm: {},
+	stores: @(Model.HasDictField || Model.HasEnumField || Model.HasConstField ? "useUserInfo()" : "{}"),
+	dropdownData: {} as any,
+});
+
+// 自行添加其他规则
+const rules = ref<FormRules>({
+  @foreach (var column in Model.AddUpdateFieldList.Where(col => col.WhetherRequired == "Y")) {
+	// if(column.EffectType == "Input" || @column.EffectType == "InputNumber" || @column.EffectType == "InputTextArea"){
+	// @:@column.LowerPropertyName: [{required: true, message: '请输入@(column.ColumnComment)!', trigger: 'blur',},],
+	// }
+	if(column.EffectType == "DatePicker" || @column.EffectType == "DictSelector" || @column.EffectType == "EnumSelector" || @column.EffectType == "ApiTreeSelector"){
+	@:@column.LowerPropertyName: [{required: true, message: '请选择@(column.ColumnComment)!', trigger: 'change',},],
+	} else {
+	@:@column.LowerPropertyName: [{required: true, message: '请输入@(column.ColumnComment)!', trigger: 'blur',},],
+	}
+  }
+});
+
+// 页面加载时
+onMounted(async () => {
+  @if (Model.DropdownFieldList.Count > 0) {
+  @:const data = await @(Model.LowerClassName)Api.getDropdownData(false).then(res => res.data.result) ?? {};
+  @foreach (var column in Model.DropdownFieldList) {
+  @:state.dropdownData.@(column.LowerPropertyName) = data.@(column.LowerPropertyName);
+  }
+  }
+});
+
+// 打开弹窗
+const openDialog = async (row: any, title) => {
+	state.title = title;
+	row = row ?? { @(Model.GetAddDefaultValue()) };
+	state.ruleForm = row.id ? await @(Model.LowerClassName)Api.detail(row.id).then(res => res.data.result) : JSON.parse(JSON.stringify(row));
+	state.showDialog = true;
+};
+
+// 关闭弹窗
+const closeDialog = () => {
+	emit("reloadTable");
+	state.showDialog = false;
+};
+
+// 提交
+const submit = async () => {
+	ruleFormRef.value.validate(async (isValid: boolean, fields?: any) => {
+		if (isValid) {
+			let values = state.ruleForm;
+			await @(Model.LowerClassName)Api[state.ruleForm.@(Model.PrimaryKeyFieldList.First().LowerPropertyName) ? 'update' : 'add'](values);
+			closeDialog();
+		} else {
+			ElMessage({
+				message: `表单有${Object.keys(fields).length}处验证失败,请修改后再提交`,
+				type: "error",
+			});
+		}
+	});
+};
+
+@foreach (var column in Model.UploadFieldList) {
+@:const upload@(column.PropertyName)Handle = async (options: UploadRequestOptions) => {
+	@:const res = await @(Model.LowerClassName)Api.upload@(column.PropertyName)(options);
+	@:state.ruleForm.@(column.LowerPropertyName) = res.data.result?.url;
+	@:};
+@:
+}
+//将属性或者函数暴露给父组件
+defineExpose({ openDialog });
+</script>
 <template>
 	<div class="@(Model.LowerClassName)-container">
-		<el-dialog v-model="isShowDialog" :width="800" draggable :close-on-click-modal="false">
+		<el-dialog v-model="state.showDialog" :width="800" draggable :close-on-click-modal="false">
 			<template #header>
 				<div style="color: #fff">
-					<!--<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Edit /> </el-icon>-->
-					<span>{{ props.title }}</span>
+					<span>{{ state.title }}</span>
 				</div>
 			</template>
-			<el-form :model="ruleForm" ref="ruleFormRef" label-width="auto" :rules="rules">
+			<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto" :rules="rules">
 				<el-row :gutter="35">
-					@foreach (var column in Model.TableField){
-					if(column.ColumnKey == "True"){
+					@foreach (var column in Model.PrimaryKeyFieldList) {
 					@:<el-form-item v-show="false">
-						<el-input v-model="ruleForm.@(column.LowerPropertyName)" />
-					</el-form-item>
-					}else{
-					if (column.WhetherAddUpdate == "Y"){
-					if(column.EffectType == "ForeignKey") {
-					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
-						@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
-							@:<el-select clearable filterable v-model="ruleForm.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
-								@:<el-option v-for="(item,index) in dropdownData.@(column.LowerPropertyName) ?? []" :key="index" :value="item.value" :label="item.label" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-					}else if(column.EffectType == "ApiTreeSelector"){
-					@:<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="dropdownData.@(column.LowerPropertyName) ?? []"
-								@:props="{ checkStrictly: true, emitPath: false }"
-								placeholder="请选择@(column.ColumnComment)"
-								clearable
-								filterable
-								class="w100"
-								v-model="ruleForm.@(column.LowerPropertyName)"
-							>
-								<template #default="{ node, data }">
-									<span>{{ data.label }}</span>
-									<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
-								</template>
-							</el-cascader>
-						</el-form-item>
-					</el-col>
-					}else if(column.EffectType == "DictSelector" || column.EffectType == "EnumSelector"){
-					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20" @(Model.IsStatus(column) ? $"v-if='!ruleForm.{Model.PrimaryKeyFieldList.First().LowerPropertyName}'" : "")>
+						@:<el-input v-model="state.ruleForm.@(column.LowerPropertyName)" />
+					@:</el-form-item>
+					}
+					@foreach (var column in Model.AddUpdateFieldList) {
+					var showStatus = Model.IsStatus(column) ? $"v-if=\"state.ruleForm.{Model.PrimaryKeyFieldList.First().LowerPropertyName}\" " : "";
+					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20" @showStatus>
 						@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
-							if (Model.IsStatus(column)) {
-							@:<el-switch v-model="ruleForm.@column.LowerPropertyName" :active-value="1" :inactive-value="2" size="small" />
+						if (column.IsSelectorEffectType) {
+							if (column.EffectType == "ApiTreeSelector") {
+							@:<el-cascader
+								@::options="state.dropdownData.@(column.LowerPropertyName) ?? []"
+								@::props="{ checkStrictly: true, emitPath: false }"
+								@:v-model="state.ruleForm.@(column.LowerPropertyName)"
+								@:placeholder="请选择@(column.ColumnComment)"
+								@:clearable
+								@:filterable
+								@:class="w100">
+								@:<template #default="{ node, data }">
+									@:<span>{{ data.label }}</span>
+									@:<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
+								@:</template>
+							@:</el-cascader>
 							} else {
-							@:<el-select clearable filterable v-model="ruleForm.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
-								@:<el-option v-for="(item, index) in getDictDataByCode('@(column.DictTypeCode)')"  :key="index" :value="item.code" :label="`[${item.code}]${item.value}`"></el-option>
+							@:<el-select clearable filterable v-model="state.ruleForm.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
+								if (column.EffectType == "ForeignKey") {
+								@:<el-option v-for="(item,index) in state.dropdownData.@(column.LowerPropertyName) ?? []" :key="index" :value="item.value" :label="item.label" />
+								} else if (column.EffectType == "ConstSelector") {
+								@:<el-option v-for="(item, index) in state.stores.getConstDataByType('@column.DictTypeCode') ?? []" :key="index" :label="item.name" :value="item.code" />
+								} else if (column.EffectType == "DictSelector" || column.EffectType == "EnumSelector") {
+								@:<el-option v-for="(item, index) in state.stores.getDictDataByCode('@(column.DictTypeCode)') ?? []"  :key="index" :value="item.code" :label="`[${item.code}]${item.value}`" />
+								}
 							@:</el-select>
 							}
-						</el-form-item>
-					</el-col>
-					}else if(column.EffectType == "ConstSelector"){
-					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
-						@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
-							@:<el-select clearable filterable v-model="ruleForm.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
-								@:<el-option v-for="(item, index) in getConstDataByType('@column.DictTypeCode')" :key="index" :label="item.name" :value="item.code">{{ item.name }}</el-option>
-							</el-select>
-						</el-form-item>
-					</el-col>
-					}else if(column.EffectType == "Input"){
-					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
-						@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
-							@:<el-input v-model="ruleForm.@(column.LowerPropertyName)" placeholder="请输入@(column.ColumnComment)" maxlength="@(column.ColumnLength)" show-word-limit clearable />
-						</el-form-item>
-					</el-col>
-					}else if(column.EffectType == "InputNumber"){
-					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
-						@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
-							@:<el-input-number v-model="ruleForm.@(column.LowerPropertyName)" placeholder="请输入@(column.ColumnComment)" clearable />
-						</el-form-item>
-					</el-col>
-					}else if(column.EffectType == "InputTextArea"){
-					@:<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
-						@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
-							@:<el-input v-model="ruleForm.@(column.LowerPropertyName)" placeholder="请输入@(column.ColumnComment)" type="textarea" maxlength="@(column.ColumnLength)" show-word-limit clearable />
-						</el-form-item>
-					</el-col>
-					}else if(column.EffectType == "Switch"){
-					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
-						@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
-							@:<el-switch v-model="ruleForm.@(column.LowerPropertyName)" active-text="是" inactive-text="否" />
-						</el-form-item>
-					</el-col>
-					}else if(column.EffectType == "DatePicker"){
-					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
-						@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
-							@:<el-date-picker v-model="ruleForm.@(column.LowerPropertyName)" type="date" placeholder="@(column.ColumnComment)" />
-						</el-form-item>
-					</el-col>
-					}else if(column.EffectType == "Upload"){
-					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
-						@:<el-form-item label="@column.ColumnComment" prop="@(column.LowerPropertyName)">
+						} else if (column.EffectType == "Upload") {
 							@:<el-upload
-							@:list-type="picture-card"
-							@::show-file-list="false"
-							@::http-request="upload@(column.PropertyName)Handle">
+								@:list-type="picture-card"
+								@::show-file-list="false"
+								@::http-request="upload@(column.PropertyName)Handle">
 								@:<img
-								@:v-if="ruleForm.@(column.LowerPropertyName)"
-								@::src="ruleForm.@(column.LowerPropertyName)"
-								@:@@click="ruleForm.@(column.LowerPropertyName)=''"
-								@:style="width: 100%; height: 100%; object-fit: contain"/>
-								@:<el-icon v-else><Plus /></el-icon>
-							</el-upload>
-						</el-form-item>
-					</el-col>
-					}else{
-					}
-					}
-					}
+									@:v-if="state.ruleForm.@(column.LowerPropertyName)"
+									@::src="state.ruleForm.@(column.LowerPropertyName)"
+									@:@@click="state.ruleForm.@(column.LowerPropertyName)=''"
+									@:style="width: 100%; height: 100%; object-fit: contain"/>
+									@:<el-icon v-else><Plus /></el-icon>
+							@:</el-upload>
+						} else if (column.EffectType == "InputNumber") {
+							@:<el-input-number v-model="state.ruleForm.@(column.LowerPropertyName)" placeholder="请输入@(column.ColumnComment)" clearable />
+						} else if (column.EffectType == "Switch") {
+							@:<el-switch v-model="state.ruleForm.@(column.LowerPropertyName)" active-text="是" inactive-text="否" />
+						} else if (column.EffectType == "DatePicker") {
+							@:<el-date-picker v-model="state.ruleForm.@(column.LowerPropertyName)" type="date" placeholder="@(column.ColumnComment)" />
+						} else {
+							var inputType = column.EffectType == "InputTextArea" ? "type=\"textarea\"" : "";
+							@:<el-input v-model="state.ruleForm.@(column.LowerPropertyName)" placeholder="请输入@(column.ColumnComment)" maxlength="@(column.ColumnLength)" @(inputType) show-word-limit clearable />
+						}
+						@:</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>
+					<el-button @@click="() => state.showDialog = false">取 消</el-button>
+					<el-button @@click="submit" type="primary">确 定</el-button>
 				</span>
 			</template>
 		</el-dialog>
@@ -127,116 +175,6 @@
 </template>
 <style lang="scss" scoped>
 :deep(.el-select), :deep(.el-input-number) {
-	width: 100%;
+  width: 100%;
 }
-</style>
-<script lang="ts" setup name="@(Model.LowerClassName)">
-	import { ref,onMounted } from "vue";
-	import { ElMessage } from "element-plus";
-	import type { FormRules } from "element-plus";
-	@if(Model.TableField.Any(x=>x.EffectType == "DatePicker")){
-  	@:import { formatDate } from '/@@/utils/formatTime';
-	}
-	@if(Model.TableField.Any(x=>x.EffectType == "Upload")){
-    @:import { Plus } from "@@element-plus/icons-vue";
-    @:import { UploadRequestOptions } from "element-plus";
-	}
-	import { use@(Model.ClassName)Api } from '/@@/api/@(Model.PagePath)/@(Model.LowerClassName)';
-	@if(Model.HasDictField || Model.HasEnumField) {
-	@:import { useUserInfo } from "/@@/stores/userInfo";
-	@:
-	@:const getDictDataByCode = useUserInfo().getDictDataByCode;
-	}
-	//父级传递来的参数
-	var props = defineProps({
-		title: {
-			type: String,
-			default: "",
-		},
-	});
-	//父级传递来的函数,用于回调
-	const emit = defineEmits(["reloadTable"]);
-	const ruleFormRef = ref();
-	const isShowDialog = ref(false);
-	const ruleForm = ref<any>({});
-	const @(Model.LowerClassName)Api = use@(Model.ClassName)Api();
-	@if (Model.DropdownFieldList.Count > 0) {
-	@:const dropdownData = ref<any>({});
-	}
-	//自行添加其他规则
-	const rules = ref<FormRules>({
-	@foreach (var column in Model.TableField) {
-	if(column.WhetherRequired == "Y") {
-		if(column.EffectType == "Input" || @column.EffectType == "InputNumber" || @column.EffectType == "InputTextArea"){
-		@:@column.LowerPropertyName: [{required: true, message: '请输入@(column.ColumnComment)!', trigger: 'blur',},],
-		}else if(column.EffectType == "DatePicker" || @column.EffectType == "DictSelector" || @column.EffectType == "EnumSelector" || @column.EffectType == "ApiTreeSelector"){
-		@:@column.LowerPropertyName: [{required: true, message: '请选择@(column.ColumnComment)!', trigger: 'change',},],
-		}
-    }
-	}
-	});
-
-	// 页面加载时
-	onMounted(async () => {
-		@if (Model.DropdownFieldList.Count > 0) {
-		@:const data = await @(Model.LowerClassName)Api.getDropdownData(false).then(res => res.data.result) ?? {};
-		@foreach (var column in Model.DropdownFieldList) {
-		@:dropdownData.value.@(column.LowerPropertyName) = data.@(column.LowerPropertyName);
-		}
-		}
-	});
-
-	@if(Model.TableField.Any(x=>x.EffectType == "ConstSelector")){
-	@:// 获取根据常量类型获取常量数据列表
-	@:const getConstDataByType = (type: string) => useUserInfo().constList?.find(x => x.code == type)?.data?.result ?? [];
-	}
-
-	// 打开弹窗
-	const openDialog = async (row: any) => {
-		// ruleForm.value = JSON.parse(JSON.stringify(row));
-		// 改用detail获取最新数据来编辑
-		let rowData = JSON.parse(JSON.stringify(row));
-		if (rowData.id)
-			ruleForm.value = (await @(Model.LowerClassName)Api.detail(rowData.id)).data.result;
-		else
-			ruleForm.value = rowData;
-		isShowDialog.value = true;
-	};
-
-	// 关闭弹窗
-	const closeDialog = () => {
-		emit("reloadTable");
-		isShowDialog.value = false;
-	};
-
-	// 取消
-	const cancel = () => {
-		isShowDialog.value = false;
-	};
-
-	// 提交
-	const submit = async () => {
-		ruleFormRef.value.validate(async (isValid: boolean, fields?: any) => {
-			if (isValid) {
-				let values = ruleForm.value;
-				await @(Model.LowerClassName)Api[ruleForm.value.@(Model.PrimaryKeyFieldList.First().LowerPropertyName) ? 'update' : 'add'](values);
-				closeDialog();
-			} else {
-				ElMessage({
-					message: `表单有${Object.keys(fields).length}处验证失败,请修改后再提交`,
-					type: "error",
-				});
-			}
-		});
-	};
-	
-	@foreach (var column in Model.UploadFieldList) {
-	@:const upload@(column.PropertyName)Handle = async (options: UploadRequestOptions) => {
-		@:const res = await @(Model.LowerClassName)Api.upload@(column.PropertyName)(options);
-		@:ruleForm.value.@(column.LowerPropertyName) = res.data.result?.url;
-	@:};
-	@:
-	}
-	//将属性或者函数暴露给父组件
-	defineExpose({ openDialog });
-</script>
+</style>

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

@@ -1,47 +1,182 @@
-<template>
+<script lang="ts" setup name="@(Model.LowerClassName)">
+import { ref, reactive, onMounted } from "vue";
+import { auth } from '/@@/utils/authFunction';
+import { getAPI } from '/@@/utils/axios-utils';
+import { ElMessageBox, ElMessage } from "element-plus";
+import { downloadStreamFile } from "/@@/utils/downloadFile";
+@if(Model.TableField.Any(x => x.EffectType == "ConstSelector")) {
+@:import { codeToName, getConstType } from '/@@/utils/constHelper';
+}
+@if(Model.TableField.Any(x => x.EffectType == "DatePicker")) {
+@:import { formatDate } from '/@@/utils/formatTime';
+}
+@if(Model.PrintType == "custom") {
+@:// 推荐设置操作 width 为 200
+@:import { hiprint } from 'vue-plugin-hiprint';
+@:import { SysPrintApi } from '/@@/api-services/api';
+@:import { SysPrint } from '/@@/api-services/models';
+}
+import { use@(Model.ClassName)Api } from '/@@/api/@(Model.PagePath)/@(Model.LowerClassName)';
+@if(Model.HasDictField || Model.HasEnumField || Model.HasConstField) {
+@:import { useUserInfo } from "/@@/stores/userInfo";
+@:import DictLabel from "/@@/components/table/dictLabel.vue";
+}
+import editDialog from '/@@/views/@(Model.PagePath)/@(Model.LowerClassName)/component/editDialog.vue'
+import printDialog from '/@@/views/system/print/component/hiprint/preview.vue'
+import ModifyRecord from '/@@/components/table/modifyRecord.vue';
+@if(Model.ImportFieldList.Count > 0) {
+@:import ImportData from "/@@/components/table/importData.vue";
+}
+
+const @(Model.LowerClassName)Api = use@(Model.ClassName)Api();
+const printDialogRef = ref();
+const editDialogRef = ref();
+@if (Model.ImportFieldList.Count > 0) {
+@:const importDataRef = ref();
+}
+const state = reactive({
+  loading: false,
+  tableLoading: false,
+  stores: @(Model.HasDictField || Model.HasEnumField || Model.HasConstField ? "useUserInfo()" : "{}"),
+  showAdvanceQueryUI: @(Model.HasLikeQuery ? "false" : "true"),
+  dropdownData: {} as any,
+  selectData: [] as [],
+  tableQueryParams: {},
+  tableParams: {
+    page: 1,
+    pageSize: 20,
+    total: 0,
+    field: 'createTime', // 默认的排序字段
+    order: 'descending', // 排序方向
+    descStr: 'descending', // 降序排序的关键字符
+  },
+  tableData: [],
+});
+
+// 页面加载时
+onMounted(async () => {
+  @if (Model.DropdownFieldList.Count > 0) {
+  @:const data = await @(Model.LowerClassName)Api.getDropdownData(true).then(res => res.data.result) ?? {};
+  @foreach (var column in Model.DropdownFieldList) {
+  @:state.dropdownData.@(column.LowerPropertyName) = data.@(column.LowerPropertyName);
+  }
+  }
+});
+
+// 查询操作
+const handleQuery = async (params: any = {}) => {
+  state.tableLoading = true;
+  state.tableParams = Object.assign(state.tableParams, params);
+  const result = await @(Model.LowerClassName)Api.page(Object.assign(state.tableQueryParams, state.tableParams)).then(res => res.data.result);
+  state.tableParams.total = result?.total;
+  state.tableData = result?.items ?? [];
+  state.tableLoading = false;
+};
+
+// 列排序
+const sortChange = async (column: any) => {
+  state.tableParams.field = column.prop;
+  state.tableParams.order = column.order;
+  await handleQuery();
+};
+
+// 设置状态
+const change@(Model.ClassName)Status = async (row: any) => {
+  await @(Model.LowerClassName)Api.setStatus({ @(Model.PrimaryKeysFormat(", ", "{0}: row.{0}", true)), status: row.status }).then(() => ElMessage.success('状态设置成功'));
+};
+
+// 打开打印页面
+const openPrint@(Model.ClassName) = async (row: any) => {
+@if(Model.PrintType == "custom"){
+  @:var res = await getAPI(SysPrintApi).apiSysPrintPrintNameGet('@Model.PrintName');
+  @:var printTemplate = res.data.result as SysPrint;
+  @:var template = JSON.parse(printTemplate.template);
+  @:row['printDate'] = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS')
+  @:printDialogRef.value.showDialog(new hiprint.PrintTemplate({template: template}), row, template.panels[0].width);
+  }
+}
+
+// 删除
+const del@(Model.ClassName) = (row: any) => {
+  ElMessageBox.confirm(`确定要删除吗?`, "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    await @(Model.LowerClassName)Api.delete({ @(Model.PrimaryKeysFormat(", ", "{0}: row.{0}", true)) });
+    handleQuery();
+    ElMessage.success("删除成功");
+  }).catch(() => {});
+};
+
+// 批量删除
+const batchDel@(Model.ClassName) = () => {
+  ElMessageBox.confirm(`确定要删除${state.selectData.length}条记录吗?`, "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    await @(Model.LowerClassName)Api.batchDelete(state.selectData.map(u => ({ @(Model.PrimaryKeysFormat(", ", "{0}: u.{0}", true)) }) )).then(res => {
+      ElMessage.success(`成功批量删除${res.data.result}条记录`);
+      handleQuery();
+    });
+  }).catch(() => {});
+};
+
+@if (Model.ImportFieldList.Count > 0) {
+@:// 导出数据
+@:const export@(Model.ClassName)Data = async () => {
+  @:const params = Object.assign(state.tableQueryParams, state.tableParams, { page: 1, pageSize: 99999999 });
+  @:@(Model.LowerClassName)Api.exportData(params).then(res => downloadStreamFile(res));
+@:}
+}
+
+handleQuery();
+</script>
+<template>
   <div class="@(Model.LowerClassName)-container">
     <el-card shadow="hover" :body-style="{ paddingBottom: '0' }"> 
-      <el-form :model="queryParams" ref="queryForm" labelWidth="90">
+      <el-form :model="state.tableQueryParams" ref="queryForm" labelWidth="90">
         <el-row>
           @if(Model.QueryWhetherList.Count > 0) {
           if(Model.HasLikeQuery) {
           @:<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10">
             @:<el-form-item label="关键字">
-              @:<el-input v-model="queryParams.keyword" clearable placeholder="请输入模糊查询关键字"/>
+              @:<el-input v-model="state.tableQueryParams.keyword" clearable placeholder="请输入模糊查询关键字"/>
             @:</el-form-item>
           @:</el-col>
           }
           foreach (var column in Model.QueryWhetherList) {
-          @:<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI">
+          @:<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="state.showAdvanceQueryUI">
             if(column.EffectType == "Input" || column.EffectType == "InputTextArea"){
             @:<el-form-item label="@column.ColumnComment">
-              @:<el-input v-model="queryParams.@(column.LowerPropertyName)" clearable placeholder="请输入@(column.ColumnComment)"/>
+              @:<el-input v-model="state.tableQueryParams.@(column.LowerPropertyName)" clearable placeholder="请输入@(column.ColumnComment)"/>
             @:</el-form-item>
             }else if(column.EffectType == "InputTextArea"){
             @:<el-form-item label="@column.ColumnComment">
-              @:<el-input-number v-model="queryParams.@(column.LowerPropertyName)"  clearable placeholder="请输入@(column.ColumnComment)"/>
+              @:<el-input-number v-model="state.tableQueryParams.@(column.LowerPropertyName)"  clearable placeholder="请输入@(column.ColumnComment)"/>
               @:
             @:</el-form-item>
             }else if(column.EffectType == "InputNumber"){
             @:<el-form-item label="@column.ColumnComment">
-              @:<el-input-number v-model="queryParams.@(column.LowerPropertyName)"  clearable placeholder="请输入@(column.ColumnComment)"/>
+              @:<el-input-number v-model="state.tableQueryParams.@(column.LowerPropertyName)"  clearable placeholder="请输入@(column.ColumnComment)"/>
             @:</el-form-item>
             }else if(column.EffectType == "ForeignKey") {
             @:<el-form-item label="@column.ColumnComment">
-              @:<el-select clearable filterable v-model="queryParams.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
-                @:<el-option v-for="(item,index) in dropdownData.@(column.LowerPropertyName) ?? []" :key="index" :value="item.value" :label="item.label" />
+              @:<el-select clearable filterable v-model="state.tableQueryParams.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
+                @:<el-option v-for="(item,index) in state.dropdownData.@(column.LowerPropertyName) ?? []" :key="index" :value="item.value" :label="item.label" />
               @:</el-select>
             @:</el-form-item>
             }else if(column.EffectType == "ApiTreeSelector"){
             @:<el-form-item label="@column.ColumnComment">
               @:<el-cascader
-                @::options="dropdownData.@(column.LowerPropertyName) ?? []"
+                @::options="state.dropdownData.@(column.LowerPropertyName) ?? []"
                 @:@:props="{ checkStrictly: true, emitPath: false }"
                 @:placeholder="请选择@(column.ColumnComment)"
                 @:clearable
                 @:filterable
                 @:class="w100"
-                @:v-model="queryParams.@(column.LowerPropertyName)"
+                @:v-model="state.tableQueryParams.@(column.LowerPropertyName)"
                 @:>
                   @:<template #default="{ node, data }">
                     @:<span>{{ data.label }}</span>
@@ -51,16 +186,16 @@
             @:</el-form-item>
             }else if(column.EffectType == "DictSelector" || column.EffectType == "EnumSelector"){
             @:<el-form-item label="@column.ColumnComment">
-              @:<el-select clearable filterable v-model="queryParams.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
-                @:<el-option v-for="(item,index) in getDictDataByCode('@(column.DictTypeCode)')" :key="index" :value="item.code" :label="`[${item.code}]${item.value}`" />
+              @:<el-select clearable filterable v-model="state.tableQueryParams.@(column.LowerPropertyName)" placeholder="请选择@(column.ColumnComment)">
+                @:<el-option v-for="(item,index) in state.stores.getDictDataByCode('@(column.DictTypeCode)')" :key="index" :value="item.code" :label="`[${item.code}]${item.value}`" />
               @:</el-select>
             @:</el-form-item>
             }else if(column.EffectType == "DatePicker"){
             @:<el-form-item label="@column.ColumnComment">
               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')]" />
+              @:<el-date-picker type="daterange" v-model="state.tableQueryParams.@(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)" />
+              @:<el-date-picker placeholder="请选择@(column.ColumnComment)" value-format="YYYY/MM/DD"  v-model="state.tableQueryParams.@(column.LowerPropertyName)" />
               }
             @:</el-form-item>
             }
@@ -68,20 +203,21 @@
           }
           }
           <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-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 icon="ele-Refresh" @@click="() => queryParams = {}"> 重置 </el-button>
+                @:<el-button icon="ele-Refresh" @@click="() => state.tableQueryParams = {}"> 重置 </el-button>
                 @if (Model.HasLikeQuery) {
-                @:<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>
+                @:<el-button icon="ele-ZoomIn" @@click="() => state.showAdvanceQueryUI = true" v-if="!state.showAdvanceQueryUI" style="margin-left:5px;"> 高级查询 </el-button>
+                @:<el-button icon="ele-ZoomOut" @@click="() => state.showAdvanceQueryUI = false" v-if="state.showAdvanceQueryUI" style="margin-left:5px;"> 隐藏 </el-button>
                 }
                 }
-                <el-button type="danger" style="margin-left:5px;" icon="ele-Delete" @@click="batchDel@(Model.ClassName)" :disabled="selectData.length == 0" v-auth="'@(Model.LowerClassName):batchDelete'"> 删除 </el-button>
-                <el-button type="primary" style="margin-left:5px;" icon="ele-Plus" @@click="openAdd@(Model.ClassName)" v-auth="'@(Model.LowerClassName):add'"> 新增 </el-button>
+                <el-button type="danger" style="margin-left:5px;" icon="ele-Delete" @@click="batchDel@(Model.ClassName)" :disabled="state.selectData.length == 0" v-auth="'@(Model.LowerClassName):batchDelete'"> 删除 </el-button>
+                <el-button type="primary" style="margin-left:5px;" icon="ele-Plus" @@click="editDialogRef.openDialog(null, '新增@(Model.BusName)')" v-auth="'@(Model.LowerClassName):add'"> 新增 </el-button>
                 @if (Model.ImportFieldList.Count > 0) {
-                @:<el-button type="warning" icon="ele-MostlyCloudy" @@click="importDataRef.openDialog()" v-auth="'@(Model.LowerClassName):import'"> 导入 </el-button>
+                @:<el-button type="primary" style="margin-left:5px;" icon="ele-FolderOpened" @@click="export@(Model.ClassName)Data" v-reclick="20000" v-auth="'@(Model.LowerClassName):export'"> 导出 </el-button>
+                @:<el-button type="warning" style="margin-left:5px;" icon="ele-MostlyCloudy" @@click="importDataRef.openDialog()" v-auth="'@(Model.LowerClassName):import'"> 导入 </el-button>
                 }
               </el-button-group>
             </el-form-item>
@@ -91,7 +227,7 @@
         @:<el-row>
           @:<el-col>
             @:<el-button-group style="margin-left:20px;margin-bottom:5px;">
-              @:<el-button type="primary" icon="ele-Plus" @@click="openAdd@(Model.ClassName)" v-auth="'@(Model.LowerClassName):add'"> 新增 </el-button>
+              @:<el-button type="primary" icon="ele-Plus" @@click="editDialogRef.openDialog(null, '新增@(Model.BusName)')" v-auth="'@(Model.LowerClassName):add'"> 新增 </el-button>
             </el-button-group>
           @:</el-col>
         @:</el-row>
@@ -99,48 +235,41 @@
       </el-form>
     </el-card>
     <el-card class="full-table" shadow="hover" style="margin-top: 5px">
-      <el-table :data="tableData" @@selection-change="(val: any[]) => { selectData = val; }" style="width: 100%" v-loading="loading" tooltip-effect="light" row-key="@Model.PrimaryKeyFieldList.First().LowerPropertyName" @@sort-change="sortChange" border>
+      <el-table :data="state.tableData" @@selection-change="(val: any[]) => { state.selectData = val; }" style="width: 100%" v-loading="state.tableLoading" tooltip-effect="light" row-key="@Model.PrimaryKeyFieldList.First().LowerPropertyName" @@sort-change="sortChange" border>
         <el-table-column type="selection" width="40" align="center" v-auth="'@(Model.LowerClassName):batchDelete'" />
         <el-table-column type="index" label="序号" width="55" align="center"/>
-        @foreach (var column in Model.TableField.Where(u => u.WhetherTable == "Y")){
-        if(column.EffectType == "Upload" || @column.EffectType == "ForeignKey" || @column.EffectType == "ApiTreeSelector" || @column.EffectType == "Switch" || @column.EffectType == "ConstSelector"){
+    @foreach (var column in Model.TableField.Where(u => u.WhetherTable == "Y")){
+      if(column.EffectType == "DictSelector" || column.EffectType == "EnumSelector" || column.EffectType == "Upload" || @column.EffectType == "Switch") {
         @:<el-table-column @(Model.GetElTableColumnCustomProperty(column)) show-overflow-tooltip>
           @:<template #default="scope">
-            if(column.EffectType == "Upload"){
+        if (column.EffectType == "Upload") {
             @:<el-image
-            @:v-if="scope.row.@column.LowerPropertyName"
-            @:style="width: 60px; height: 60px"
-            @::src="scope.row.@column.LowerPropertyName"
-            @::lazy="true"
-            @::hide-on-click-modal="true"
-            @::preview-src-list="[scope.row.@column.LowerPropertyName]"
-            @::initial-index="0"
-            @:fit="scale-down"
-            @:preview-teleported />
-            }else if(column.EffectType == "ForeignKey" || column.EffectType == "ApiTreeSelector"){
-            @:<span>{{scope.row.@(column.LowerExtendedPropertyName)}}</span>
-            }else if(column.EffectType == "Switch"){
+              @:v-if="scope.row.@column.LowerPropertyName"
+              @:style="width: 60px; height: 60px"
+              @::src="scope.row.@column.LowerPropertyName"
+              @::lazy="true"
+              @::hide-on-click-modal="true"
+              @::preview-src-list="[scope.row.@column.LowerPropertyName]"
+              @::initial-index="0"
+              @:fit="scale-down"
+              @:preview-teleported />
+        } else if (column.EffectType == "Switch") {
             @:<el-tag v-if="scope.row.@(column.LowerPropertyName)"> 是 </el-tag>
             @:<el-tag type="danger" v-else> 否 </el-tag>
-            }else if(column.EffectType == "ConstSelector"){
-            @:<span>{{codeToName(scope.row.@(column.LowerPropertyName), '@(column.DictTypeCode)')}}</span>
-            }
-          @:</template>
-        @:</el-table-column>
-        } else if (column.EffectType == "DictSelector" || column.EffectType == "EnumSelector") {
-        @:<el-table-column @(Model.GetElTableColumnCustomProperty(column)) show-overflow-tooltip>
-          @:<template #default="scope">
-            if (Model.IsStatus(column)) {
-              @:<el-switch v-model="scope.row.@column.LowerPropertyName" :active-value="1" :inactive-value="2" size="small" @@change="change@(Model.ClassName)Status(scope.row)" />
-            } else {
-              @:<DictLabel :value="scope.row.@column.LowerPropertyName" code="@column.DictTypeCode" />
-            }
+        } else if (Model.IsStatus(column)) {
+            @:<el-switch v-model="scope.row.@column.LowerPropertyName" :active-value="1" :inactive-value="2" size="small" @@change="change@(Model.ClassName)Status(scope.row)" />
+        } else {
+            @:<DictLabel :value="scope.row.@column.LowerPropertyName" code="@column.DictTypeCode" />
+        }
           @:</template>
         @:</el-table-column>
-        } else {
+      } else if (column.EffectType == "ConstSelector" || column.EffectType == "ForeignKey" || column.EffectType == "ApiTreeSelector") {
+        var formatter = column.EffectType == "ConstSelector" ? $"codeToName(row.{column.LowerPropertyName}, '{column.DictTypeCode}')" : $"row.{column.LowerExtendedPropertyName}";
+        @:<el-table-column @(Model.GetElTableColumnCustomProperty(column)) :formatter="(row) => @(formatter)" show-overflow-tooltip />
+      } else {
         @:<el-table-column @(Model.GetElTableColumnCustomProperty(column)) show-overflow-tooltip />
-        }
-        }
+      }
+    }
         <el-table-column label="修改记录" width="100" align="center" show-overflow-tooltip>
           <template #default="scope">
             <ModifyRecord :data="scope.row" />
@@ -148,206 +277,32 @@
         </el-table-column>
         <el-table-column label="操作" width="@(Model.PrintType == "custom" ? "200" : "140")" align="center" fixed="right" show-overflow-tooltip v-if="auth('@(Model.LowerClassName):update') || auth('@(Model.LowerClassName):delete')">
           <template #default="scope">
-            @if (Model.PrintType == "custom"){
+            @if (Model.PrintType == "custom") {
             @:<el-button icon="ele-Printer" size="small" text type="primary" @@click="openPrint@(Model.ClassName)(scope.row)" v-auth="'@(Model.LowerClassName):print'"> 打印 </el-button>
             }
-            <el-button icon="ele-Edit" size="small" text type="primary" @@click="openEdit@(Model.ClassName)(scope.row)" v-auth="'@(Model.LowerClassName):update'"> 编辑 </el-button>
+            <el-button icon="ele-Edit" size="small" text type="primary" @@click="editDialogRef.openDialog(scope.row, '编辑@(Model.BusName)')" v-auth="'@(Model.LowerClassName):update'"> 编辑 </el-button>
             <el-button icon="ele-Delete" size="small" text type="primary" @@click="del@(Model.ClassName)(scope.row)" v-auth="'@(Model.LowerClassName):delete'"> 删除 </el-button>
           </template>
         </el-table-column>
       </el-table>
-      <el-pagination
-				v-model:currentPage="tableParams.page"
-				v-model:page-size="tableParams.pageSize"
-				:page-sizes="[10, 20, 50, 100, 200, 500]"
-                :total="tableParams.total"
-                @@size-change="handleSizeChange"
-                @@current-change="handleCurrentChange"
-                layout="total, sizes, prev, pager, next, jumper"
-				size="small"
-				background
-      />
-      <printDialog
-        ref="printDialogRef"
-        :title="print@(Model.ClassName)Title"
-        @@reloadTable="handleQuery" />
-      <editDialog
-        ref="editDialogRef"
-        :title="edit@(Model.ClassName)Title"
-        @@reloadTable="handleQuery"
-      />
+      <el-pagination 
+              v-model:currentPage="state.tableParams.page"
+              v-model:page-size="state.tableParams.pageSize"
+              @@size-change="(val) => handleQuery({ pageSize: val })"
+              @@current-change="(val) => handleQuery({ page: val })"
+              layout="total, sizes, prev, pager, next, jumper"
+              :page-sizes="[10, 20, 50, 100, 200, 500]"
+              :total="state.tableParams.total"
+              size="small"
+              background />
+      @if (Model.ImportFieldList.Count > 0) {
+      @:<ImportData ref="importDataRef" :import="@(Model.LowerClassName)Api.importData" :download="@(Model.LowerClassName)Api.downloadTemplate" v-auth="'@(Model.LowerClassName):import'" @@refresh="handleQuery"/>
+      }
+      <printDialog ref="printDialogRef" :title="'打印@(Model.BusName)'" @@reloadTable="handleQuery" />
+      <editDialog ref="editDialogRef" @@reloadTable="handleQuery" />
     </el-card>
   </div>
-  @if (Model.ImportFieldList.Count > 0) {
-  @:<ImportData
-        @:ref="importDataRef"
-        @::import="@(Model.LowerClassName)Api.importData"
-        @::download="@(Model.LowerClassName)Api.downloadTemplate"
-        @:v-auth="'@(Model.LowerClassName):import'"
-        @:@@refresh="handleQuery"
-        @:/>
-  }
 </template>
-
-<script lang="ts" setup name="@(Model.LowerClassName)">
-  import { ref, onMounted } from "vue";
-  import { auth } from '/@@/utils/authFunction';
-  import { getAPI } from '/@@/utils/axios-utils';
-  import { ElMessageBox, ElMessage } from "element-plus";
-  @if(Model.TableField.Any(x => x.EffectType == "DatePicker")) {
-  @:import { formatDate } from '/@@/utils/formatTime';
-  }
-  @if(Model.TableField.Any(x => x.EffectType == "ConstSelector")) {
-  @:import { codeToName, getConstType } from '/@@/utils/constHelper';
-  }
-  @if(Model.PrintType == "custom") {
-  @:// 推荐设置操作 width 为 200
-  @:import { hiprint } from 'vue-plugin-hiprint';
-  @:import { SysPrintApi } from '/@@/api-services/api';
-  @:import { SysPrint } from '/@@/api-services/models';
-  }
-  @if(Model.ImportFieldList.Count > 0) {
-  @:import ImportData from "/@@/components/table/importData.vue";
-  }
-  import editDialog from '/@@/views/@(Model.PagePath)/@(Model.LowerClassName)/component/editDialog.vue'
-  import printDialog from '/@@/views/system/print/component/hiprint/preview.vue'
-  import ModifyRecord from '/@@/components/table/modifyRecord.vue';
-  import { use@(Model.ClassName)Api} from '/@@/api/@(Model.PagePath)/@(Model.LowerClassName)';
-  @if(Model.HasDictField || Model.HasEnumField) {
-  @:import { useUserInfo } from "/@@/stores/userInfo";
-  @:import DictLabel from "/@@/components/table/dictLabel.vue";
-  @:
-  @:const getDictDataByCode = useUserInfo().getDictDataByCode;
-  }
-  const showAdvanceQueryUI = ref(@(Model.HasLikeQuery ? "false" : "true"));
-  const @(Model.LowerClassName)Api = use@(Model.ClassName)Api();
-  const printDialogRef = ref();
-  const editDialogRef = ref();
-  @if (Model.ImportFieldList.Count > 0){
-  @:const importDataRef = ref();
-  }
-  @if (Model.DropdownFieldList.Count > 0) {
-  @:const dropdownData = ref<any>({});
-  }
-  const loading = ref(false);
-  const tableData = ref<any>([]);
-  const selectData = ref<any>([]);
-  const queryParams = ref<any>({});
-  const tableParams = ref({
-    page: 1,
-    pageSize: 10,
-    field: 'createTime', // 默认的排序字段
-    order: 'descending', // 排序方向
-    descstr: 'descending', // 降序排序的关键字符
-    total: 0 as any,
-  });
-
-  const print@(Model.ClassName)Title = ref("");
-  const edit@(Model.ClassName)Title = ref("");
-
-  // 页面加载时
-  onMounted(async () => {
-    @if (Model.DropdownFieldList.Count > 0) {
-    @:const data = await @(Model.LowerClassName)Api.getDropdownData(true).then(res => res.data.result) ?? {};
-    @foreach (var column in Model.DropdownFieldList) {
-    @:dropdownData.value.@(column.LowerPropertyName) = data.@(column.LowerPropertyName);
-    }
-    }
-  });
-
-  // 改变高级查询的控件显示状态
-  const changeAdvanceQueryUI = () => {
-    showAdvanceQueryUI.value = !showAdvanceQueryUI.value;
-  }
-
-  // 查询操作
-  const handleQuery = async () => {
-    loading.value = true;
-    var res = await @(Model.LowerClassName)Api.page(Object.assign(queryParams.value, tableParams.value));
-    tableData.value = res.data.result?.items ?? [];
-    tableParams.value.total = res.data.result?.total;
-    loading.value = false;
-  };
-
-  // 列排序
-  const sortChange = async (column: any) => {
-	tableParams.value.field = column.prop;
-	tableParams.value.order = column.order;
-	await handleQuery();
-  };
-
-  // 打开新增页面
-  const openAdd@(Model.ClassName) = () => {
-    edit@(Model.ClassName)Title.value = '添加@(Model.BusName)';
-    const data = { @(Model.GetAddDefaultValue()) };
-    editDialogRef.value.openDialog(data);
-  };
-
-  // 设置状态
-  const change@(Model.ClassName)Status = async (row: any) => {
-    await @(Model.LowerClassName)Api.setStatus({ id: row.id, status: row.status })
-            .then(() => ElMessage.success('状态设置成功'))
-            .catch(() => { row.status = row.status == 1 ? 2 : 1; });
-  };
-  
-  // 打开打印页面
-  const openPrint@(Model.ClassName) = async (row: any) => {
-    print@(Model.ClassName)Title.value = '打印@(Model.BusName)';
-    @if(Model.PrintType == "custom"){
-    @:var res = await getAPI(SysPrintApi).apiSysPrintPrintNameGet('@Model.PrintName');
-	@:var printTemplate = res.data.result as SysPrint;
-    @:var template = JSON.parse(printTemplate.template);
-    @:row['printDate'] = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS')
-    @:printDialogRef.value.showDialog(new hiprint.PrintTemplate({template: template}), row, template.panels[0].width);
-    }
-  }
-  
-  // 打开编辑页面
-  const openEdit@(Model.ClassName) = (row: any) => {
-    edit@(Model.ClassName)Title.value = '编辑@(Model.BusName)';
-    editDialogRef.value.openDialog(row);
-  };
-
-  // 删除
-  const del@(Model.ClassName) = (row: any) => {
-    ElMessageBox.confirm(`确定要删除吗?`, "提示", {
-      confirmButtonText: "确定",
-      cancelButtonText: "取消",
-      type: "warning",
-    }).then(async () => {
-      await @(Model.LowerClassName)Api.delete(row);
-      handleQuery();
-      ElMessage.success("删除成功");
-    }).catch(() => {});
-  };
-
-  // 批量删除
-  const batchDel@(Model.ClassName) = () => {
-    ElMessageBox.confirm(`确定要删除${selectData.value.length}条记录吗?`, "提示", {
-      confirmButtonText: "确定",
-      cancelButtonText: "取消",
-      type: "warning",
-    }).then(async () => {
-      const count = await @(Model.LowerClassName)Api.batchDelete(selectData.value.map(u => ({ @(Model.PrimaryKeysFormat(", ", "{0}: u.{0}", true)) }) )).then(res => res.data.result);
-      handleQuery();
-      ElMessage.success(`成功批量删除${count}条记录`);
-    }).catch(() => {});
-  };
-
-  // 改变页面容量
-  const handleSizeChange = (val: number) => {
-    tableParams.value.pageSize = val;
-    handleQuery();
-  };
-
-  // 改变页码序号
-  const handleCurrentChange = (val: number) => {
-    tableParams.value.page = val;
-    handleQuery();
-  };
-  
-  handleQuery();
-</script>
 <style scoped>
 :deep(.el-input), :deep(.el-select), :deep(.el-input-number) {
   width: 100%;

+ 6 - 5
Web/src/api/base/index.ts

@@ -13,11 +13,6 @@ export const useBaseApi = (module: string) => {
             method: 'post',
             data,
         }),
-        list: (params: any) => request({
-            url: baseUrl + "list",
-            method: 'get',
-            params,
-        }),
         detail: (id: any) => request({
             url: baseUrl + "detail",
             method: 'get',
@@ -53,6 +48,12 @@ export const useBaseApi = (module: string) => {
             method: 'post',
             data
         }),
+        exportData: (data: any) => request({
+            responseType: 'arraybuffer',
+            url: baseUrl + 'export',
+            method: 'post',
+            data
+        }),
         downloadTemplate: () => request({
             responseType: 'arraybuffer',
             url: baseUrl + 'import',

+ 2 - 2
Web/src/components/table/importData.vue

@@ -10,7 +10,7 @@
 			
 			<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-button class="ml10" type="info" icon="ele-Download" v-reclick="3000" @click="() => download()">模板</el-button>
 				</el-col>
 				<el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
 					<el-upload
@@ -21,7 +21,7 @@
 						ref="uploadRef"
 					>
 						<template #trigger>
-							<el-button type="primary" icon="ele-MostlyCloudy" :disable="state.loading">导入</el-button>
+							<el-button type="primary" icon="ele-MostlyCloudy" v-reclick="3000">导入</el-button>
 						</template>
 					</el-upload>
 				</el-col>

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

@@ -16,4 +16,16 @@ export function downloadFile(url: string, filename: string | undefined = undefin
 
     document.body.removeChild(link);
     window.URL.revokeObjectURL(url);
+}
+
+/**
+ * 文件流下载
+ * @param res
+ */
+export function downloadStreamFile(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);
 }

+ 6 - 10
Web/src/views/system/codeGen/index.vue

@@ -105,7 +105,6 @@ const state = reactive({
 
 onMounted(async () => {
 	handleQuery();
-
 	let res = await getAPI(SysCodeGenApi).apiSysCodeGenApplicationNamespacesGet();
 	state.applicationNamespaces = res.data.result as Array<string>;
 });
@@ -180,13 +179,11 @@ const deleConfig = (row: any) => {
 		confirmButtonText: '确定',
 		cancelButtonText: '取消',
 		type: 'warning',
-	})
-		.then(async () => {
+	}).then(async () => {
 			await getAPI(SysCodeGenApi).apiSysCodeGenDeletePost([{ id: row.id }]);
 			handleQuery();
 			ElMessage.success('操作成功');
-		})
-		.catch(() => {});
+	}).catch(() => {});
 };
 
 // 同步生成
@@ -196,13 +193,12 @@ const syncCodeGen = async (row: any) => {
     cancelButtonText: '取消',
     type: 'warning',
   }).then(async () => {
-    await getAPI(SysCodeGenApi).apiSysCodeGenDeletePost([{ id: state.ruleForm.id }]);
-    state.ruleForm.id = undefined;
-    await getAPI(SysCodeGenApi).apiSysCodeGenAddPost(state.ruleForm as AddCodeGenInput);
+    await getAPI(SysCodeGenApi).apiSysCodeGenDeletePost([{ id: row.id }]);
+    row.id = undefined;
+    await getAPI(SysCodeGenApi).apiSysCodeGenAddPost(row);
     handleQuery();
     ElMessage.success('同步成功');
-  })
-  .catch(() => {});
+  }).catch(() => {});
 }
 
 // 开始生成代码

+ 1 - 1
Web/src/views/system/database/component/genEntity.vue

@@ -39,7 +39,7 @@
 			<template #footer>
 				<span class="dialog-footer">
 					<el-button @click="cancel">取 消</el-button>
-					<el-button type="primary" @click="submit">确 定</el-button>
+					<el-button type="primary" v-reclick="3000" @click="submit">确 定</el-button>
 				</span>
 			</template>
 		</el-dialog>

+ 1 - 1
Web/src/views/system/database/component/genSeedData.vue

@@ -37,7 +37,7 @@
 			<template #footer>
 				<span class="dialog-footer">
 					<el-button @click="cancel">取 消</el-button>
-					<el-button type="primary" @click="submit">确 定</el-button>
+					<el-button type="primary" v-reclick="3000" @click="submit">确 定</el-button>
 				</span>
 			</template>
 		</el-dialog>