Procházet zdrojové kódy

Merge branch 'next' of https://gitee.com/zuohuaijun/Admin.NET into next

SW před 1 rokem
rodič
revize
18ad0bd922
92 změnil soubory, kde provedl 5013 přidání a 190 odebrání
  1. 1 0
      Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj
  2. 5 5
      Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
  3. 21 0
      Admin.NET/Admin.NET.Core/Extension/ObjectExtension.cs
  4. 2 13
      Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs
  5. 6 7
      Admin.NET/Admin.NET.Core/Service/Job/DbJobPersistence.cs
  6. 7 15
      Admin.NET/Admin.NET.Core/Service/Job/JobMonitor.cs
  7. 2 2
      Admin.NET/Admin.NET.Core/Service/Logging/SysLogDiffService.cs
  8. 2 2
      Admin.NET/Admin.NET.Core/Service/Logging/SysLogExService.cs
  9. 2 2
      Admin.NET/Admin.NET.Core/Service/Logging/SysLogOpService.cs
  10. 2 2
      Admin.NET/Admin.NET.Core/Service/Logging/SysLogVisService.cs
  11. 18 6
      Admin.NET/Admin.NET.Core/Service/OpenAccess/Dto/OpenAccessInput.cs
  12. 7 13
      Admin.NET/Admin.NET.Core/Service/OpenAccess/SysOpenAccessService.cs
  13. 5 4
      Admin.NET/Admin.NET.Core/Service/Tenant/SysTenantService.cs
  14. 11 0
      Admin.NET/Admin.NET.Core/Util/AdminResultProvider.cs
  15. 2 1
      Admin.NET/Admin.NET.Web.Core/Handlers/JwtHandler.cs
  16. 8 1
      Admin.NET/Admin.NET.Web.Core/Startup.cs
  17. 2 2
      Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/editDialog.vue.vm
  18. 1 1
      Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/index.vue.vm
  19. 7 0
      Admin.NET/Admin.NET.sln
  20. 23 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Admin.NET.Plugin.ApprovalFlow.csproj
  21. 13 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Configuration/ApprovalFlow.json
  22. 19 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Const/ApprovalFlowConst.cs
  23. 53 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Entity/ApprovalFlow.cs
  24. 14 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Enum/FlowTypeEnum.cs
  25. 19 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/GlobalUsings.cs
  26. 46 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Middleware/ApprovalFlowMiddleware.cs
  27. 25 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/SeedData/SysMenuSeedData.cs
  28. 148 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/ApprovalFlowService.cs
  29. 88 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFlowDto.cs
  30. 147 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFlowInput.cs
  31. 88 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFlowOutput.cs
  32. 23 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Startup.cs
  33. 11 0
      Admin.NET/Plugins/Admin.NET.Plugin.GoView/Util/GoViewResultProvider.cs
  34. 1 1
      Admin.NET/Plugins/Admin.NET.Plugin.ReZero/Admin.NET.Plugin.ReZero.csproj
  35. 2 2
      README.md
  36. 10 5
      Web/package.json
  37. 6 0
      Web/postcss.config.js
  38. 15 0
      Web/src/api-services/_approvalFlow/api.ts
  39. 711 0
      Web/src/api-services/_approvalFlow/apis/approval-flow-api.ts
  40. 70 0
      Web/src/api-services/_approvalFlow/base.ts
  41. 83 0
      Web/src/api-services/_approvalFlow/configuration.ts
  42. 18 0
      Web/src/api-services/_approvalFlow/index.ts
  43. 134 0
      Web/src/api-services/_approvalFlow/models/add-approval-flow-input.ts
  44. 69 0
      Web/src/api-services/_approvalFlow/models/admin-result-approval-flow.ts
  45. 70 0
      Web/src/api-services/_approvalFlow/models/admin-result-int64.ts
  46. 71 0
      Web/src/api-services/_approvalFlow/models/admin-result-list-approval-flow-output.ts
  47. 69 0
      Web/src/api-services/_approvalFlow/models/admin-result-sql-sugar-paged-list-approval-flow-output.ts
  48. 94 0
      Web/src/api-services/_approvalFlow/models/approval-flow-input.ts
  49. 142 0
      Web/src/api-services/_approvalFlow/models/approval-flow-output.ts
  50. 150 0
      Web/src/api-services/_approvalFlow/models/approval-flow.ts
  51. 30 0
      Web/src/api-services/_approvalFlow/models/delete-approval-flow-input.ts
  52. 11 0
      Web/src/api-services/_approvalFlow/models/index.ts
  53. 79 0
      Web/src/api-services/_approvalFlow/models/sql-sugar-paged-list-approval-flow-output.ts
  54. 142 0
      Web/src/api-services/_approvalFlow/models/update-approval-flow-input.ts
  55. 20 56
      Web/src/api-services/apis/sys-open-access-api.ts
  56. 69 0
      Web/src/api-services/models/generate-signature-input.ts
  57. 1 0
      Web/src/api-services/models/index.ts
  58. 3 0
      Web/src/assets/main.css
  59. 68 27
      Web/src/components/table/search.vue
  60. 11 1
      Web/src/layout/routerView/iframes.vue
  61. 1 0
      Web/src/main.ts
  62. 67 9
      Web/src/theme/element.scss
  63. 1 1
      Web/src/types/views.d.ts
  64. 80 0
      Web/src/views/approvalFlow/component/LogicFlow/Panel/PanelControl.vue
  65. 14 0
      Web/src/views/approvalFlow/component/LogicFlow/Panel/PanelDataDialog.vue
  66. 114 0
      Web/src/views/approvalFlow/component/LogicFlow/Panel/PanelNode.vue
  67. 48 0
      Web/src/views/approvalFlow/component/LogicFlow/Property/PropertyCommon.vue
  68. 30 0
      Web/src/views/approvalFlow/component/LogicFlow/Property/PropertyDialog.vue
  69. 55 0
      Web/src/views/approvalFlow/component/LogicFlow/Register/Edges/EdgeSql.ts
  70. 79 0
      Web/src/views/approvalFlow/component/LogicFlow/Register/Nodes/NodeEnd.ts
  71. 150 0
      Web/src/views/approvalFlow/component/LogicFlow/Register/Nodes/NodeSql.ts
  72. 64 0
      Web/src/views/approvalFlow/component/LogicFlow/Register/Nodes/NodeStart.ts
  73. 25 0
      Web/src/views/approvalFlow/component/LogicFlow/Register/Nodes/NodeTask.ts
  74. 110 0
      Web/src/views/approvalFlow/component/LogicFlow/Register/Nodes/NodeUser.ts
  75. 9 0
      Web/src/views/approvalFlow/component/LogicFlow/Register/RegisterEdge.ts
  76. 17 0
      Web/src/views/approvalFlow/component/LogicFlow/Register/RegisterNode.ts
  77. 125 0
      Web/src/views/approvalFlow/component/detailDialog.vue
  78. 162 0
      Web/src/views/approvalFlow/component/editDialog.vue
  79. 264 0
      Web/src/views/approvalFlow/component/editFlowDialog.vue
  80. 154 0
      Web/src/views/approvalFlow/component/editFormDialog.vue
  81. 219 0
      Web/src/views/approvalFlow/index.vue
  82. 4 3
      Web/src/views/system/dict/index.vue
  83. 153 0
      Web/src/views/system/openAccess/component/generateSign.vue
  84. 0 5
      Web/src/views/system/openAccess/component/helpView.vue
  85. 9 0
      Web/src/views/system/openAccess/index.vue
  86. 1 1
      Web/src/views/system/print/component/editPrint.vue
  87. 1 3
      Web/src/views/system/print/component/hiprint/index.vue
  88. 15 0
      Web/tailwind.config.js
  89. 1 0
      docker/README.md
  90. 35 0
      docker/app/Configuration/Cache.json
  91. 14 0
      docker/docker-compose.yml
  92. 15 0
      docker/redis/redis.conf

+ 1 - 0
Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj

@@ -26,6 +26,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\Admin.NET.Core\Admin.NET.Core.csproj" />
+    <ProjectReference Include="..\Plugins\Admin.NET.Plugin.ApprovalFlow\Admin.NET.Plugin.ApprovalFlow.csproj" />
     <ProjectReference Include="..\Plugins\Admin.NET.Plugin.DingTalk\Admin.NET.Plugin.DingTalk.csproj" />
     <ProjectReference Include="..\Plugins\Admin.NET.Plugin.GoView\Admin.NET.Plugin.GoView.csproj" />
     <ProjectReference Include="..\Plugins\Admin.NET.Plugin.ReZero\Admin.NET.Plugin.ReZero.csproj" />

+ 5 - 5
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -20,9 +20,9 @@
     <PackageReference Include="AspNet.Security.OAuth.Weixin" Version="6.0.15" />
     <PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
     <PackageReference Include="Elastic.Clients.Elasticsearch" Version="8.13.12" />
-    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.2.36" />
-    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.2.36" />
-    <PackageReference Include="Furion.Pure" Version="4.9.2.36" />
+    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.3" />
+    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.3" />
+    <PackageReference Include="Furion.Pure" Version="4.9.3" />
     <PackageReference Include="IPTools.China" Version="1.6.0" />
     <PackageReference Include="IPTools.International" Version="1.6.0" />
     <PackageReference Include="Lazy.Captcha.Core" Version="2.0.6" />
@@ -32,14 +32,14 @@
     <PackageReference Include="MailKit" Version="4.5.0" />
     <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="6.0.29" />
     <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="6.0.29" />
-    <PackageReference Include="NewLife.Redis" Version="5.6.2024.420-beta0005" />
+    <PackageReference Include="NewLife.Redis" Version="5.6.2024.508" />
     <PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
     <PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.1.9" />
     <PackageReference Include="QRCoder" Version="1.5.1" />
     <PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
     <PackageReference Include="SixLabors.ImageSharp.Web" Version="3.1.2" />
     <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.1.0" />
-    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.2.0" />
+    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.3.2" />
     <PackageReference Include="SqlSugarCore" Version="5.1.4.154" />
     <PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.14" />
     <PackageReference Include="UAParser" Version="3.1.47" />

+ 21 - 0
Admin.NET/Admin.NET.Core/Extension/ObjectExtension.cs

@@ -253,6 +253,27 @@ public static partial class ObjectExtension
         }
     }
 
+    /// <summary>
+    /// 将 string 时间日期格式转换成字符串 如 {yyyy} => 2024
+    /// </summary>
+    /// <param name="str"></param>
+    /// <returns></returns>
+    public static string ParseToDateTimeForRep(this string str)
+    {
+        if (string.IsNullOrWhiteSpace(str))
+            str = $"{DateTime.Now.Year}/{DateTime.Now.Month}/{DateTime.Now.Day}";
+
+        var date = DateTime.Now;
+        var reg = new Regex(@"(\{.+?})");
+        var match = reg.Matches(str);
+        match.ToList().ForEach(u =>
+        {
+            var temp = date.ToString(u.ToString().Substring(1, u.Length - 2));
+            str = str.Replace(u.ToString(), temp);
+        });
+        return str;
+    }
+
     /// <summary>
     /// 是否有值
     /// </summary>

+ 2 - 13
Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs

@@ -264,19 +264,6 @@ public class SysFileService : IDynamicApiController, ITransient
             if (sysFile != null) return sysFile;
         }
 
-        var path = savePath;
-        if (string.IsNullOrWhiteSpace(savePath))
-        {
-            path = _uploadOptions.Path;
-            var reg = new Regex(@"(\{.+?})");
-            var match = reg.Matches(path);
-            match.ToList().ForEach(a =>
-            {
-                var str = DateTime.Now.ToString(a.ToString().Substring(1, a.Length - 2)); // 每天一个目录
-                path = path.Replace(a.ToString(), str);
-            });
-        }
-
         // 验证文件类型
         if (!_uploadOptions.ContentType.Contains(file.ContentType))
             throw Oops.Oh(ErrorCodeEnum.D8001);
@@ -304,6 +291,8 @@ public class SysFileService : IDynamicApiController, ITransient
         if (!VerifyFileExtensionName.IsSameType(file.OpenReadStream(), suffix))
             throw Oops.Oh(ErrorCodeEnum.D8001);
 
+        var path = string.IsNullOrWhiteSpace(savePath) ? _uploadOptions.Path : savePath;
+        path = path.ParseToDateTimeForRep();
         var newFile = new SysFile
         {
             Id = YitIdHelper.NextId(),

+ 6 - 7
Admin.NET/Admin.NET.Core/Service/Job/DbJobPersistence.cs

@@ -27,14 +27,13 @@ public class DbJobPersistence : IJobPersistence
     public async Task<IEnumerable<SchedulerBuilder>> PreloadAsync(CancellationToken stoppingToken)
     {
         using var scope = _serviceScopeFactory.CreateScope();
-        var jobDetailRep = scope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysJobDetail>>();
-        var jobTriggerRep = scope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysJobTrigger>>();
+        var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
         var dynamicJobCompiler = scope.ServiceProvider.GetRequiredService<DynamicJobCompiler>();
 
         // 获取所有定义的作业
         var allJobs = App.EffectiveTypes.ScanToBuilders().ToList();
         // 若数据库不存在任何作业,则直接返回
-        if (!jobDetailRep.IsAny(u => true)) return allJobs;
+        if (!db.Queryable<SysJobDetail>().Any(u => true)) return allJobs;
 
         // 遍历所有定义的作业
         foreach (var schedulerBuilder in allJobs)
@@ -43,14 +42,14 @@ public class DbJobPersistence : IJobPersistence
             var jobBuilder = schedulerBuilder.GetJobBuilder();
 
             // 加载数据库数据
-            var dbDetail = await jobDetailRep.GetFirstAsync(u => u.JobId == jobBuilder.JobId);
+            var dbDetail = await db.Queryable<SysJobDetail>().FirstAsync(u => u.JobId == jobBuilder.JobId);
             if (dbDetail == null) continue;
 
             // 同步数据库数据
             jobBuilder.LoadFrom(dbDetail);
 
             // 获取作业的所有数据库的触发器
-            var dbTriggers = await jobTriggerRep.GetListAsync(u => u.JobId == jobBuilder.JobId);
+            var dbTriggers = await db.Queryable<SysJobTrigger>().Where(u => u.JobId == jobBuilder.JobId).ToListAsync();
             // 遍历所有作业触发器
             foreach (var (_, triggerBuilder) in schedulerBuilder.GetEnumerable())
             {
@@ -74,7 +73,7 @@ public class DbJobPersistence : IJobPersistence
         }
 
         // 获取数据库所有通过脚本创建的作业
-        var allDbScriptJobs = await jobDetailRep.GetListAsync(u => u.CreateType != JobCreateTypeEnum.BuiltIn);
+        var allDbScriptJobs = await db.Queryable<SysJobDetail>().Where(u => u.CreateType != JobCreateTypeEnum.BuiltIn).ToListAsync();
         foreach (var dbDetail in allDbScriptJobs)
         {
             // 动态创建作业
@@ -101,7 +100,7 @@ public class DbJobPersistence : IJobPersistence
             jobBuilder.SetIncludeAnnotations(false);
 
             // 获取作业的所有数据库的触发器加入到作业中
-            var dbTriggers = await jobTriggerRep.GetListAsync(u => u.JobId == jobBuilder.JobId);
+            var dbTriggers = await db.Queryable<SysJobTrigger>().Where(u => u.JobId == jobBuilder.JobId).ToListAsync();
             var triggerBuilders = dbTriggers.Select(u => TriggerBuilder.Create(u.TriggerId).LoadFrom(u).Updated());
             var schedulerBuilder = SchedulerBuilder.Create(jobBuilder, triggerBuilders.ToArray());
 

+ 7 - 15
Admin.NET/Admin.NET.Core/Service/Job/JobMonitor.cs

@@ -9,7 +9,7 @@ namespace Admin.NET.Core.Service;
 /// <summary>
 /// 作业执行监视器
 /// </summary>
-public class JobMonitor : IJobMonitor, IDisposable
+public class JobMonitor : IJobMonitor
 {
     private readonly IEventPublisher _eventPublisher;
     private readonly IServiceScope _serviceScope;
@@ -22,26 +22,18 @@ public class JobMonitor : IJobMonitor, IDisposable
         _eventPublisher = _serviceScope.ServiceProvider.GetRequiredService<IEventPublisher>(); ;
     }
 
+    public Task OnExecutingAsync(JobExecutingContext context, CancellationToken stoppingToken)
+    {
+        return Task.CompletedTask;
+    }
+
     public async Task OnExecutedAsync(JobExecutedContext context, CancellationToken stoppingToken)
     {
         // 将异常作业发送到邮件
         if (await _sysConfigService.GetConfigValue<bool>(CommonConst.SysErrorMail) && context.Exception != null)
         {
-            var errorInfo = $"【{context.Trigger.Description}】出现错误:{context.Exception.InnerException}";
+            var errorInfo = $"【{context.Trigger.Description}】定时任务错误:{context.Exception}";
             await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, errorInfo);
         }
     }
-
-    public Task OnExecutingAsync(JobExecutingContext context, CancellationToken stoppingToken)
-    {
-        return Task.CompletedTask;
-    }
-
-    /// <summary>
-    /// 释放服务作用域
-    /// </summary>
-    public void Dispose()
-    {
-        _serviceScope.Dispose();
-    }
 }

+ 2 - 2
Admin.NET/Admin.NET.Core/Service/Logging/SysLogDiffService.cs

@@ -40,8 +40,8 @@ public class SysLogDiffService : IDynamicApiController, ITransient
     /// <returns></returns>
     [ApiDescriptionSettings(Name = "Clear"), HttpPost]
     [DisplayName("清空差异日志")]
-    public async Task<bool> Clear()
+    public void Clear()
     {
-        return await _sysLogDiffRep.DeleteAsync(u => u.Id > 0);
+        _sysLogDiffRep.AsSugarClient().DbMaintenance.TruncateTable<SysLogDiff>();
     }
 }

+ 2 - 2
Admin.NET/Admin.NET.Core/Service/Logging/SysLogExService.cs

@@ -41,9 +41,9 @@ public class SysLogExService : IDynamicApiController, ITransient
     /// <returns></returns>
     [ApiDescriptionSettings(Name = "Clear"), HttpPost]
     [DisplayName("清空异常日志")]
-    public async Task<bool> Clear()
+    public void Clear()
     {
-        return await _sysLogExRep.DeleteAsync(u => u.Id > 0);
+        _sysLogExRep.AsSugarClient().DbMaintenance.TruncateTable<SysLogEx>();
     }
 
     /// <summary>

+ 2 - 2
Admin.NET/Admin.NET.Core/Service/Logging/SysLogOpService.cs

@@ -41,9 +41,9 @@ public class SysLogOpService : IDynamicApiController, ITransient
     /// <returns></returns>
     [ApiDescriptionSettings(Name = "Clear"), HttpPost]
     [DisplayName("清空操作日志")]
-    public async Task<bool> Clear()
+    public void Clear()
     {
-        return await _sysLogOpRep.DeleteAsync(u => u.Id > 0);
+        _sysLogOpRep.AsSugarClient().DbMaintenance.TruncateTable<SysLogOp>();
     }
 
     /// <summary>

+ 2 - 2
Admin.NET/Admin.NET.Core/Service/Logging/SysLogVisService.cs

@@ -40,8 +40,8 @@ public class SysLogVisService : IDynamicApiController, ITransient
     /// <returns></returns>
     [ApiDescriptionSettings(Name = "Clear"), HttpPost]
     [DisplayName("清空访问日志")]
-    public async Task<bool> Clear()
+    public void Clear()
     {
-        return await _sysLogVisRep.DeleteAsync(u => u.Id > 0);
+        _sysLogVisRep.AsSugarClient().DbMaintenance.TruncateTable<SysLogVis>();
     }
 }

+ 18 - 6
Admin.NET/Admin.NET.Core/Service/OpenAccess/Dto/OpenAccessInput.cs

@@ -48,18 +48,18 @@ public class DeleteOpenAccessInput : BaseIdInput
 
 public class GenerateSignatureInput
 {
-    /// <summary>
-    /// 密钥
-    /// </summary>
-    [Required(ErrorMessage = "密钥不能为空")]
-    public string AppSecret { get; set; }
-
     /// <summary>
     /// 身份标识
     /// </summary>
     [Required(ErrorMessage = "身份标识不能为空")]
     public string AccessKey { get; set; }
 
+    /// <summary>
+    /// 密钥
+    /// </summary>
+    [Required(ErrorMessage = "密钥不能为空")]
+    public string AccessSecret { get; set; }
+
     /// <summary>
     /// 请求方法
     /// </summary>
@@ -70,4 +70,16 @@ public class GenerateSignatureInput
     /// </summary>
     [Required(ErrorMessage = "请求接口地址不能为空")]
     public string Url { get; set; }
+
+    /// <summary>
+    /// 时间戳
+    /// </summary>
+    [Required(ErrorMessage = "时间戳不能为空")]
+    public long Timestamp { get; set; }
+
+    /// <summary>
+    /// 随机数
+    /// </summary>
+    [Required(ErrorMessage = "随机数不能为空")]
+    public string Nonce { get; set; }
 }

+ 7 - 13
Admin.NET/Admin.NET.Core/Service/OpenAccess/SysOpenAccessService.cs

@@ -29,24 +29,18 @@ public class SysOpenAccessService : IDynamicApiController, ITransient
     }
 
     /// <summary>
-    /// 获取生成签名
+    /// 生成签名
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    [DisplayName("获取生成签名")]
-    public string GetGenerateSignature([FromQuery] GenerateSignatureInput input)
+    [DisplayName("生成签名")]
+    public string GenerateSignature(GenerateSignatureInput input)
     {
         // 密钥
-        var appSecretByte = Encoding.UTF8.GetBytes(input.AppSecret);
-        // 时间戳,精确到秒
-        DateTimeOffset currentTime = DateTimeOffset.UtcNow;
-        var timestamp = currentTime.ToUnixTimeSeconds();
-        // 唯一随机数,可以使用guid或者雪花id,以下是sqlsugar提供获取雪花id的方法
-        var nonce = YitIdHelper.NextId();
-        //// 请求方式
-        //var sMethod = method.ToString();
+        var appSecretByte = Encoding.UTF8.GetBytes(input.AccessSecret);
+
         // 拼接参数
-        var parameter = $"{input.Method}&{input.Url}&{input.AccessKey}&{timestamp}&{nonce}";
+        var parameter = $"{input.Method.ToString().ToUpper()}&{input.Url}&{input.AccessKey}&{input.Timestamp}&{input.Nonce}";
         // 使用 HMAC-SHA256 协议创建基于哈希的消息身份验证代码 (HMAC),以appSecretByte 作为密钥,对上面拼接的参数进行计算签名,所得签名进行 Base-64 编码
         using HMAC hmac = new HMACSHA256();
         hmac.Key = appSecretByte;
@@ -173,7 +167,7 @@ public class SysOpenAccessService : IDynamicApiController, ITransient
                 }
                 catch (Exception ex)
                 {
-                    logger.LogError(ex, "开接口身份验证");
+                    logger.LogError(ex, "开接口身份验证");
                     return Task.FromResult("");
                 }
             },

+ 5 - 4
Admin.NET/Admin.NET.Core/Service/Tenant/SysTenantService.cs

@@ -314,11 +314,12 @@ public class SysTenantService : IDynamicApiController, ITransient
     [DisplayName("授权租户管理员角色菜单")]
     public async Task GrantMenu(RoleMenuInput input)
     {
-        var tenantAdminUser = await _sysUserRep.GetFirstAsync(u => u.TenantId == input.Id && u.AccountType == AccountTypeEnum.SysAdmin);
-        if (tenantAdminUser == null) return;
+        // 获取租户管理员角色【sys_admin】
+        var adminRole = await _sysRoleRep.AsQueryable().ClearFilter()
+            .FirstAsync(u => u.Code == CommonConst.SysAdminRole && u.TenantId == input.Id && u.IsDelete == false);
+        if (adminRole == null) return;
 
-        var roleIds = await _sysUserRoleService.GetUserRoleIdList(tenantAdminUser.Id);
-        input.Id = roleIds[0]; // 重置租户管理员角色Id
+        input.Id = adminRole.Id; // 重置租户管理员角色Id
         await _sysRoleMenuService.GrantRoleMenu(input);
     }
 

+ 11 - 0
Admin.NET/Admin.NET.Core/Util/AdminResultProvider.cs

@@ -12,6 +12,17 @@ namespace Admin.NET.Core;
 [UnifyModel(typeof(AdminResult<>))]
 public class AdminResultProvider : IUnifyResultProvider
 {
+    /// <summary>
+    /// JWT 授权异常返回值
+    /// </summary>
+    /// <param name="context"></param>
+    /// <param name="metadata"></param>
+    /// <returns></returns>
+    public IActionResult OnAuthorizeException(DefaultHttpContext context, ExceptionMetadata metadata)
+    {
+        return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors), UnifyContext.GetSerializerSettings(context));
+    }
+
     /// <summary>
     /// 异常返回值
     /// </summary>

+ 2 - 1
Admin.NET/Admin.NET.Web.Core/Handlers/JwtHandler.cs

@@ -30,8 +30,9 @@ namespace Admin.NET.Web.Core
         /// 自动刷新Token
         /// </summary>
         /// <param name="context"></param>
+        /// <param name="httpContext"></param>
         /// <returns></returns>
-        public override async Task HandleAsync(AuthorizationHandlerContext context)
+        public override async Task HandleAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
         {
             // var serviceProvider = context.GetCurrentHttpContext().RequestServices;
             using var serviceScope = _serviceProvider.CreateScope();

+ 8 - 1
Admin.NET/Admin.NET.Web.Core/Startup.cs

@@ -229,7 +229,14 @@ public class Startup : AppStartup
         app.UseClientRateLimiting();
 
         // 任务调度看板
-        app.UseScheduleUI();
+        app.UseScheduleUI(options =>
+        {
+            options.RequestPath = "/schedule";  // 必须以 / 开头且不以 / 结尾
+            options.DisableOnProduction = true; // 生产环境关闭
+            options.DisplayEmptyTriggerJobs = true; // 是否显示空作业触发器的作业
+            options.DisplayHead = false; // 是否显示页头
+            options.DefaultExpandAllJobs = false; // 是否默认展开所有作业
+        });
 
         // 配置Swagger-Knife4UI(路由前缀一致代表独立,不同则代表共存)
         app.UseKnife4UI(options =>

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

@@ -1,9 +1,9 @@
-@{Dictionary<string, int> definedObjects = new Dictionary<string, int>();}
+@{Dictionary<string, int> definedObjects = new Dictionary<string, int>();}
 @{var pkField = Model.TableField.Where(c => c.ColumnKey == "True").FirstOrDefault();}
 @{string pkFieldName = LowerFirstLetter(pkField.PropertyName);}
 <template>
 	<div class="@(@Model.LowerClassName)-container">
-		<el-dialog v-model="isShowDialog" :width="800" draggable="">
+		<el-dialog v-model="isShowDialog" :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>-->

+ 1 - 1
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/index.vue.vm

@@ -362,7 +362,7 @@
   handleQuery();
 </script>
 <style scoped>
-:deep(.el-ipnut),
+:deep(.el-input),
 :deep(.el-select),
 :deep(.el-input-number) {
 	width: 100%;

+ 7 - 0
Admin.NET/Admin.NET.sln

@@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Plugin.DingTalk",
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Plugin.ReZero", "Plugins\Admin.NET.Plugin.ReZero\Admin.NET.Plugin.ReZero.csproj", "{04AB2E76-DE8B-4EFD-9F48-F8D4C0993106}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Plugin.ApprovalFlow", "Plugins\Admin.NET.Plugin.ApprovalFlow\Admin.NET.Plugin.ApprovalFlow.csproj", "{902A91A7-5EF0-4A63-BC2C-9B783DC00880}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -64,6 +66,10 @@ Global
 		{04AB2E76-DE8B-4EFD-9F48-F8D4C0993106}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{04AB2E76-DE8B-4EFD-9F48-F8D4C0993106}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{04AB2E76-DE8B-4EFD-9F48-F8D4C0993106}.Release|Any CPU.Build.0 = Release|Any CPU
+		{902A91A7-5EF0-4A63-BC2C-9B783DC00880}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{902A91A7-5EF0-4A63-BC2C-9B783DC00880}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{902A91A7-5EF0-4A63-BC2C-9B783DC00880}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{902A91A7-5EF0-4A63-BC2C-9B783DC00880}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -73,6 +79,7 @@ Global
 		{48EFC3A6-BDC0-4D05-819A-B1FB927FA4C8} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
 		{F6A002AD-CF7F-4771-8597-F12A50A93DAA} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
 		{04AB2E76-DE8B-4EFD-9F48-F8D4C0993106} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
+		{902A91A7-5EF0-4A63-BC2C-9B783DC00880} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {5CD801D7-984A-4F5C-8FA2-211B7A5EA9F3}

+ 23 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Admin.NET.Plugin.ApprovalFlow.csproj

@@ -0,0 +1,23 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <NoWarn>1701;1702;1591;8632</NoWarn>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>disable</Nullable>
+    <GenerateDocumentationFile>True</GenerateDocumentationFile>
+    <Copyright>© Admin.NET</Copyright>
+    <Description>Admin.NET 通用权限开发平台</Description>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <None Update="Configuration\**">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Admin.NET.Core\Admin.NET.Core.csproj" />
+  </ItemGroup>
+	
+</Project>

+ 13 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Configuration/ApprovalFlow.json

@@ -0,0 +1,13 @@
+{
+  "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
+
+  "[openapi:审批流程]": {
+    "Group": "审批流程",
+    "Title": "审批流程",
+    "Description": "对业务实体数据的增删改操作进行流程审批。",
+    "Version": "1.0.0",
+    "Order": 100
+  },
+  "ApprovalFlow": {
+  }
+}

+ 19 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Const/ApprovalFlowConst.cs

@@ -0,0 +1,19 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.ApprovalFlow;
+
+/// <summary>
+/// 审批流程相关常量
+/// </summary>
+[Const("审批流程相关常量")]
+public class ApprovalFlowConst
+{
+    /// <summary>
+    /// API分组名称
+    /// </summary>
+    public const string GroupName = "审批流程";
+}

+ 53 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Entity/ApprovalFlow.cs

@@ -0,0 +1,53 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.ApprovalFlow;
+
+/// <summary>
+/// 审批流程信息表
+/// </summary>
+[SugarTable(null, "审批流程信息表")]
+public class ApprovalFlow : EntityBaseData
+{
+    /// <summary>
+    /// 编号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "编号", Length = 32)]
+    [MaxLength(32)]
+    public string? Code { get; set; }
+
+    /// <summary>
+    /// 名称
+    /// </summary>
+    [SugarColumn(ColumnDescription = "名称", Length = 32)]
+    [MaxLength(32)]
+    public string Name { get; set; }
+
+    /// <summary>
+    /// 表单
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? FormJson { get; set; }
+
+    /// <summary>
+    /// 流程
+    /// </summary>
+    [SugarColumn(ColumnDescription = "流程", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? FlowJson { get; set; }
+
+    /// <summary>
+    /// 状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "状态")]
+    public int? Status { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    [SugarColumn(ColumnDescription = "备注", Length = 256)]
+    [MaxLength(256)]
+    public string? Remark { get; set; }
+}

+ 14 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Enum/FlowTypeEnum.cs

@@ -0,0 +1,14 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.ApprovalFlow;
+
+/// <summary>
+/// 流程类型枚举
+/// </summary>
+public enum FlowTypeEnum
+{
+}

+ 19 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/GlobalUsings.cs

@@ -0,0 +1,19 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+global using Admin.NET.Core;
+global using Furion;
+global using Furion.DependencyInjection;
+global using Furion.DynamicApiController;
+global using Furion.FriendlyException;
+global using Mapster;
+global using Microsoft.AspNetCore.Mvc;
+global using Microsoft.Extensions.DependencyInjection;
+global using SqlSugar;
+global using System;
+global using System.Collections.Generic;
+global using System.ComponentModel.DataAnnotations;
+global using System.Threading.Tasks;

+ 46 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Middleware/ApprovalFlowMiddleware.cs

@@ -0,0 +1,46 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+
+namespace Admin.NET.Plugin.ApprovalFlow;
+
+/// <summary>
+/// 扩展审批流中间件
+/// </summary>
+public static class ApprovalFlowMiddlewareExtensions
+{
+    /// <summary>
+    /// 使用审批流
+    /// </summary>
+    /// <param name="builder"></param>
+    /// <returns></returns>
+    public static IApplicationBuilder UseApprovalFlow(this IApplicationBuilder builder)
+    {
+        return builder.UseMiddleware<ApprovalFlowMiddleware>();
+    }
+}
+
+/// <summary>
+/// 审批流中间件
+/// </summary>
+public class ApprovalFlowMiddleware
+{
+    private readonly RequestDelegate _next;
+
+    public ApprovalFlowMiddleware(RequestDelegate next)
+    {
+        _next = next;
+    }
+
+    public async Task Invoke(HttpContext context)
+    {
+        //await App.GetService<SysApprovalService>().MatchApproval(context);
+
+        await _next.Invoke(context);
+    }
+}

+ 25 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/SeedData/SysMenuSeedData.cs

@@ -0,0 +1,25 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.ApprovalFlow;
+
+/// <summary>
+/// 审批流程菜单表种子数据
+/// </summary>
+public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
+{
+    /// <summary>
+    /// 种子数据
+    /// </summary>
+    /// <returns></returns>
+    public IEnumerable<SysMenu> HasData()
+    {
+        return new[]
+        {
+            new SysMenu{ Id=1320000000001, Pid=1310000000301, Title="审批流程", Path="/platform/approvalFlow", Name="approvalFlow", Component="/approvalFlow/index", Icon="ele-Help", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=2000 },
+        };
+    }
+}

+ 148 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/ApprovalFlowService.cs

@@ -0,0 +1,148 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Microsoft.AspNetCore.Http;
+
+namespace Admin.NET.Plugin.ApprovalFlow.Service;
+
+/// <summary>
+/// 审批流程服务
+/// </summary>
+[ApiDescriptionSettings(ApprovalFlowConst.GroupName, Order = 100)]
+public class ApprovalFlowService : IDynamicApiController, ITransient
+{
+    private readonly SqlSugarRepository<ApprovalFlow> _approvalFlowRep;
+
+    public ApprovalFlowService(SqlSugarRepository<ApprovalFlow> approvalFlowRep)
+    {
+        _approvalFlowRep = approvalFlowRep;
+    }
+
+    /// <summary>
+    /// 分页查询审批流
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Page")]
+    public async Task<SqlSugarPagedList<ApprovalFlowOutput>> Page(ApprovalFlowInput input)
+    {
+        return await _approvalFlowRep.AsQueryable()
+            .WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), u => u.Code.Contains(input.SearchKey.Trim()) || u.Name.Contains(input.SearchKey.Trim()) || u.Remark.Contains(input.SearchKey.Trim()))
+            .WhereIF(!string.IsNullOrWhiteSpace(input.Code), u => u.Code.Contains(input.Code.Trim()))
+            .WhereIF(!string.IsNullOrWhiteSpace(input.Name), u => u.Name.Contains(input.Name.Trim()))
+            .WhereIF(!string.IsNullOrWhiteSpace(input.Remark), u => u.Remark.Contains(input.Remark.Trim()))
+            .Select<ApprovalFlowOutput>()
+            .ToPagedListAsync(input.Page, input.PageSize);
+    }
+
+    /// <summary>
+    /// 增加审批流
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "Add"), HttpPost]
+    public async Task<long> Add(AddApprovalFlowInput input)
+    {
+        var entity = input.Adapt<ApprovalFlow>();
+        if (input.Code == null)
+        {
+            entity.Code = await LastCode("");
+        }
+        await _approvalFlowRep.InsertAsync(entity);
+        return entity.Id;
+    }
+
+    /// <summary>
+    /// 更新审批流
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "Update"), HttpPost]
+    public async Task Update(UpdateApprovalFlowInput input)
+    {
+        var entity = input.Adapt<ApprovalFlow>();
+        await _approvalFlowRep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
+    }
+
+    /// <summary>
+    /// 删除审批流
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "Delete"), HttpPost]
+    public async Task Delete(DeleteApprovalFlowInput input)
+    {
+        var entity = await _approvalFlowRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        await _approvalFlowRep.FakeDeleteAsync(entity);  // 假删除
+    }
+
+    /// <summary>
+    /// 获取审批流
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    public async Task<ApprovalFlow> GetDetail([FromQuery] QueryByIdApprovalFlowInput input)
+    {
+        return await _approvalFlowRep.GetFirstAsync(u => u.Id == input.Id);
+    }
+
+    /// <summary>
+    /// 根据编码获取审批流信息
+    /// </summary>
+    /// <param name="code"></param>
+    /// <returns></returns>
+    public async Task<ApprovalFlow> GetInfo([FromQuery] string code)
+    {
+        return await _approvalFlowRep.GetFirstAsync(u => u.Code == code);
+    }
+
+    /// <summary>
+    /// 获取审批流列表
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    public async Task<List<ApprovalFlowOutput>> GetList([FromQuery] ApprovalFlowInput input)
+    {
+        return await _approvalFlowRep.AsQueryable().Select<ApprovalFlowOutput>().ToListAsync();
+    }
+
+    /// <summary>
+    /// 获取今天创建的最大编号
+    /// </summary>
+    /// <param name="prefix"></param>
+    /// <returns></returns>
+    private async Task<string> LastCode(string prefix)
+    {
+        var today = DateTime.Now.Date;
+        var count = await _approvalFlowRep.AsQueryable().Where(u => u.CreateTime >= today).CountAsync();
+        return prefix + DateTime.Now.ToString("yyMMdd") + string.Format("{0:d2}", count + 1);
+    }
+
+    /// <summary>
+    /// 匹配审批流程
+    /// </summary>
+    /// <param name="context"></param>
+    /// <returns></returns>
+    [NonAction]
+    public async Task MatchApproval(HttpContext context)
+    {
+        var request = context.Request;
+        var response = context.Response;
+
+        var path = request.Path.ToString().Split("/");
+
+        var method = request.Method;
+        var qs = request.QueryString;
+        var h = request.Headers;
+        var b = request.Body;
+
+        var requestHeaders = request.Headers;
+        var responseHeaders = response.Headers;
+
+        await Task.CompletedTask;
+    }
+}

+ 88 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFlowDto.cs

@@ -0,0 +1,88 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.ApprovalFlow.Service;
+
+/// <summary>
+/// 审批流输出参数
+/// </summary>
+public class ApprovalFlowDto
+{
+    /// <summary>
+    /// 主键Id
+    /// </summary>
+    public long Id { get; set; }
+
+    /// <summary>
+    /// 编号
+    /// </summary>
+    public string? Code { get; set; }
+
+    /// <summary>
+    /// 名称
+    /// </summary>
+    public string? Name { get; set; }
+
+    /// <summary>
+    /// 表单
+    /// </summary>
+    public string? FormJson { get; set; }
+
+    /// <summary>
+    /// 流程
+    /// </summary>
+    public string? FlowJson { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string? Remark { get; set; }
+
+    /// <summary>
+    /// 创建时间
+    /// </summary>
+    public DateTime? CreateTime { get; set; }
+
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    public DateTime? UpdateTime { get; set; }
+
+    /// <summary>
+    /// 创建者Id
+    /// </summary>
+    public long? CreateUserId { get; set; }
+
+    /// <summary>
+    /// 创建者姓名
+    /// </summary>
+    public string? CreateUserName { get; set; }
+
+    /// <summary>
+    /// 修改者Id
+    /// </summary>
+    public long? UpdateUserId { get; set; }
+
+    /// <summary>
+    /// 修改者姓名
+    /// </summary>
+    public string? UpdateUserName { get; set; }
+
+    /// <summary>
+    /// 创建者部门Id
+    /// </summary>
+    public long? CreateOrgId { get; set; }
+
+    /// <summary>
+    /// 创建者部门名称
+    /// </summary>
+    public string? CreateOrgName { get; set; }
+
+    /// <summary>
+    /// 软删除
+    /// </summary>
+    public bool IsDelete { get; set; }
+}

+ 147 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFlowInput.cs

@@ -0,0 +1,147 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.ApprovalFlow.Service;
+
+/// <summary>
+/// 审批流基础输入参数
+/// </summary>
+public class ApprovalFlowBaseInput
+{
+    /// <summary>
+    /// 编号
+    /// </summary>
+    public virtual string? Code { get; set; }
+
+    /// <summary>
+    /// 名称
+    /// </summary>
+    public virtual string? Name { get; set; }
+
+    /// <summary>
+    /// 表单
+    /// </summary>
+    public virtual string? FormJson { get; set; }
+
+    /// <summary>
+    /// 流程
+    /// </summary>
+    public virtual string? FlowJson { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public virtual string? Remark { get; set; }
+
+    /// <summary>
+    /// 创建时间
+    /// </summary>
+    public virtual DateTime? CreateTime { get; set; }
+
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    public virtual DateTime? UpdateTime { get; set; }
+
+    /// <summary>
+    /// 创建者Id
+    /// </summary>
+    public virtual long? CreateUserId { get; set; }
+
+    /// <summary>
+    /// 创建者姓名
+    /// </summary>
+    public virtual string? CreateUserName { get; set; }
+
+    /// <summary>
+    /// 修改者Id
+    /// </summary>
+    public virtual long? UpdateUserId { get; set; }
+
+    /// <summary>
+    /// 修改者姓名
+    /// </summary>
+    public virtual string? UpdateUserName { get; set; }
+
+    /// <summary>
+    /// 创建者部门Id
+    /// </summary>
+    public virtual long? CreateOrgId { get; set; }
+
+    /// <summary>
+    /// 创建者部门名称
+    /// </summary>
+    public virtual string? CreateOrgName { get; set; }
+
+    /// <summary>
+    /// 软删除
+    /// </summary>
+    public virtual bool IsDelete { get; set; }
+}
+
+/// <summary>
+/// 审批流分页查询输入参数
+/// </summary>
+public class ApprovalFlowInput : BasePageInput
+{
+    /// <summary>
+    /// 关键字查询
+    /// </summary>
+    public string? SearchKey { get; set; }
+
+    /// <summary>
+    /// 编号
+    /// </summary>
+    public string? Code { get; set; }
+
+    /// <summary>
+    /// 名称
+    /// </summary>
+    public string? Name { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string? Remark { get; set; }
+}
+
+/// <summary>
+/// 审批流增加输入参数
+/// </summary>
+public class AddApprovalFlowInput : ApprovalFlowBaseInput
+{
+    /// <summary>
+    /// 软删除
+    /// </summary>
+    [Required(ErrorMessage = "软删除不能为空")]
+    public override bool IsDelete { get; set; }
+}
+
+/// <summary>
+/// 审批流删除输入参数
+/// </summary>
+public class DeleteApprovalFlowInput : BaseIdInput
+{
+}
+
+/// <summary>
+/// 审批流更新输入参数
+/// </summary>
+public class UpdateApprovalFlowInput : ApprovalFlowBaseInput
+{
+    /// <summary>
+    /// 主键Id
+    /// </summary>
+    [Required(ErrorMessage = "主键Id不能为空")]
+    public long Id { get; set; }
+}
+
+/// <summary>
+/// 审批流主键查询输入参数
+/// </summary>
+public class QueryByIdApprovalFlowInput : DeleteApprovalFlowInput
+{
+}

+ 88 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFlowOutput.cs

@@ -0,0 +1,88 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.ApprovalFlow.Service;
+
+/// <summary>
+/// 审批流输出参数
+/// </summary>
+public class ApprovalFlowOutput
+{
+    /// <summary>
+    /// 主键Id
+    /// </summary>
+    public long Id { get; set; }
+
+    /// <summary>
+    /// 编号
+    /// </summary>
+    public string? Code { get; set; }
+
+    /// <summary>
+    /// 名称
+    /// </summary>
+    public string? Name { get; set; }
+
+    /// <summary>
+    /// 表单
+    /// </summary>
+    public string? FormJson { get; set; }
+
+    /// <summary>
+    /// 流程
+    /// </summary>
+    public string? FlowJson { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string? Remark { get; set; }
+
+    /// <summary>
+    /// 创建时间
+    /// </summary>
+    public DateTime? CreateTime { get; set; }
+
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    public DateTime? UpdateTime { get; set; }
+
+    /// <summary>
+    /// 创建者Id
+    /// </summary>
+    public long? CreateUserId { get; set; }
+
+    /// <summary>
+    /// 创建者姓名
+    /// </summary>
+    public string? CreateUserName { get; set; }
+
+    /// <summary>
+    /// 修改者Id
+    /// </summary>
+    public long? UpdateUserId { get; set; }
+
+    /// <summary>
+    /// 修改者姓名
+    /// </summary>
+    public string? UpdateUserName { get; set; }
+
+    /// <summary>
+    /// 创建者部门Id
+    /// </summary>
+    public long? CreateOrgId { get; set; }
+
+    /// <summary>
+    /// 创建者部门名称
+    /// </summary>
+    public string? CreateOrgName { get; set; }
+
+    /// <summary>
+    /// 软删除
+    /// </summary>
+    public bool IsDelete { get; set; }
+}

+ 23 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Startup.cs

@@ -0,0 +1,23 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+
+namespace Admin.NET.Plugin.ApprovalFlow;
+
+[AppStartup(100)]
+public class Startup : AppStartup
+{
+    public void ConfigureServices(IServiceCollection services)
+    {
+    }
+
+    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+    {
+        app.UseApprovalFlow();
+    }
+}

+ 11 - 0
Admin.NET/Plugins/Admin.NET.Plugin.GoView/Util/GoViewResultProvider.cs

@@ -12,6 +12,17 @@ namespace Admin.NET.Plugin.GoView;
 [UnifyModel(typeof(GoViewResult<>))]
 public class GoViewResultProvider : IUnifyResultProvider
 {
+    /// <summary>
+    /// JWT 授权异常返回值
+    /// </summary>
+    /// <param name="context"></param>
+    /// <param name="metadata"></param>
+    /// <returns></returns>
+    public IActionResult OnAuthorizeException(DefaultHttpContext context, ExceptionMetadata metadata)
+    {
+        return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors));
+    }
+
     /// <summary>
     /// 异常返回值
     /// </summary>

+ 1 - 1
Admin.NET/Plugins/Admin.NET.Plugin.ReZero/Admin.NET.Plugin.ReZero.csproj

@@ -24,7 +24,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="Rezero.Api" Version="1.0.39" />
+    <PackageReference Include="Rezero.Api" Version="1.0.46" />
   </ItemGroup>  
 
   <ItemGroup>

+ 2 - 2
README.md

@@ -10,9 +10,9 @@
 </div>
 
 ## 🎁框架介绍
-基于 .NET6 (Furion/SqlSugar) 实现的通用权限开发框架,前端采用 Vue3/Element-plus,代码简洁、易扩展。整合最新技术,模块插件式开发,前后端分离,开箱即用。集成多租户、缓存、数据校验、鉴权、事件总线、动态API、通讯、远程请求、任务调度、打印等众多黑科技。
+基于 .NET6 (Furion/SqlSugar) 实现的通用权限开发框架,前端采用 Vue3+Element-plus+Vite5,整合众多优秀技术和框架,模块插件式开发。集成多租户、缓存、数据校验、鉴权、事件总线、动态API、通讯、远程请求、任务调度、打印等众多黑科技。代码结构简单清晰,注释详尽,易于上手与二次开发,即便是复杂业务逻辑也能迅速实现,真正实现“开箱即用”。
 
-前后端分离架构模式,内置国密算法(SM2/SM4),软件层面完全符合等保测评和信创要求,同时实现国产化机型、操作系统(麒麟)、中间件、数据库(达梦、人大金仓等)等适配!
+面向中小企业快速开发平台框架,框架采用主流技术开发设计,前后端分离架构模式。完美适配国产化软硬件环境,支持国产中间件、国产数据库、麒麟操作系统、Windows、Linux部署使用;集成国密加解密插件,使用SM2、SM3、SM4等国密算法进行签名、数据完整性保护;软件层面全面遵循等级保护测评要求,完全符合等保、密评要求。
 
 ```
 超高人气的框架(Furion)配合高性能超简单的ORM(SqlSugar)加持,阅历痛点,相见恨晚!让 .NET 开发更简单,更通用,更流行!

+ 10 - 5
Web/package.json

@@ -12,6 +12,8 @@
 	},
 	"dependencies": {
 		"@element-plus/icons-vue": "^2.3.1",
+		"@logicflow/core": "^1.2.27",
+		"@logicflow/extension": "^1.2.27",
 		"@microsoft/signalr": "^8.0.0",
 		"@vue-office/docx": "^1.6.1",
 		"@vue-office/excel": "^1.7.8",
@@ -46,7 +48,7 @@
 		"splitpanes": "^3.1.5",
 		"vcrontab-3": "^3.3.22",
 		"vform3-builds": "^3.0.10",
-		"vue": "^3.4.26",
+		"vue": "^3.4.27",
 		"vue-clipboard3": "^2.0.0",
 		"vue-demi": "^0.14.7",
 		"vue-grid-layout": "3.0.0-beta1",
@@ -62,21 +64,24 @@
 	"devDependencies": {
 		"@plugin-web-update-notification/vite": "^1.7.1",
 		"@types/lodash-es": "^4.17.12",
-		"@types/node": "^20.12.8",
+		"@types/node": "^20.12.11",
 		"@types/nprogress": "^0.2.3",
 		"@types/sortablejs": "^1.15.8",
 		"@typescript-eslint/eslint-plugin": "^7.8.0",
 		"@typescript-eslint/parser": "^7.8.0",
 		"@vitejs/plugin-vue": "^5.0.4",
 		"@vitejs/plugin-vue-jsx": "^3.1.0",
-		"@vue/compiler-sfc": "^3.4.26",
+		"@vue/compiler-sfc": "^3.4.27",
+		"autoprefixer": "^10.4.19",
 		"code-inspector-plugin": "^0.13.0",
 		"eslint": "^9.2.0",
-		"eslint-plugin-vue": "^9.25.0",
+		"eslint-plugin-vue": "^9.26.0",
 		"less": "^4.2.0",
+		"postcss": "^8.4.38",
 		"prettier": "^3.2.5",
 		"rollup-plugin-visualizer": "^5.12.0",
-		"sass": "^1.76.0",
+		"sass": "^1.77.1",
+		"tailwindcss": "^3.4.3",
 		"terser": "^5.31.0",
 		"typescript": "^5.4.5",
 		"vite": "^5.2.11",

+ 6 - 0
Web/postcss.config.js

@@ -0,0 +1,6 @@
+module.exports = {
+	plugins: {
+		tailwindcss: {},
+		autoprefixer: {},
+	},
+};

+ 15 - 0
Web/src/api-services/_approvalFlow/api.ts

@@ -0,0 +1,15 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+export * from './apis/approval-flow-api';
+

+ 711 - 0
Web/src/api-services/_approvalFlow/apis/approval-flow-api.ts

@@ -0,0 +1,711 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import globalAxios, { AxiosResponse, AxiosInstance, AxiosRequestConfig } from 'axios';
+import { Configuration } from '../configuration';
+// Some imports not used depending on template conditions
+// @ts-ignore
+import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../../base';
+import { AddApprovalFlowInput } from '../models';
+import { AdminResultApprovalFlow } from '../models';
+import { AdminResultInt64 } from '../models';
+import { AdminResultListApprovalFlowOutput } from '../models';
+import { AdminResultSqlSugarPagedListApprovalFlowOutput } from '../models';
+import { ApprovalFlowInput } from '../models';
+import { DeleteApprovalFlowInput } from '../models';
+import { UpdateApprovalFlowInput } from '../models';
+/**
+ * ApprovalFlowApi - axios parameter creator
+ * @export
+ */
+export const ApprovalFlowApiAxiosParamCreator = function (configuration?: Configuration) {
+    return {
+        /**
+         * 
+         * @summary 增加审批流
+         * @param {AddApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiApprovalFlowAddPost: async (body?: AddApprovalFlowInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/approvalFlow/add`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 删除审批流
+         * @param {DeleteApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiApprovalFlowDeletePost: async (body?: DeleteApprovalFlowInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/approvalFlow/delete`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 获取审批流
+         * @param {number} id 主键Id
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiApprovalFlowDetailGet: async (id: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'id' is not null or undefined
+            if (id === null || id === undefined) {
+                throw new RequiredError('id','Required parameter id was null or undefined when calling apiApprovalFlowDetailGet.');
+            }
+            const localVarPath = `/api/approvalFlow/detail`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            if (id !== undefined) {
+                localVarQueryParameter['Id'] = id;
+            }
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 根据编码获取审批流信息
+         * @param {string} [code] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiApprovalFlowInfoGet: async (code?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/approvalFlow/info`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            if (code !== undefined) {
+                localVarQueryParameter['code'] = code;
+            }
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 获取审批流列表
+         * @param {string} [searchKey] 关键字查询
+         * @param {string} [code] 编号
+         * @param {string} [name] 名称
+         * @param {string} [remark] 备注
+         * @param {number} [page] 当前页码
+         * @param {number} [pageSize] 页码容量
+         * @param {string} [field] 排序字段
+         * @param {string} [order] 排序方向
+         * @param {string} [descStr] 降序排序
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiApprovalFlowListGet: async (searchKey?: string, code?: string, name?: string, remark?: string, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/approvalFlow/list`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            if (searchKey !== undefined) {
+                localVarQueryParameter['SearchKey'] = searchKey;
+            }
+
+            if (code !== undefined) {
+                localVarQueryParameter['Code'] = code;
+            }
+
+            if (name !== undefined) {
+                localVarQueryParameter['Name'] = name;
+            }
+
+            if (remark !== undefined) {
+                localVarQueryParameter['Remark'] = remark;
+            }
+
+            if (page !== undefined) {
+                localVarQueryParameter['Page'] = page;
+            }
+
+            if (pageSize !== undefined) {
+                localVarQueryParameter['PageSize'] = pageSize;
+            }
+
+            if (field !== undefined) {
+                localVarQueryParameter['Field'] = field;
+            }
+
+            if (order !== undefined) {
+                localVarQueryParameter['Order'] = order;
+            }
+
+            if (descStr !== undefined) {
+                localVarQueryParameter['DescStr'] = descStr;
+            }
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 分页查询审批流
+         * @param {ApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiApprovalFlowPagePost: async (body?: ApprovalFlowInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/approvalFlow/page`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 更新审批流
+         * @param {UpdateApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiApprovalFlowUpdatePost: async (body?: UpdateApprovalFlowInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/approvalFlow/update`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+    }
+};
+
+/**
+ * ApprovalFlowApi - functional programming interface
+ * @export
+ */
+export const ApprovalFlowApiFp = function(configuration?: Configuration) {
+    return {
+        /**
+         * 
+         * @summary 增加审批流
+         * @param {AddApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowAddPost(body?: AddApprovalFlowInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultInt64>>> {
+            const localVarAxiosArgs = await ApprovalFlowApiAxiosParamCreator(configuration).apiApprovalFlowAddPost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 删除审批流
+         * @param {DeleteApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowDeletePost(body?: DeleteApprovalFlowInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await ApprovalFlowApiAxiosParamCreator(configuration).apiApprovalFlowDeletePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 获取审批流
+         * @param {number} id 主键Id
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowDetailGet(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultApprovalFlow>>> {
+            const localVarAxiosArgs = await ApprovalFlowApiAxiosParamCreator(configuration).apiApprovalFlowDetailGet(id, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 根据编码获取审批流信息
+         * @param {string} [code] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowInfoGet(code?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultApprovalFlow>>> {
+            const localVarAxiosArgs = await ApprovalFlowApiAxiosParamCreator(configuration).apiApprovalFlowInfoGet(code, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 获取审批流列表
+         * @param {string} [searchKey] 关键字查询
+         * @param {string} [code] 编号
+         * @param {string} [name] 名称
+         * @param {string} [remark] 备注
+         * @param {number} [page] 当前页码
+         * @param {number} [pageSize] 页码容量
+         * @param {string} [field] 排序字段
+         * @param {string} [order] 排序方向
+         * @param {string} [descStr] 降序排序
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowListGet(searchKey?: string, code?: string, name?: string, remark?: string, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultListApprovalFlowOutput>>> {
+            const localVarAxiosArgs = await ApprovalFlowApiAxiosParamCreator(configuration).apiApprovalFlowListGet(searchKey, code, name, remark, page, pageSize, field, order, descStr, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 分页查询审批流
+         * @param {ApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowPagePost(body?: ApprovalFlowInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSqlSugarPagedListApprovalFlowOutput>>> {
+            const localVarAxiosArgs = await ApprovalFlowApiAxiosParamCreator(configuration).apiApprovalFlowPagePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 更新审批流
+         * @param {UpdateApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowUpdatePost(body?: UpdateApprovalFlowInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await ApprovalFlowApiAxiosParamCreator(configuration).apiApprovalFlowUpdatePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+    }
+};
+
+/**
+ * ApprovalFlowApi - factory interface
+ * @export
+ */
+export const ApprovalFlowApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
+    return {
+        /**
+         * 
+         * @summary 增加审批流
+         * @param {AddApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowAddPost(body?: AddApprovalFlowInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultInt64>> {
+            return ApprovalFlowApiFp(configuration).apiApprovalFlowAddPost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 删除审批流
+         * @param {DeleteApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowDeletePost(body?: DeleteApprovalFlowInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return ApprovalFlowApiFp(configuration).apiApprovalFlowDeletePost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 获取审批流
+         * @param {number} id 主键Id
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowDetailGet(id: number, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultApprovalFlow>> {
+            return ApprovalFlowApiFp(configuration).apiApprovalFlowDetailGet(id, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 根据编码获取审批流信息
+         * @param {string} [code] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowInfoGet(code?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultApprovalFlow>> {
+            return ApprovalFlowApiFp(configuration).apiApprovalFlowInfoGet(code, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 获取审批流列表
+         * @param {string} [searchKey] 关键字查询
+         * @param {string} [code] 编号
+         * @param {string} [name] 名称
+         * @param {string} [remark] 备注
+         * @param {number} [page] 当前页码
+         * @param {number} [pageSize] 页码容量
+         * @param {string} [field] 排序字段
+         * @param {string} [order] 排序方向
+         * @param {string} [descStr] 降序排序
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowListGet(searchKey?: string, code?: string, name?: string, remark?: string, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultListApprovalFlowOutput>> {
+            return ApprovalFlowApiFp(configuration).apiApprovalFlowListGet(searchKey, code, name, remark, page, pageSize, field, order, descStr, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 分页查询审批流
+         * @param {ApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowPagePost(body?: ApprovalFlowInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSqlSugarPagedListApprovalFlowOutput>> {
+            return ApprovalFlowApiFp(configuration).apiApprovalFlowPagePost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 更新审批流
+         * @param {UpdateApprovalFlowInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiApprovalFlowUpdatePost(body?: UpdateApprovalFlowInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return ApprovalFlowApiFp(configuration).apiApprovalFlowUpdatePost(body, options).then((request) => request(axios, basePath));
+        },
+    };
+};
+
+/**
+ * ApprovalFlowApi - object-oriented interface
+ * @export
+ * @class ApprovalFlowApi
+ * @extends {BaseAPI}
+ */
+export class ApprovalFlowApi extends BaseAPI {
+    /**
+     * 
+     * @summary 增加审批流
+     * @param {AddApprovalFlowInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof ApprovalFlowApi
+     */
+    public async apiApprovalFlowAddPost(body?: AddApprovalFlowInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultInt64>> {
+        return ApprovalFlowApiFp(this.configuration).apiApprovalFlowAddPost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 删除审批流
+     * @param {DeleteApprovalFlowInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof ApprovalFlowApi
+     */
+    public async apiApprovalFlowDeletePost(body?: DeleteApprovalFlowInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return ApprovalFlowApiFp(this.configuration).apiApprovalFlowDeletePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 获取审批流
+     * @param {number} id 主键Id
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof ApprovalFlowApi
+     */
+    public async apiApprovalFlowDetailGet(id: number, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultApprovalFlow>> {
+        return ApprovalFlowApiFp(this.configuration).apiApprovalFlowDetailGet(id, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 根据编码获取审批流信息
+     * @param {string} [code] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof ApprovalFlowApi
+     */
+    public async apiApprovalFlowInfoGet(code?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultApprovalFlow>> {
+        return ApprovalFlowApiFp(this.configuration).apiApprovalFlowInfoGet(code, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 获取审批流列表
+     * @param {string} [searchKey] 关键字查询
+     * @param {string} [code] 编号
+     * @param {string} [name] 名称
+     * @param {string} [remark] 备注
+     * @param {number} [page] 当前页码
+     * @param {number} [pageSize] 页码容量
+     * @param {string} [field] 排序字段
+     * @param {string} [order] 排序方向
+     * @param {string} [descStr] 降序排序
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof ApprovalFlowApi
+     */
+    public async apiApprovalFlowListGet(searchKey?: string, code?: string, name?: string, remark?: string, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultListApprovalFlowOutput>> {
+        return ApprovalFlowApiFp(this.configuration).apiApprovalFlowListGet(searchKey, code, name, remark, page, pageSize, field, order, descStr, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 分页查询审批流
+     * @param {ApprovalFlowInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof ApprovalFlowApi
+     */
+    public async apiApprovalFlowPagePost(body?: ApprovalFlowInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSqlSugarPagedListApprovalFlowOutput>> {
+        return ApprovalFlowApiFp(this.configuration).apiApprovalFlowPagePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 更新审批流
+     * @param {UpdateApprovalFlowInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof ApprovalFlowApi
+     */
+    public async apiApprovalFlowUpdatePost(body?: UpdateApprovalFlowInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return ApprovalFlowApiFp(this.configuration).apiApprovalFlowUpdatePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+}

+ 70 - 0
Web/src/api-services/_approvalFlow/base.ts

@@ -0,0 +1,70 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { Configuration } from "./configuration";
+// Some imports not used depending on template conditions
+// @ts-ignore
+import globalAxios, { AxiosRequestConfig, AxiosInstance } from 'axios';
+
+export const BASE_PATH = "/".replace(/\/+$/, "");
+
+/**
+ *
+ * @export
+ */
+export const COLLECTION_FORMATS = {
+    csv: ",",
+    ssv: " ",
+    tsv: "\t",
+    pipes: "|",
+};
+
+/**
+ *
+ * @export
+ * @interface RequestArgs
+ */
+export interface RequestArgs {
+    url: string;
+    options: AxiosRequestConfig;
+}
+
+/**
+ *
+ * @export
+ * @class BaseAPI
+ */
+export class BaseAPI {
+    protected configuration: Configuration | undefined;
+
+    constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) {
+        if (configuration) {
+            this.configuration = configuration;
+            this.basePath = configuration.basePath || this.basePath;
+        }
+    }
+};
+
+/**
+ *
+ * @export
+ * @class RequiredError
+ * @extends {Error}
+ */
+export class RequiredError extends Error {
+    name: "RequiredError" = "RequiredError";
+    constructor(public field: string, msg?: string) {
+        super(msg);
+    }
+}

+ 83 - 0
Web/src/api-services/_approvalFlow/configuration.ts

@@ -0,0 +1,83 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+export interface ConfigurationParameters {
+    apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
+    username?: string;
+    password?: string;
+    accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
+    basePath?: string;
+    baseOptions?: any;
+}
+
+export class Configuration {
+
+    /**
+     * parameter for apiKey security
+     *
+     * @param name security name
+     * @memberof Configuration
+     */
+    apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
+
+    /**
+     * parameter for basic security
+     *
+     * @type {string}
+     * @memberof Configuration
+     */
+    username?: string;
+
+    /**
+     * parameter for basic security
+     *
+     * @type {string}
+     * @memberof Configuration
+     */
+    password?: string;
+
+    /**
+     * parameter for oauth2 security
+     *
+     * @param name security name
+     * @param scopes oauth2 scope
+     * @memberof Configuration
+     */
+    accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
+
+    /**
+     * override base path
+     *
+     * @type {string}
+     * @memberof Configuration
+     */
+    basePath?: string;
+
+    /**
+     * base options for axios calls
+     *
+     * @type {any}
+     * @memberof Configuration
+     */
+    baseOptions?: any;
+
+    constructor(param: ConfigurationParameters = {}) {
+        this.apiKey = param.apiKey;
+        this.username = param.username;
+        this.password = param.password;
+        this.accessToken = param.accessToken;
+        this.basePath = param.basePath;
+        this.baseOptions = param.baseOptions;
+    }
+}

+ 18 - 0
Web/src/api-services/_approvalFlow/index.ts

@@ -0,0 +1,18 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+export * from "./api";
+export * from "./configuration";
+export * from "./models";
+

+ 134 - 0
Web/src/api-services/_approvalFlow/models/add-approval-flow-input.ts

@@ -0,0 +1,134 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 审批流增加输入参数
+ *
+ * @export
+ * @interface AddApprovalFlowInput
+ */
+export interface AddApprovalFlowInput {
+
+    /**
+     * 编号
+     *
+     * @type {string}
+     * @memberof AddApprovalFlowInput
+     */
+    code?: string | null;
+
+    /**
+     * 名称
+     *
+     * @type {string}
+     * @memberof AddApprovalFlowInput
+     */
+    name?: string | null;
+
+    /**
+     * 表单
+     *
+     * @type {string}
+     * @memberof AddApprovalFlowInput
+     */
+    formJson?: string | null;
+
+    /**
+     * 流程
+     *
+     * @type {string}
+     * @memberof AddApprovalFlowInput
+     */
+    flowJson?: string | null;
+
+    /**
+     * 备注
+     *
+     * @type {string}
+     * @memberof AddApprovalFlowInput
+     */
+    remark?: string | null;
+
+    /**
+     * 创建时间
+     *
+     * @type {Date}
+     * @memberof AddApprovalFlowInput
+     */
+    createTime?: Date | null;
+
+    /**
+     * 更新时间
+     *
+     * @type {Date}
+     * @memberof AddApprovalFlowInput
+     */
+    updateTime?: Date | null;
+
+    /**
+     * 创建者Id
+     *
+     * @type {number}
+     * @memberof AddApprovalFlowInput
+     */
+    createUserId?: number | null;
+
+    /**
+     * 创建者姓名
+     *
+     * @type {string}
+     * @memberof AddApprovalFlowInput
+     */
+    createUserName?: string | null;
+
+    /**
+     * 修改者Id
+     *
+     * @type {number}
+     * @memberof AddApprovalFlowInput
+     */
+    updateUserId?: number | null;
+
+    /**
+     * 修改者姓名
+     *
+     * @type {string}
+     * @memberof AddApprovalFlowInput
+     */
+    updateUserName?: string | null;
+
+    /**
+     * 创建者部门Id
+     *
+     * @type {number}
+     * @memberof AddApprovalFlowInput
+     */
+    createOrgId?: number | null;
+
+    /**
+     * 创建者部门名称
+     *
+     * @type {string}
+     * @memberof AddApprovalFlowInput
+     */
+    createOrgName?: string | null;
+
+    /**
+     * 软删除
+     *
+     * @type {boolean}
+     * @memberof AddApprovalFlowInput
+     */
+    isDelete: boolean;
+}

+ 69 - 0
Web/src/api-services/_approvalFlow/models/admin-result-approval-flow.ts

@@ -0,0 +1,69 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { ApprovalFlow } from './approval-flow';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultApprovalFlow
+ */
+export interface AdminResultApprovalFlow {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultApprovalFlow
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultApprovalFlow
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultApprovalFlow
+     */
+    message?: string | null;
+
+    /**
+     * @type {ApprovalFlow}
+     * @memberof AdminResultApprovalFlow
+     */
+    result?: ApprovalFlow;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultApprovalFlow
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultApprovalFlow
+     */
+    time?: Date;
+}

+ 70 - 0
Web/src/api-services/_approvalFlow/models/admin-result-int64.ts

@@ -0,0 +1,70 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultInt64
+ */
+export interface AdminResultInt64 {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultInt64
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultInt64
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultInt64
+     */
+    message?: string | null;
+
+    /**
+     * 数据
+     *
+     * @type {number}
+     * @memberof AdminResultInt64
+     */
+    result?: number;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultInt64
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultInt64
+     */
+    time?: Date;
+}

+ 71 - 0
Web/src/api-services/_approvalFlow/models/admin-result-list-approval-flow-output.ts

@@ -0,0 +1,71 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { ApprovalFlowOutput } from './approval-flow-output';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultListApprovalFlowOutput
+ */
+export interface AdminResultListApprovalFlowOutput {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultListApprovalFlowOutput
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultListApprovalFlowOutput
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultListApprovalFlowOutput
+     */
+    message?: string | null;
+
+    /**
+     * 数据
+     *
+     * @type {Array<ApprovalFlowOutput>}
+     * @memberof AdminResultListApprovalFlowOutput
+     */
+    result?: Array<ApprovalFlowOutput> | null;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultListApprovalFlowOutput
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultListApprovalFlowOutput
+     */
+    time?: Date;
+}

+ 69 - 0
Web/src/api-services/_approvalFlow/models/admin-result-sql-sugar-paged-list-approval-flow-output.ts

@@ -0,0 +1,69 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { SqlSugarPagedListApprovalFlowOutput } from './sql-sugar-paged-list-approval-flow-output';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultSqlSugarPagedListApprovalFlowOutput
+ */
+export interface AdminResultSqlSugarPagedListApprovalFlowOutput {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultSqlSugarPagedListApprovalFlowOutput
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultSqlSugarPagedListApprovalFlowOutput
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultSqlSugarPagedListApprovalFlowOutput
+     */
+    message?: string | null;
+
+    /**
+     * @type {SqlSugarPagedListApprovalFlowOutput}
+     * @memberof AdminResultSqlSugarPagedListApprovalFlowOutput
+     */
+    result?: SqlSugarPagedListApprovalFlowOutput;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultSqlSugarPagedListApprovalFlowOutput
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultSqlSugarPagedListApprovalFlowOutput
+     */
+    time?: Date;
+}

+ 94 - 0
Web/src/api-services/_approvalFlow/models/approval-flow-input.ts

@@ -0,0 +1,94 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 审批流分页查询输入参数
+ *
+ * @export
+ * @interface ApprovalFlowInput
+ */
+export interface ApprovalFlowInput {
+
+    /**
+     * 当前页码
+     *
+     * @type {number}
+     * @memberof ApprovalFlowInput
+     */
+    page?: number;
+
+    /**
+     * 页码容量
+     *
+     * @type {number}
+     * @memberof ApprovalFlowInput
+     */
+    pageSize?: number;
+
+    /**
+     * 排序字段
+     *
+     * @type {string}
+     * @memberof ApprovalFlowInput
+     */
+    field?: string | null;
+
+    /**
+     * 排序方向
+     *
+     * @type {string}
+     * @memberof ApprovalFlowInput
+     */
+    order?: string | null;
+
+    /**
+     * 降序排序
+     *
+     * @type {string}
+     * @memberof ApprovalFlowInput
+     */
+    descStr?: string | null;
+
+    /**
+     * 关键字查询
+     *
+     * @type {string}
+     * @memberof ApprovalFlowInput
+     */
+    searchKey?: string | null;
+
+    /**
+     * 编号
+     *
+     * @type {string}
+     * @memberof ApprovalFlowInput
+     */
+    code?: string | null;
+
+    /**
+     * 名称
+     *
+     * @type {string}
+     * @memberof ApprovalFlowInput
+     */
+    name?: string | null;
+
+    /**
+     * 备注
+     *
+     * @type {string}
+     * @memberof ApprovalFlowInput
+     */
+    remark?: string | null;
+}

+ 142 - 0
Web/src/api-services/_approvalFlow/models/approval-flow-output.ts

@@ -0,0 +1,142 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 审批流输出参数
+ *
+ * @export
+ * @interface ApprovalFlowOutput
+ */
+export interface ApprovalFlowOutput {
+
+    /**
+     * 主键Id
+     *
+     * @type {number}
+     * @memberof ApprovalFlowOutput
+     */
+    id?: number;
+
+    /**
+     * 编号
+     *
+     * @type {string}
+     * @memberof ApprovalFlowOutput
+     */
+    code?: string | null;
+
+    /**
+     * 名称
+     *
+     * @type {string}
+     * @memberof ApprovalFlowOutput
+     */
+    name?: string | null;
+
+    /**
+     * 表单
+     *
+     * @type {string}
+     * @memberof ApprovalFlowOutput
+     */
+    formJson?: string | null;
+
+    /**
+     * 流程
+     *
+     * @type {string}
+     * @memberof ApprovalFlowOutput
+     */
+    flowJson?: string | null;
+
+    /**
+     * 备注
+     *
+     * @type {string}
+     * @memberof ApprovalFlowOutput
+     */
+    remark?: string | null;
+
+    /**
+     * 创建时间
+     *
+     * @type {Date}
+     * @memberof ApprovalFlowOutput
+     */
+    createTime?: Date | null;
+
+    /**
+     * 更新时间
+     *
+     * @type {Date}
+     * @memberof ApprovalFlowOutput
+     */
+    updateTime?: Date | null;
+
+    /**
+     * 创建者Id
+     *
+     * @type {number}
+     * @memberof ApprovalFlowOutput
+     */
+    createUserId?: number | null;
+
+    /**
+     * 创建者姓名
+     *
+     * @type {string}
+     * @memberof ApprovalFlowOutput
+     */
+    createUserName?: string | null;
+
+    /**
+     * 修改者Id
+     *
+     * @type {number}
+     * @memberof ApprovalFlowOutput
+     */
+    updateUserId?: number | null;
+
+    /**
+     * 修改者姓名
+     *
+     * @type {string}
+     * @memberof ApprovalFlowOutput
+     */
+    updateUserName?: string | null;
+
+    /**
+     * 创建者部门Id
+     *
+     * @type {number}
+     * @memberof ApprovalFlowOutput
+     */
+    createOrgId?: number | null;
+
+    /**
+     * 创建者部门名称
+     *
+     * @type {string}
+     * @memberof ApprovalFlowOutput
+     */
+    createOrgName?: string | null;
+
+    /**
+     * 软删除
+     *
+     * @type {boolean}
+     * @memberof ApprovalFlowOutput
+     */
+    isDelete?: boolean;
+}

+ 150 - 0
Web/src/api-services/_approvalFlow/models/approval-flow.ts

@@ -0,0 +1,150 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 审批流程信息表
+ *
+ * @export
+ * @interface ApprovalFlow
+ */
+export interface ApprovalFlow {
+
+    /**
+     * 雪花Id
+     *
+     * @type {number}
+     * @memberof ApprovalFlow
+     */
+    id?: number;
+
+    /**
+     * 创建时间
+     *
+     * @type {Date}
+     * @memberof ApprovalFlow
+     */
+    createTime?: Date | null;
+
+    /**
+     * 更新时间
+     *
+     * @type {Date}
+     * @memberof ApprovalFlow
+     */
+    updateTime?: Date | null;
+
+    /**
+     * 创建者Id
+     *
+     * @type {number}
+     * @memberof ApprovalFlow
+     */
+    createUserId?: number | null;
+
+    /**
+     * 创建者姓名
+     *
+     * @type {string}
+     * @memberof ApprovalFlow
+     */
+    createUserName?: string | null;
+
+    /**
+     * 修改者Id
+     *
+     * @type {number}
+     * @memberof ApprovalFlow
+     */
+    updateUserId?: number | null;
+
+    /**
+     * 修改者姓名
+     *
+     * @type {string}
+     * @memberof ApprovalFlow
+     */
+    updateUserName?: string | null;
+
+    /**
+     * 软删除
+     *
+     * @type {boolean}
+     * @memberof ApprovalFlow
+     */
+    isDelete?: boolean;
+
+    /**
+     * 创建者部门Id
+     *
+     * @type {number}
+     * @memberof ApprovalFlow
+     */
+    createOrgId?: number | null;
+
+    /**
+     * 创建者部门名称
+     *
+     * @type {string}
+     * @memberof ApprovalFlow
+     */
+    createOrgName?: string | null;
+
+    /**
+     * 编号
+     *
+     * @type {string}
+     * @memberof ApprovalFlow
+     */
+    code?: string | null;
+
+    /**
+     * 名称
+     *
+     * @type {string}
+     * @memberof ApprovalFlow
+     */
+    name?: string | null;
+
+    /**
+     * 表单
+     *
+     * @type {string}
+     * @memberof ApprovalFlow
+     */
+    formJson?: string | null;
+
+    /**
+     * 流程
+     *
+     * @type {string}
+     * @memberof ApprovalFlow
+     */
+    flowJson?: string | null;
+
+    /**
+     * 状态
+     *
+     * @type {number}
+     * @memberof ApprovalFlow
+     */
+    status?: number | null;
+
+    /**
+     * 备注
+     *
+     * @type {string}
+     * @memberof ApprovalFlow
+     */
+    remark?: string | null;
+}

+ 30 - 0
Web/src/api-services/_approvalFlow/models/delete-approval-flow-input.ts

@@ -0,0 +1,30 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 审批流删除输入参数
+ *
+ * @export
+ * @interface DeleteApprovalFlowInput
+ */
+export interface DeleteApprovalFlowInput {
+
+    /**
+     * 主键Id
+     *
+     * @type {number}
+     * @memberof DeleteApprovalFlowInput
+     */
+    id: number;
+}

+ 11 - 0
Web/src/api-services/_approvalFlow/models/index.ts

@@ -0,0 +1,11 @@
+export * from './add-approval-flow-input';
+export * from './admin-result-approval-flow';
+export * from './admin-result-int64';
+export * from './admin-result-list-approval-flow-output';
+export * from './admin-result-sql-sugar-paged-list-approval-flow-output';
+export * from './approval-flow';
+export * from './approval-flow-input';
+export * from './approval-flow-output';
+export * from './delete-approval-flow-input';
+export * from './sql-sugar-paged-list-approval-flow-output';
+export * from './update-approval-flow-input';

+ 79 - 0
Web/src/api-services/_approvalFlow/models/sql-sugar-paged-list-approval-flow-output.ts

@@ -0,0 +1,79 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { ApprovalFlowOutput } from './approval-flow-output';
+ /**
+ * 分页泛型集合
+ *
+ * @export
+ * @interface SqlSugarPagedListApprovalFlowOutput
+ */
+export interface SqlSugarPagedListApprovalFlowOutput {
+
+    /**
+     * 页码
+     *
+     * @type {number}
+     * @memberof SqlSugarPagedListApprovalFlowOutput
+     */
+    page?: number;
+
+    /**
+     * 页容量
+     *
+     * @type {number}
+     * @memberof SqlSugarPagedListApprovalFlowOutput
+     */
+    pageSize?: number;
+
+    /**
+     * 总条数
+     *
+     * @type {number}
+     * @memberof SqlSugarPagedListApprovalFlowOutput
+     */
+    total?: number;
+
+    /**
+     * 总页数
+     *
+     * @type {number}
+     * @memberof SqlSugarPagedListApprovalFlowOutput
+     */
+    totalPages?: number;
+
+    /**
+     * 当前页集合
+     *
+     * @type {Array<ApprovalFlowOutput>}
+     * @memberof SqlSugarPagedListApprovalFlowOutput
+     */
+    items?: Array<ApprovalFlowOutput> | null;
+
+    /**
+     * 是否有上一页
+     *
+     * @type {boolean}
+     * @memberof SqlSugarPagedListApprovalFlowOutput
+     */
+    hasPrevPage?: boolean;
+
+    /**
+     * 是否有下一页
+     *
+     * @type {boolean}
+     * @memberof SqlSugarPagedListApprovalFlowOutput
+     */
+    hasNextPage?: boolean;
+}

+ 142 - 0
Web/src/api-services/_approvalFlow/models/update-approval-flow-input.ts

@@ -0,0 +1,142 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 审批流程
+ * 对业务实体数据的增删改操作进行流程审批。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 审批流更新输入参数
+ *
+ * @export
+ * @interface UpdateApprovalFlowInput
+ */
+export interface UpdateApprovalFlowInput {
+
+    /**
+     * 编号
+     *
+     * @type {string}
+     * @memberof UpdateApprovalFlowInput
+     */
+    code?: string | null;
+
+    /**
+     * 名称
+     *
+     * @type {string}
+     * @memberof UpdateApprovalFlowInput
+     */
+    name?: string | null;
+
+    /**
+     * 表单
+     *
+     * @type {string}
+     * @memberof UpdateApprovalFlowInput
+     */
+    formJson?: string | null;
+
+    /**
+     * 流程
+     *
+     * @type {string}
+     * @memberof UpdateApprovalFlowInput
+     */
+    flowJson?: string | null;
+
+    /**
+     * 备注
+     *
+     * @type {string}
+     * @memberof UpdateApprovalFlowInput
+     */
+    remark?: string | null;
+
+    /**
+     * 创建时间
+     *
+     * @type {Date}
+     * @memberof UpdateApprovalFlowInput
+     */
+    createTime?: Date | null;
+
+    /**
+     * 更新时间
+     *
+     * @type {Date}
+     * @memberof UpdateApprovalFlowInput
+     */
+    updateTime?: Date | null;
+
+    /**
+     * 创建者Id
+     *
+     * @type {number}
+     * @memberof UpdateApprovalFlowInput
+     */
+    createUserId?: number | null;
+
+    /**
+     * 创建者姓名
+     *
+     * @type {string}
+     * @memberof UpdateApprovalFlowInput
+     */
+    createUserName?: string | null;
+
+    /**
+     * 修改者Id
+     *
+     * @type {number}
+     * @memberof UpdateApprovalFlowInput
+     */
+    updateUserId?: number | null;
+
+    /**
+     * 修改者姓名
+     *
+     * @type {string}
+     * @memberof UpdateApprovalFlowInput
+     */
+    updateUserName?: string | null;
+
+    /**
+     * 创建者部门Id
+     *
+     * @type {number}
+     * @memberof UpdateApprovalFlowInput
+     */
+    createOrgId?: number | null;
+
+    /**
+     * 创建者部门名称
+     *
+     * @type {string}
+     * @memberof UpdateApprovalFlowInput
+     */
+    createOrgName?: string | null;
+
+    /**
+     * 软删除
+     *
+     * @type {boolean}
+     * @memberof UpdateApprovalFlowInput
+     */
+    isDelete?: boolean;
+
+    /**
+     * 主键Id
+     *
+     * @type {number}
+     * @memberof UpdateApprovalFlowInput
+     */
+    id: number;
+}

+ 20 - 56
Web/src/api-services/apis/sys-open-access-api.ts

@@ -21,7 +21,7 @@ import { AddOpenAccessInput } from '../models';
 import { AdminResultSqlSugarPagedListOpenAccessOutput } from '../models';
 import { AdminResultString } from '../models';
 import { DeleteOpenAccessInput } from '../models';
-import { HttpMethodEnum } from '../models';
+import { GenerateSignatureInput } from '../models';
 import { OpenAccessInput } from '../models';
 import { UpdateOpenAccessInput } from '../models';
 /**
@@ -128,27 +128,12 @@ export const SysOpenAccessApiAxiosParamCreator = function (configuration?: Confi
         },
         /**
          * 
-         * @summary 获取生成的签名
-         * @param {string} appSecret 密钥
-         * @param {string} accessKey 身份标识
-         * @param {string} url 请求接口地址
-         * @param {HttpMethodEnum} [method] 请求方法
+         * @summary 生成签名
+         * @param {GenerateSignatureInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        apiSysOpenAccessGenerateSignatureGet: async (appSecret: string, accessKey: string, url: string, method?: HttpMethodEnum, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            // verify required parameter 'appSecret' is not null or undefined
-            if (appSecret === null || appSecret === undefined) {
-                throw new RequiredError('appSecret','Required parameter appSecret was null or undefined when calling apiSysOpenAccessGenerateSignatureGet.');
-            }
-            // verify required parameter 'accessKey' is not null or undefined
-            if (accessKey === null || accessKey === undefined) {
-                throw new RequiredError('accessKey','Required parameter accessKey was null or undefined when calling apiSysOpenAccessGenerateSignatureGet.');
-            }
-            // verify required parameter 'url' is not null or undefined
-            if (url === null || url === undefined) {
-                throw new RequiredError('url','Required parameter url was null or undefined when calling apiSysOpenAccessGenerateSignatureGet.');
-            }
+        apiSysOpenAccessGenerateSignaturePost: async (body?: GenerateSignatureInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
             const localVarPath = `/api/sysOpenAccess/generateSignature`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
@@ -156,7 +141,7 @@ export const SysOpenAccessApiAxiosParamCreator = function (configuration?: Confi
             if (configuration) {
                 baseOptions = configuration.baseOptions;
             }
-            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
             const localVarHeaderParameter = {} as any;
             const localVarQueryParameter = {} as any;
 
@@ -169,21 +154,7 @@ export const SysOpenAccessApiAxiosParamCreator = function (configuration?: Confi
                 localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
             }
 
-            if (appSecret !== undefined) {
-                localVarQueryParameter['AppSecret'] = appSecret;
-            }
-
-            if (accessKey !== undefined) {
-                localVarQueryParameter['AccessKey'] = accessKey;
-            }
-
-            if (method !== undefined) {
-                localVarQueryParameter['Method'] = method;
-            }
-
-            if (url !== undefined) {
-                localVarQueryParameter['Url'] = url;
-            }
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
 
             const query = new URLSearchParams(localVarUrlObj.search);
             for (const key in localVarQueryParameter) {
@@ -195,6 +166,8 @@ export const SysOpenAccessApiAxiosParamCreator = function (configuration?: Confi
             localVarUrlObj.search = (new URLSearchParams(query)).toString();
             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
 
             return {
                 url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
@@ -379,16 +352,13 @@ export const SysOpenAccessApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 获取生成的签名
-         * @param {string} appSecret 密钥
-         * @param {string} accessKey 身份标识
-         * @param {string} url 请求接口地址
-         * @param {HttpMethodEnum} [method] 请求方法
+         * @summary 生成签名
+         * @param {GenerateSignatureInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysOpenAccessGenerateSignatureGet(appSecret: string, accessKey: string, url: string, method?: HttpMethodEnum, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultString>>> {
-            const localVarAxiosArgs = await SysOpenAccessApiAxiosParamCreator(configuration).apiSysOpenAccessGenerateSignatureGet(appSecret, accessKey, url, method, options);
+        async apiSysOpenAccessGenerateSignaturePost(body?: GenerateSignatureInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultString>>> {
+            const localVarAxiosArgs = await SysOpenAccessApiAxiosParamCreator(configuration).apiSysOpenAccessGenerateSignaturePost(body, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -466,16 +436,13 @@ export const SysOpenAccessApiFactory = function (configuration?: Configuration,
         },
         /**
          * 
-         * @summary 获取生成的签名
-         * @param {string} appSecret 密钥
-         * @param {string} accessKey 身份标识
-         * @param {string} url 请求接口地址
-         * @param {HttpMethodEnum} [method] 请求方法
+         * @summary 生成签名
+         * @param {GenerateSignatureInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysOpenAccessGenerateSignatureGet(appSecret: string, accessKey: string, url: string, method?: HttpMethodEnum, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultString>> {
-            return SysOpenAccessApiFp(configuration).apiSysOpenAccessGenerateSignatureGet(appSecret, accessKey, url, method, options).then((request) => request(axios, basePath));
+        async apiSysOpenAccessGenerateSignaturePost(body?: GenerateSignatureInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultString>> {
+            return SysOpenAccessApiFp(configuration).apiSysOpenAccessGenerateSignaturePost(body, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -540,17 +507,14 @@ export class SysOpenAccessApi extends BaseAPI {
     }
     /**
      * 
-     * @summary 获取生成的签名
-     * @param {string} appSecret 密钥
-     * @param {string} accessKey 身份标识
-     * @param {string} url 请求接口地址
-     * @param {HttpMethodEnum} [method] 请求方法
+     * @summary 生成签名
+     * @param {GenerateSignatureInput} [body] 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof SysOpenAccessApi
      */
-    public async apiSysOpenAccessGenerateSignatureGet(appSecret: string, accessKey: string, url: string, method?: HttpMethodEnum, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultString>> {
-        return SysOpenAccessApiFp(this.configuration).apiSysOpenAccessGenerateSignatureGet(appSecret, accessKey, url, method, options).then((request) => request(this.axios, this.basePath));
+    public async apiSysOpenAccessGenerateSignaturePost(body?: GenerateSignatureInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultString>> {
+        return SysOpenAccessApiFp(this.configuration).apiSysOpenAccessGenerateSignaturePost(body, options).then((request) => request(this.axios, this.basePath));
     }
     /**
      * 

+ 69 - 0
Web/src/api-services/models/generate-signature-input.ts

@@ -0,0 +1,69 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { HttpMethodEnum } from './http-method-enum';
+ /**
+ * 
+ *
+ * @export
+ * @interface GenerateSignatureInput
+ */
+export interface GenerateSignatureInput {
+
+    /**
+     * 身份标识
+     *
+     * @type {string}
+     * @memberof GenerateSignatureInput
+     */
+    accessKey: string;
+
+    /**
+     * 密钥
+     *
+     * @type {string}
+     * @memberof GenerateSignatureInput
+     */
+    accessSecret: string;
+
+    /**
+     * @type {HttpMethodEnum}
+     * @memberof GenerateSignatureInput
+     */
+    method?: HttpMethodEnum;
+
+    /**
+     * 请求接口地址
+     *
+     * @type {string}
+     * @memberof GenerateSignatureInput
+     */
+    url: string;
+
+    /**
+     * 时间戳
+     *
+     * @type {number}
+     * @memberof GenerateSignatureInput
+     */
+    timestamp: number;
+
+    /**
+     * 随机数
+     *
+     * @type {string}
+     * @memberof GenerateSignatureInput
+     */
+    nonce: string;
+}

+ 1 - 0
Web/src/api-services/models/index.ts

@@ -144,6 +144,7 @@ export * from './enum-type-output';
 export * from './file-input';
 export * from './gen-auth-url-input';
 export * from './gender-enum';
+export * from './generate-signature-input';
 export * from './http-method-enum';
 export * from './iaction-result';
 export * from './jtoken';

+ 3 - 0
Web/src/assets/main.css

@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;

+ 68 - 27
Web/src/components/table/search.vue

@@ -1,10 +1,10 @@
 <template>
 	<div class="table-search-container" v-if="props.search.length > 0">
-		<el-form ref="tableSearchRef" :model="searchModel" label-width="100px" class="table-form">
+		<el-form ref="tableSearchRef" :model="state.innerModelValue" label-width="100px" class="table-form">
 			<el-row :gutter="20">
 				<!-- <el-col :xs="12" :sm="8" :md="8" :lg="6" :xl="4" class="mb20"></el-col> -->
 				<el-col :xs="12" :sm="5" :md="5" :lg="6" :xl="4" class="mb20" v-for="(val, key) in search" :key="key" v-show="key < 3 || state.isToggle">
-					<template v-if="val.type !== ''">
+					<template v-if="val.type">
 						<el-form-item
 							label-width="auto"
 							:label="val.label"
@@ -12,7 +12,7 @@
 							:rules="[{ required: val.required, message: `${val.label}不能为空`, trigger: val.type === 'input' ? 'blur' : 'change' }]"
 						>
 							<el-input
-								v-model="searchModel[val.prop]"
+								v-model="state.innerModelValue[val.prop]"
 								v-bind="val.comProps"
 								:placeholder="val.placeholder"
 								:clearable="!val.required"
@@ -22,7 +22,7 @@
 								class="w100"
 							/>
 							<el-date-picker
-								v-model="searchModel[val.prop]"
+								v-model="state.innerModelValue[val.prop]"
 								v-bind="val.comProps"
 								type="date"
 								:placeholder="val.placeholder"
@@ -32,7 +32,7 @@
 								class="w100"
 							/>
 							<el-date-picker
-								v-model="searchModel[val.prop]"
+								v-model="state.innerModelValue[val.prop]"
 								v-bind="val.comProps"
 								type="monthrange"
 								value-format="YYYY/MM/DD"
@@ -43,18 +43,22 @@
 								class="w100"
 							/>
 							<el-date-picker
-								v-model="searchModel[val.prop]"
+								v-model="state.innerModelValue[val.prop]"
 								v-bind="val.comProps"
 								type="daterange"
 								value-format="YYYY/MM/DD"
-								:placeholder="val.placeholder"
+								range-separator="至"
+								start-placeholder="开始日期"
+								end-placeholder="结束日期"
 								:clearable="!val.required"
+								:shortcuts="shortcuts"
+								:default-time="defaultTime"
 								v-else-if="val.type === 'daterange'"
 								@change="val.change"
 								class="w100"
 							/>
 							<el-select
-								v-model="searchModel[val.prop]"
+								v-model="state.innerModelValue[val.prop]"
 								v-bind="val.comProps"
 								:clearable="!val.required"
 								:placeholder="val.placeholder"
@@ -74,7 +78,7 @@
 								@change="val.change"
 								class="w100"
 								v-bind="val.comProps"
-								v-model="searchModel[val.prop]"
+								v-model="state.innerModelValue[val.prop]"
 							>
 							</el-cascader>
 						</el-form-item>
@@ -105,9 +109,9 @@
 </template>
 
 <script setup lang="ts" name="makeTableDemoSearch">
-import { reactive, ref, toRefs } from 'vue';
+import { reactive, ref, watch } from 'vue';
 import type { FormInstance } from 'element-plus';
-import { saulVModel } from '/@/utils/saulVModel';
+import { dayjs } from 'element-plus';
 
 // 定义父组件传过来的值
 const props = defineProps({
@@ -117,10 +121,6 @@ const props = defineProps({
 		type: Array<TableSearchType>,
 		default: () => [],
 	},
-	reset: {
-		type: Object,
-		default: () => ({}),
-	},
 	modelValue: {
 		type: Object,
 		default: () => ({}),
@@ -130,28 +130,39 @@ const props = defineProps({
 // 定义子组件向父组件传值/事件
 const emit = defineEmits(['search', 'reset', 'update:modelValue']);
 
-// 将 props中的值转为 ref
-const refProps = toRefs(props);
-const searchModel = refProps.modelValue;
-
 // 定义变量内容
 const tableSearchRef = ref<FormInstance>();
 const state = reactive({
 	isToggle: false,
 	cascaderProps: { checkStrictly: true, emitPath: false, value: 'id', label: 'name', expandTrigger: 'hover' },
+	/** 内部 modelValue */
+	innerModelValue: {} as EmptyObjectType,
 });
 
-const model = saulVModel(props, 'modelValue', emit).value;
+/** 监听 props.modelValue 变化 */
+watch(
+	() => props.modelValue,
+	(val) => {
+		state.innerModelValue = val;
+	}
+);
+
+/** 监听 state.innerModelValue 变化 */
+watch(
+	() => state.innerModelValue,
+	(val) => {
+		emit('update:modelValue', val);
+	},
+	{ deep: true }
+);
 
 // 查询
 const onSearch = (formEl: FormInstance | undefined) => {
 	if (!formEl) return;
-	formEl.validate((valid: boolean) => {
-		if (valid) {
-			emit('search', model);
-		} else {
-			return false;
-		}
+	formEl.validate((isValid: boolean): void => {
+		if (!isValid) return;
+
+		emit('search', state.innerModelValue);
 	});
 };
 
@@ -159,8 +170,38 @@ const onSearch = (formEl: FormInstance | undefined) => {
 const onReset = (formEl: FormInstance | undefined) => {
 	if (!formEl) return;
 	formEl.resetFields();
-	emit('reset', model);
+	emit('reset', state.innerModelValue);
 };
+
+/** 时间范围默认时间 */
+const defaultTime = ref<[Date, Date]>([new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)]);
+/** 时间范围快捷选择 */
+const shortcuts = [
+	{
+		text: '7天内',
+		value: () => {
+			const end = dayjs().endOf('day').toDate();
+			const start = dayjs().startOf('day').add(-7, 'day').toDate();
+			return [start, end];
+		},
+	},
+	{
+		text: '1个月内',
+		value: () => {
+			const end = dayjs().endOf('day').toDate();
+			const start = dayjs().startOf('day').add(-1, 'month').toDate();
+			return [start, end];
+		},
+	},
+	{
+		text: '3个月内',
+		value: () => {
+			const end = dayjs().endOf('day').toDate();
+			const start = dayjs().startOf('day').add(-3, 'month').toDate();
+			return [start, end];
+		},
+	},
+];
 </script>
 
 <style scoped lang="scss">

+ 11 - 1
Web/src/layout/routerView/iframes.vue

@@ -3,7 +3,17 @@
 		<div class="layout-padding-auto layout-padding-view">
 			<div class="w100" v-for="v in setIframeList" :key="v.path" v-loading="v.meta.loading" element-loading-background="white">
 				<transition-group :name="name">
-					<iframe :src="`${v.meta.isLink}&token=${getToken()}`" :key="v.path" frameborder="0" height="100%" width="100%" style="position: absolute" :data-url="v.path" v-show="getRoutePath === v.path" ref="iframeRef" />
+					<iframe
+						:src="`${v.meta.isLink}${v.meta.isLink.indexOf('?') > 0 ? '&' : '?'}token=${getToken()}`"
+						:key="v.path"
+						frameborder="0"
+						height="100%"
+						width="100%"
+						style="position: absolute"
+						:data-url="v.path"
+						v-show="getRoutePath === v.path"
+						ref="iframeRef"
+					/>
 				</transition-group>
 			</div>
 		</div>

+ 1 - 0
Web/src/main.ts

@@ -16,6 +16,7 @@ import VueSignaturePad from 'vue-signature-pad'; // 电子签名
 import vue3TreeOrg from 'vue3-tree-org'; // 组织架构图
 import 'vue3-tree-org/lib/vue3-tree-org.css'; // 组织架构图样式
 import 'animate.css'; // 动画库
+import '/@/assets/main.css'
 
 import { disAutoConnect } from 'vue-plugin-hiprint';
 disAutoConnect();

+ 67 - 9
Web/src/theme/element.scss

@@ -11,6 +11,7 @@
 	font-size: 14px !important;
 	margin-right: 5px;
 }
+
 .el-button--small i.iconfont,
 .el-button--small i.fa {
 	font-size: 12px !important;
@@ -31,16 +32,19 @@
 	.el-form-item:last-of-type {
 		margin-bottom: 0 !important;
 	}
+
 	// 修复行内表单最后一个 el-form-item 位置下移问题
 	&.el-form--inline {
 		.el-form-item--large.el-form-item:last-of-type {
 			margin-bottom: 22px !important;
 		}
+
 		.el-form-item--default.el-form-item:last-of-type,
 		.el-form-item--small.el-form-item:last-of-type {
 			margin-bottom: 18px !important;
 		}
 	}
+
 	// https://gitee.com/lyt-top/vue-next-admin/issues/I5K1PM
 	.el-form-item .el-form-item__label .el-icon {
 		margin-right: 0px;
@@ -52,6 +56,7 @@
 .el-alert {
 	border: 1px solid;
 }
+
 .el-alert__title {
 	word-break: break-all;
 }
@@ -71,26 +76,31 @@
 	background-color: var(--next-bg-menuBarActiveColor) !important;
 	//background-color: var(--el-color-primary-light-7) !important;
 }
+
 // 默认样式修改
 .el-menu {
 	border-right: none !important;
 	width: 220px;
 }
+
 .el-menu-item {
 	height: 45px !important;
 	line-height: 45px !important;
 	margin: 0 0 1px 0;
 }
+
 //.el-menu-item,
 .el-sub-menu__title {
 	color: var(--next-bg-menuBarColor);
 	height: 45px !important;
 	line-height: 45px !important;
 }
+
 // 修复点击左侧菜单折叠再展开时,宽度不跟随问题
 .el-menu--collapse {
 	width: 64px !important;
 }
+
 // 外部链接时
 .el-menu-item a,
 .el-menu-item a:hover,
@@ -99,6 +109,7 @@
 	color: inherit;
 	text-decoration: none;
 }
+
 // 第三方图标字体间距/大小设置
 .el-menu-item .iconfont,
 .el-sub-menu .iconfont,
@@ -106,6 +117,7 @@
 .el-sub-menu .fa {
 	@include generalIcon;
 }
+
 // 水平菜单、横向菜单高亮 背景色,鼠标 hover 时,有子级菜单的背景色
 .el-menu-item.is-active,
 .el-sub-menu.is-active .el-sub-menu__title,
@@ -113,21 +125,26 @@
 	@extend .el-menu-hover-bg-color;
 	color: var(--el-color-primary-dark-2);
 }
+
 .el-sub-menu:not(.is-active) .el-sub-menu__title {
 	color: var(--next-bg-menuBarColor);
 }
+
 // 鼠标 hover 时背景色
 .el-menu-item:hover {
 	//@extend .el-menu-hover-bg-color;
 	background-color: var(--el-color-primary-light-9);
 }
+
 .el-sub-menu.is-active.is-opened .el-sub-menu__title {
 	background-color: unset !important;
 }
+
 // 当前选中菜单右侧边框
 .el-menu-item.is-active {
 	border-right: solid 3px var(--el-color-primary);
 }
+
 // 子级菜单背景颜色
 // .el-menu--inline {
 // 	background: var(--next-bg-menuBar-light-1);
@@ -137,85 +154,102 @@
 	color: var(--el-color-white) !important;
 	text-decoration: none;
 }
+
 // 水平菜单、横向菜单折叠背景色
 .el-popper.is-pure.is-light {
 	// 水平菜单
 	.el-menu--vertical {
 		background: var(--next-bg-menuBar);
+
 		.el-sub-menu.is-active .el-sub-menu__title {
 			color: var(--el-menu-active-color);
 		}
+
 		.el-popper.is-pure.is-light {
 			.el-menu--vertical {
 				.el-sub-menu .el-sub-menu__title {
 					background-color: unset !important;
 					color: var(--next-bg-menuBarColor);
 				}
+
 				.el-sub-menu.is-active .el-sub-menu__title {
 					color: var(--el-menu-active-color);
 				}
 			}
 		}
 	}
+
 	// 横向菜单
 	.el-menu--horizontal {
 		background: var(--next-bg-topBar);
+
 		.el-menu-item,
 		.el-sub-menu {
 			height: 45px !important;
 			line-height: 45px !important;
 			color: var(--next-bg-topBarColor);
+
 			.el-sub-menu__title {
 				height: 45px !important;
 				line-height: 45px !important;
 				color: var(--next-bg-topBarColor);
 			}
+
 			.el-popper.is-pure.is-light {
 				.el-menu--horizontal {
 					.el-sub-menu .el-sub-menu__title {
 						background-color: unset !important;
 						color: var(--next-bg-topBarColor);
 					}
+
 					.el-sub-menu.is-active .el-sub-menu__title {
 						color: var(--el-menu-active-color);
 					}
 				}
 			}
 		}
+
 		.el-menu-item.is-active,
 		.el-sub-menu.is-active .el-sub-menu__title {
 			color: var(--el-menu-active-color);
 		}
+
 		// 鼠标 hover 时背景色
 		.el-menu-item:hover {
 			background-color: var(--el-color-primary-light-9);
 		}
 	}
 }
+
 // 横向菜单(经典、横向)布局
 .el-menu.el-menu--horizontal {
 	border-bottom: none !important;
 
 	width: 100% !important;
+
 	.el-menu-item,
 	.el-sub-menu__title {
 		height: 50px !important;
 		color: var(--next-bg-topBarColor);
 	}
+
 	.el-menu-item:not(.is-active):hover,
 	.el-sub-menu:not(.is-active):hover .el-sub-menu__title {
 		color: var(--next-bg-topBarColor);
 		//border-right: solid 3px var(--el-color-primary);
 	}
+
 	// 鼠标 hover 时背景色
 	.el-menu-item:hover {
 		background-color: var(--el-color-primary-light-9);
 	}
+
 	// 菜单激活时去掉全局右边框
 	.el-menu-item.is-active {
 		border-right: none;
 	}
 }
+
 // 菜单收起时,图标不居中问题
 .el-menu--collapse {
 	.el-menu-item .iconfont,
@@ -224,6 +258,7 @@
 	.el-sub-menu .fa {
 		margin-right: 0 !important;
 	}
+
 	.el-sub-menu__title {
 		padding-right: 0 !important;
 	}
@@ -238,10 +273,13 @@
 /* Dropdown 下拉菜单
 ------------------------------- */
 .el-dropdown-menu {
-	list-style: none !important; /*修复 Dropdown 下拉菜单样式问题 2022.03.04*/
+	list-style: none !important;
+	/*修复 Dropdown 下拉菜单样式问题 2022.03.04*/
 }
+
 .el-dropdown-menu .el-dropdown-menu__item {
 	white-space: nowrap;
+
 	&:not(.is-disabled):hover {
 		background-color: var(--el-dropdown-menuItem-hover-fill);
 		color: var(--el-dropdown-menuItem-hover-color);
@@ -254,6 +292,7 @@
 	font-size: 30px !important;
 	font-weight: 400 !important;
 }
+
 .el-step__title {
 	font-size: 14px;
 }
@@ -262,6 +301,7 @@
 ------------------------------- */
 .el-overlay {
 	overflow: hidden;
+
 	.el-overlay-dialog {
 		display: flex;
 		align-items: center;
@@ -269,20 +309,24 @@
 		position: unset !important;
 		width: 100%;
 		height: 100%;
+
 		.el-dialog {
 			margin: 0 auto !important;
 			position: absolute;
+
 			.el-dialog__body {
 				padding: 20px !important;
 			}
 		}
 	}
 }
+
 .el-dialog__body {
 	max-height: calc(100vh - 85px) !important;
 	overflow-y: auto;
 	overflow-x: auto;
 }
+
 // .el-dialog__title{
 // 	font-size: 14px;
 // 	color: #FFF;
@@ -293,16 +337,19 @@
 	padding: 8px 0px 8px 20px;
 	background: var(--el-color-primary);
 }
+
 // .el-dialog__footer {
 // 	// border-top: 1px solid var(--el-color-info-light-7);
 // 	padding-bottom: 10px;
 // }
 .el-dialog__headerbtn {
 	margin-top: -5px;
+
 	.el-dialog__close {
 		color: #fff;
 	}
 }
+
 .el-dialog__headerbtn:hover .el-dialog__close {
 	color: var(--el-color-danger) !important;
 }
@@ -319,16 +366,20 @@
 	.el-button.is-text {
 		padding: 0;
 	}
+
 	// 表头背景色
 	--el-table-header-bg-color: var(--next-bg-main-color);
+
 	// 表头字体颜色
 	thead {
 		color: var(--el-text-color-regular);
+
 		th {
 			font-weight: 500;
 		}
 	}
 }
+
 /*防止分栏布局二级菜单很多时,滚动条消失问题*/
 .layout-columns-warp .layout-aside .el-scrollbar__view {
 	height: unset !important;
@@ -346,6 +397,7 @@
 	background-color: var(--el-color-primary) !important;
 	color: var(--el-color-white) !important;
 }
+
 // 分页组件靠右显示
 .el-pagination {
 	float: right;
@@ -357,21 +409,26 @@
 .el-scrollbar__bar {
 	z-index: 4;
 }
+
 /*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/
 .el-scrollbar__wrap {
 	max-height: 100%;
 }
+
 .el-select-dropdown .el-scrollbar__wrap {
 	overflow-x: scroll !important;
 }
+
 /*修复Select 选择器高度问题*/
 .el-select-dropdown__wrap {
 	max-height: 274px !important;
 }
+
 /*修复Cascader 级联选择器高度问题*/
 .el-cascader-menu__wrap.el-scrollbar__wrap {
 	height: 204px !important;
 }
+
 /*用于界面高度自适应(main.vue),区分 scrollbar__view,防止其它使用 scrollbar 的地方出现滚动条消失*/
 .layout-container-view .el-scrollbar__view {
 	height: 100%;
@@ -381,6 +438,7 @@
 ------------------------------- */
 .el-drawer {
 	--el-drawer-padding-primary: unset !important;
+
 	.el-drawer__header {
 		padding: 0 15px !important;
 		height: 50px;
@@ -390,6 +448,7 @@
 		border-bottom: 1px solid var(--el-border-color);
 		color: var(--el-text-color-primary);
 	}
+
 	.el-drawer__body {
 		width: 100%;
 		height: 100%;
@@ -418,26 +477,25 @@ $--el-table-text-color: #fb6d49;
 		flex-direction: column;
 		height: 100%;
 
-		//放弃使用 .el-card:nth-child(2) ,方便后期页面扩展
 		.full-table {
 			flex: 1;
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
 
 			.el-card__body {
 				height: 100%;
 				display: flex;
 				flex-direction: column;
+				justify-content: space-between;
 
 				.el-table {
 					flex: 1;
 				}
 
-				.el-pagination {
-					display: flex;
-
-					> span:first-child {
-						flex: 1;
-						text-align: right;
-					}
+				.el-pagination > span:first-child {
+					flex: 1;
+					text-align: right;
 				}
 			}
 		}

+ 1 - 1
Web/src/types/views.d.ts

@@ -311,7 +311,7 @@ declare type TableSearchType = {
 	prop: string;
 	placeholder: string;
 	required: boolean;
-	type: string;
+	type: 'input' | 'date' | 'daterange' | 'monthrange' | 'select' | 'cascader'; // vxe-next-admin 原定义是 string
 	options?: SelectOptionType[];
 	cascaderData?: object[];
 	cascaderProps?: object;

+ 80 - 0
Web/src/views/approvalFlow/component/LogicFlow/Panel/PanelControl.vue

@@ -0,0 +1,80 @@
+<template>
+	<div class="panel-control">
+		<el-button-group>
+			<el-button type="default" size="small" @click="$_zoomIn">放大</el-button>
+			<el-button type="default" size="small" @click="$_zoomOut">缩小</el-button>
+			<el-button type="default" size="small" @click="$_zoomReset">大小适应</el-button>
+			<el-button type="default" size="small" @click="$_translateRest">定位还原</el-button>
+			<el-button type="default" size="small" @click="$_reset">还原(大小&定位)</el-button>
+			<el-button type="default" size="small" @click="$_undo" :disabled="state.undoDisable">上一步(ctrl+z)</el-button>
+			<el-button type="default" size="small" @click="$_redo" :disabled="state.redoDisable">下一步(ctrl+y)</el-button>
+			<el-button type="default" size="small" @click="$_download">下载图片</el-button>
+			<el-button type="default" size="small" @click="$_catData">查看数据</el-button>
+			<el-button type="default" size="small" @click="$_showMiniMap">查看缩略图</el-button>
+		</el-button-group>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { reactive } from 'vue';
+
+var props = defineProps({
+	lf: Object,
+});
+const emit = defineEmits(['catData']);
+
+const state = reactive({
+	undoDisable: true,
+	redoDisable: true,
+});
+
+const $_zoomIn = () => {
+	props.lf?.zoom(true);
+};
+
+const $_zoomOut = () => {
+	props.lf?.zoom(false);
+};
+
+const $_zoomReset = () => {
+	props.lf?.resetZoom();
+};
+
+const $_translateRest = () => {
+	props.lf?.resetTranslate();
+};
+
+const $_reset = () => {
+	props.lf?.resetZoom();
+	props.lf?.resetTranslate();
+};
+
+const $_undo = () => {
+	props.lf?.undo();
+};
+
+const $_redo = () => {
+	props.lf?.redo();
+};
+
+const $_download = () => {
+	props.lf?.getSnapshot();
+};
+
+const $_catData = () => {
+	emit('catData');
+};
+
+const $_showMiniMap = () => {
+	props.lf?.extension.miniMap.show(props.lf.graphModel.width - 210, 70);
+};
+</script>
+
+<style lang="scss" scoped>
+.panel-control {
+	position: absolute;
+	top: 30px;
+	right: 50px;
+	z-index: 2;
+}
+</style>

+ 14 - 0
Web/src/views/approvalFlow/component/LogicFlow/Panel/PanelDataDialog.vue

@@ -0,0 +1,14 @@
+<template>
+	<div>
+		<vue-json-pretty :path="'res'" :data="graphData"></vue-json-pretty>
+	</div>
+</template>
+
+<script setup lang="ts">
+import VueJsonPretty from 'vue-json-pretty';
+import 'vue-json-pretty/lib/styles.css';
+
+var props = defineProps({
+	graphData: Object,
+});
+</script>

+ 114 - 0
Web/src/views/approvalFlow/component/LogicFlow/Panel/PanelNode.vue

@@ -0,0 +1,114 @@
+<template>
+	<div class="f-container-panel">
+		<div v-for="item in state.nodeList" :key="item.text" class="node-item" @mousedown="dragNode(item)">
+			<div class="node-item-icon" :class="item.class">
+				<div v-if="item.type === 'user' || item.type === 'time'" class="shape" />
+			</div>
+			<span class="node-label">{{ item.text }}</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { reactive } from 'vue';
+
+var props = defineProps({
+	lf: Object,
+});
+
+const state = reactive({
+	nodeList: [
+		{
+			text: '开始',
+			type: 'bpmn:startEvent',
+			class: 'bpmn-start',
+		},
+		{
+			text: '用户',
+			type: 'bpmn:userTask',
+			class: 'bpmn-user',
+		},
+		{
+			type: 'bpmn:exclusiveGateway',
+			text: '网关',
+			class: 'bpmn-exclusiveGateway',
+		},
+		{
+			type: 'task-node',
+			text: '任务',
+			class: 'node-task',
+		},
+		{
+			text: '结束',
+			type: 'bpmn:endEvent',
+			class: 'bpmn-end',
+		},
+	],
+});
+
+const dragNode = (item: any) => {
+	props.lf.dnd.startDrag({
+		type: item.type,
+	});
+};
+</script>
+
+<style lang="scss" scoped>
+.f-container-panel {
+	position: absolute;
+	top: 10px;
+	left: 15px;
+	width: 60px;
+	padding: 20px 10px;
+	background-color: white;
+	box-shadow: 0 0 10px 1px rgb(228, 224, 219);
+	border-radius: 6px;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	z-index: 101;
+	.node-item {
+		margin-bottom: 15px;
+		text-align: center;
+	}
+	.node-item-icon {
+		width: 30px;
+		height: 30px;
+		background-size: cover;
+	}
+	.node-label {
+		font-size: 12px;
+		margin-top: 5px;
+		user-select: none;
+	}
+	.node-rect {
+		border: 1px solid black;
+	}
+	.node-task {
+		background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAXCAYAAADgKtSgAAAAAXNSR0IArs4c6QAAAm9JREFUSEu1lTFo1GAUx38vZ6sOooIoVsWKk23SCp1UkIrg0EHs0M5OOolY9NJz6XXpNaeidOhQB3G1oCLUTXRxK9pe7lykk7YKhdJZuDzJXXMkaXKpBd+Y7//93sv/ve/7hP8YshO2WdKiGPQ0tMpvVTaqBSlm7f0X+EQAU4/JXcNNR5/jsRoATEcnBFqVKhSrtkz6ycySDkqOKTcvF+N/sq1yy9FXwEjDAY9JMRgDDiRYsIJQQHkGdAE11xYzrIvAw+AsP1PW511bRoO1KLysIyh+5ckhfEK5AOxNEig8rNpSSoT7H62yfkQZjGxWCl4nc7Ux2egu6r6D+xnyYBY41q7JkcrPPdXje/6wFga7tqROlOXoMtAX6OPaxkazrNekTpdCtxi0Rg7htpuXuTSXrJIOYLAYrh6DH/5ZqI7LQgNuOboADMUhRgcnl8dktV1zrbIuogzENOuuLUcDeGv8WiKh4ualP2tqrGmdQbiTDk8WbLq2HM6EO/oGuBHTfXFtGWh67t8dOU6jnAKuhoTDri1v0xKcL2l33eArcGhL8wHhFx4/3XEpRCahd1r7DWEpNKdLRidXlu7JZlICq6wvUG4Ga16O3tp9+ZY656aj6wJHQrD3Rge34o21HH0CjashiPbHv/ex9hh1atuqFCpAVet8J8cZUU7E7GtuEUbdvMwnVm45qlkNzFwPJYh4bpZ0WAxehm7BFeBsG2B0vV3ljQM1rZcR5gRmKrbM9jl6HcgrXAr97megXLHlnRVcdjFw06WEMB9pX/WB+D43YuuZS32J/ARhr1OnJTFZ8w29C9SBNfV4vetnLrNpOxT8BWh07BjujplpAAAAAElFTkSuQmCC)
+			center center no-repeat;
+		cursor: grab;
+	}
+	.bpmn-start {
+		background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg==)
+			center center no-repeat;
+		cursor: grab;
+	}
+	.bpmn-end {
+		background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC)
+			center center no-repeat;
+		cursor: grab;
+	}
+	.bpmn-user {
+		background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==)
+			center center no-repeat;
+		cursor: grab;
+	}
+	.bpmn-exclusiveGateway {
+		background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAAHeEJUAAAAABGdBTUEAALGPC/xhBQAAAvVJREFUOBGNVEFrE0EU/mY3bQoiFlOkaUJrQUQoWMGePLX24EH0IIoHKQiCV0G8iE1covgLiqA/QTzVm1JPogc9tIJYFaQtlhQxqYjSpunu+L7JvmUTU3AgmTfvffPNN++9WSA1DO182f6xwILzD5btfAoQmwL5KJEwiQyVbSVZ0IgRyV6PTpIJ81E5ZvqfHQR0HUOBHW4L5Et2kQ6Zf7iAOhTFAA8s0pEP7AXO1uAA52SbqGk6h/6J45LaLhO64ByfcUzM39V7ZiAdS2yCePPEIQYvTUHqM/n7dgQNfBKWPjpF4ISk8q3J4nB11qw6X8l+FsF3EhlkEMfrjIer3wJTLwS2aCNcj4DbGxXTw00JmAuO+Ni6bBxVUCvS5d9aa04+so4pHW5jLTywuXAL7jJ+D06sl82Sgl2JuVBQn498zkc2bGKxULHjCnSMadBKYDYYHAtsby1EQ5lNGrQd4Y3v4Zo0XdGEmDno46yCM9Tk+RiJmUYHS/aXHPNTcjxcbTFna000PFJHIVZ5lFRqRpJWk9/+QtlOUYJj9HG5pVFEU7zqIYDVsw2s+AJaD8wTd2umgSCCyUxgGsS1Y6TBwXQQTFuZaHcd8gAGioE90hlsY+wMcs30RduYtxanjMGal8H5dMW67dmT1JFtYUEe8LiQLRsPZ6IIc7A4J5tqco3T0pnv/4u0kyzrYUq7gASuEyI8VXKvB9Odytv6jS/PNaZBln0nioJG/AVQRZvApOdhjj3Jt8QC8Im09SafwdBdvIpztpxWxpeKCC+EsFdS8DCyuCn2munFpL7ctHKp+Xc5cMybeIyMAN33SPL3ZR9QV1XVwLyzHm6Iv0/yeUuUb7PPlZC4D4HZkeu6dpF4v9j9MreGtMbxMMRLIcjJic9yHi7WQ3yVKzZVWUr5UrViJvn1FfUlwe/KYVfYyWRLSGNu16hR01U9IacajXPei0wx/5BqgInvJN+MMNtNme7ReU9SBbgntovn0kKHpFg7UogZvaZiOue/q1SBo9ktHzQAAAAASUVORK5CYII=)
+			center center no-repeat;
+		cursor: grab;
+	}
+}
+</style>

+ 48 - 0
Web/src/views/approvalFlow/component/LogicFlow/Property/PropertyCommon.vue

@@ -0,0 +1,48 @@
+<template>
+	<div class="property-common">
+		<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto" :rules="rules">
+			<el-form-item v-show="false">
+				<el-input v-model="nodeData.id" />
+			</el-form-item>
+			<el-tabs>
+				<el-tab-pane label="基本信息">
+					<el-row :gutter="35">
+						<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+							<el-form-item label="编号" prop="id">
+								<el-input v-model="nodeData.id" placeholder="请输入编号" maxlength="32" readonly show-word-limit clearable />
+							</el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+							<el-form-item label="属性" prop="type">
+								<el-input v-model="nodeData.type" placeholder="请输入编号" maxlength="32" readonly show-word-limit clearable />
+							</el-form-item>
+						</el-col>
+					</el-row>
+				</el-tab-pane>
+				<el-tab-pane label="扩展信息">
+					<el-row :gutter="35"></el-row>
+				</el-tab-pane>
+			</el-tabs>
+		</el-form>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from 'vue';
+import type { FormRules } from 'element-plus';
+
+var props = defineProps({
+	nodeData: Object,
+});
+const emit = defineEmits(['setProperties']);
+
+const ruleFormRef = ref();
+
+const state = reactive({
+	ruleForm: {} as any,
+});
+
+const rules = ref<FormRules>({});
+</script>
+
+<style lang="scss" scoped></style>

+ 30 - 0
Web/src/views/approvalFlow/component/LogicFlow/Property/PropertyDialog.vue

@@ -0,0 +1,30 @@
+<template>
+	<div class="property-dialog">
+		<PropertyCommon :nodeData="nodeData" @setProperties="setProperties"></PropertyCommon>
+	</div>
+</template>
+
+<script setup>
+import { reactive, ref } from 'vue';
+
+import PropertyCommon from '/@/views/approvalFlow/component/LogicFlow/Property/PropertyCommon.vue';
+
+var props = defineProps({
+	lf: Object,
+	nodeData: Object,
+});
+const emit = defineEmits(['setPropertiesFinish']);
+
+const ruleFormRef = ref();
+
+const state = reactive({});
+
+const setProperties = () => {};
+</script>
+
+<style lang="scss" scoped>
+.property-dialog {
+	padding-left: 20px;
+	padding-right: 20px;
+}
+</style>

+ 55 - 0
Web/src/views/approvalFlow/component/LogicFlow/Register/Edges/EdgeSql.ts

@@ -0,0 +1,55 @@
+import { BezierEdge, BezierEdgeModel } from '@logicflow/core';
+
+class CustomEdge2 extends BezierEdge { }
+
+class CustomEdgeModel2 extends BezierEdgeModel {
+    getEdgeStyle() {
+        const style = super.getEdgeStyle();
+        // svg属性
+        style.strokeWidth = 1;
+        style.stroke = '#ababac';
+        return style;
+    }
+    /**
+     * 重写此方法,使保存数据是能带上锚点数据。
+     */
+    getData() {
+        const data = super.getData();
+        data.sourceAnchorId = this.sourceAnchorId;
+        data.targetAnchorId = this.targetAnchorId;
+        return data;
+    }
+    /**
+     * 给边自定义方案,使其支持基于锚点的位置更新边的路径
+     */
+    updatePathByAnchor() {
+        // TODO
+        const sourceNodeModel = this.graphModel.getNodeModelById(this.sourceNodeId);
+        const sourceAnchor = sourceNodeModel
+            .getDefaultAnchor()
+            .find((anchor) => anchor.id === this.sourceAnchorId);
+        const targetNodeModel = this.graphModel.getNodeModelById(this.targetNodeId);
+        const targetAnchor = targetNodeModel
+            .getDefaultAnchor()
+            .find((anchor) => anchor.id === this.targetAnchorId);
+        const startPoint = {
+            x: sourceAnchor.x,
+            y: sourceAnchor.y,
+        };
+        this.updateStartPoint(startPoint);
+        const endPoint = {
+            x: targetAnchor.x,
+            y: targetAnchor.y,
+        };
+        this.updateEndPoint(endPoint);
+        // 这里需要将原有的pointsList设置为空,才能触发bezier的自动计算control点。
+        this.pointsList = [];
+        this.initPoints();
+    }
+}
+
+export default {
+    type: 'edge-sql',
+    view: CustomEdge2,
+    model: CustomEdgeModel2,
+};

+ 79 - 0
Web/src/views/approvalFlow/component/LogicFlow/Register/Nodes/NodeEnd.ts

@@ -0,0 +1,79 @@
+import { CircleNode, CircleNodeModel, h } from "@logicflow/core";
+
+class EndNode extends CircleNode {
+    getIconShape() {
+        const { model } = this.props;
+        const { x, y, width, height } = model;
+        const stroke = '#404040';
+        return h(
+            'svg',
+            {
+                x: x - width / 2,
+                y: y - height / 2,
+                width: 40,
+                height: 40,
+                viewBox: '0 0 1024 1024'
+            },
+            h(
+                'path',
+                {
+                    fill: stroke,
+                    d: 'M212.992 526.336 212.992 526.336 212.992 526.336 215.04 526.336 212.992 526.336Z'
+                }
+            ),
+            h(
+                'path',
+                {
+                    fill: stroke,
+                    d: 'M724.992 296.96 724.992 296.96 296.96 296.96 296.96 724.992 724.992 724.992 724.992 296.96Z'
+                }
+            )
+        );
+    }
+    getShape() {
+        const { model } = this.props
+        const { x, y, r } = model
+        const { fill, stroke, strokeWidth } = model.getNodeStyle()
+        return h('g', {}, [h('circle', { cx: x, cy: y, r, fill, stroke, strokeWidth }), this.getIconShape()]);
+    }
+}
+
+class EndNodeModel extends CircleNodeModel {
+    initNodeData(data) {
+        data.text = {
+            value: (data.text && data.text.value) || '',
+            x: data.x,
+            y: data.y + 35
+        }
+        super.initNodeData(data)
+        this.r = 20
+    }
+    // 自定义锚点样式
+    getAnchorStyle() {
+        const style = super.getAnchorStyle()
+        style.hover.r = 8
+        style.hover.fill = 'rgb(24, 125, 255)'
+        style.hover.stroke = 'rgb(24, 125, 255)'
+        return style
+    }
+    // 自定义节点outline
+    getOutlineStyle() {
+        const style = super.getOutlineStyle()
+        style.stroke = '#88f'
+        return style
+    }
+    getConnectedSourceRules() {
+        const rules = super.getConnectedSourceRules()
+        const notAsTarget = {
+            message: '终止节点不能作为连线的起点',
+            validate: () => false
+        }
+        rules.push(notAsTarget)
+        return rules
+    }
+}
+export default {
+    type: 'end-node',
+    view: EndNode,
+    model: EndNodeModel
+}

+ 150 - 0
Web/src/views/approvalFlow/component/LogicFlow/Register/Nodes/NodeSql.ts

@@ -0,0 +1,150 @@
+import { HtmlNode, HtmlNodeModel, h } from '@logicflow/core';
+
+class SqlNode extends HtmlNode {
+    /**
+     * 1.1.7版本后支持在view中重写锚点形状。
+     * 重写锚点新增
+     */
+    getAnchorShape(anchorData) {
+        const { x, y, type } = anchorData;
+        return h('rect', {
+            x: x - 5,
+            y: y - 5,
+            width: 10,
+            height: 10,
+            className: `custom-anchor ${type === 'left' ? 'incomming-anchor' : 'outgoing-anchor'
+                }`,
+        });
+    }
+    setHtml(rootEl) {
+        rootEl.innerHTML = '';
+        const {
+            properties: { fields, tableName },
+        } = this.props.model;
+        rootEl.setAttribute('class', 'table-container');
+        const container = document.createElement('div');
+        container.className = `table-node table-color-${Math.ceil(
+            Math.random() * 4,
+        )}`;
+        const tableNameElement = document.createElement('div');
+        tableNameElement.innerText = tableName;
+        tableNameElement.className = 'table-name';
+        container.appendChild(tableNameElement);
+        const fragment = document.createDocumentFragment();
+        for (let i = 0; i < fields.length; i++) {
+            const item = fields[i];
+            const itemElement = document.createElement('div');
+            itemElement.className = 'table-feild';
+            const itemKey = document.createElement('span');
+            itemKey.innerText = item.key;
+            const itemType = document.createElement('span');
+            itemType.innerText = item.type;
+            itemType.className = 'feild-type';
+            itemElement.appendChild(itemKey);
+            itemElement.appendChild(itemType);
+            fragment.appendChild(itemElement);
+        }
+        container.appendChild(fragment);
+        rootEl.appendChild(container);
+    }
+}
+
+class SqlNodeModel extends HtmlNodeModel {
+    /**
+     * 给model自定义添加字段方法
+     */
+    addField(item) {
+        this.properties.fields.unshift(item);
+        this.setAttributes();
+        // 为了保持节点顶部位置不变,在节点变化后,对节点进行一个位移,位移距离为添加高度的一半。
+        this.move(0, 24 / 2);
+        // 更新节点连接边的path
+        this.incoming.edges.forEach((egde) => {
+            // 调用自定义的更新方案
+            egde.updatePathByAnchor();
+        });
+        this.outgoing.edges.forEach((edge) => {
+            // 调用自定义的更新方案
+            edge.updatePathByAnchor();
+        });
+    }
+    getOutlineStyle() {
+        const style = super.getOutlineStyle();
+        style.stroke = 'none';
+        style.hover.stroke = 'none';
+        return style;
+    }
+    // 如果不用修改锚地形状,可以重写颜色相关样式
+    getAnchorStyle(anchorInfo) {
+        const style = super.getAnchorStyle();
+        if (anchorInfo.type === 'left') {
+            style.fill = 'red';
+            style.hover.fill = 'transparent';
+            style.hover.stroke = 'transpanrent';
+            style.className = 'lf-hide-default';
+        } else {
+            style.fill = 'green';
+        }
+        return style;
+    }
+    setAttributes() {
+        this.width = 200;
+        const {
+            properties: { fields },
+        } = this;
+        this.height = 60 + fields.length * 24;
+        const circleOnlyAsTarget = {
+            message: '只允许从右边的锚点连出',
+            validate: (sourceNode, targetNode, sourceAnchor) => {
+                return sourceAnchor.type === 'right';
+            },
+        };
+        this.sourceRules.push(circleOnlyAsTarget);
+        this.targetRules.push({
+            message: '只允许连接左边的锚点',
+            validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
+                return targetAnchor.type === 'left';
+            },
+        });
+    }
+    getDefaultAnchor() {
+        const {
+            id,
+            x,
+            y,
+            width,
+            height,
+            isHovered,
+            isSelected,
+            properties: { fields, isConnection },
+        } = this;
+        const anchors = [];
+        fields.forEach((feild, index) => {
+            // 如果是连出,就不显示左边的锚点
+            if (isConnection || !(isHovered || isSelected)) {
+                anchors.push({
+                    x: x - width / 2 + 10,
+                    y: y - height / 2 + 60 + index * 24,
+                    id: `${id}_${feild.key}_left`,
+                    edgeAddable: false,
+                    type: 'left',
+                });
+            }
+            if (!isConnection) {
+                anchors.push({
+                    x: x + width / 2 - 10,
+                    y: y - height / 2 + 60 + index * 24,
+                    id: `${id}_${feild.key}_right`,
+                    type: 'right',
+                });
+            }
+        });
+        return anchors;
+    }
+}
+
+export default {
+    type: 'sql-node',
+    model: SqlNodeModel,
+    view: SqlNode,
+};

+ 64 - 0
Web/src/views/approvalFlow/component/LogicFlow/Register/Nodes/NodeStart.ts

@@ -0,0 +1,64 @@
+import { CircleNode, CircleNodeModel, h } from "@logicflow/core";
+
+class StartNode extends CircleNode {
+    getLabelShape() {
+        const { model } = this.props;
+        const { x, y } = model;
+        return h('text', { fill: '#000000', fontSize: 12, x: x - 12, y: y + 4, width: 50, height: 25 }, 'Start');
+    }
+    getShape() {
+        const { model } = this.props;
+        const { x, y, r } = model;
+        const { fill, stroke, strokeWidth } = model.getNodeStyle();
+        return h('g', {}, [h('circle', { cx: x, cy: y, r, fill, stroke, strokeWidth }), this.getLabelShape()]);
+    }
+}
+
+class StartModel extends CircleNodeModel {
+    // 自定义节点形状属性
+    initNodeData(data) {
+        data.text = {
+            value: (data.text && data.text.value) || '',
+            x: data.x,
+            y: data.y + 35,
+            dragable: false,
+            editable: true
+        }
+        super.initNodeData(data)
+        this.r = 20
+    }
+    // 自定义节点样式属性
+    getNodeStyle() {
+        const style = super.getNodeStyle()
+        return style
+    }
+    // 自定义锚点样式
+    getAnchorStyle() {
+        const style = super.getAnchorStyle()
+        style.hover.r = 8
+        style.hover.fill = 'rgb(24, 125, 255)'
+        style.hover.stroke = 'rgb(24, 125, 255)'
+        return style
+    }
+    // 自定义节点outline
+    getOutlineStyle() {
+        const style = super.getOutlineStyle()
+        style.stroke = '#88f'
+        return style
+    }
+    getConnectedTargetRules() {
+        const rules = super.getConnectedTargetRules()
+        const notAsTarget = {
+            message: '起始节点不能作为连线的终点',
+            validate: () => false
+        }
+        rules.push(notAsTarget)
+        return rules
+    }
+}
+
+export default {
+    type: 'start-node',
+    view: StartNode,
+    model: StartModel
+}

+ 25 - 0
Web/src/views/approvalFlow/component/LogicFlow/Register/Nodes/NodeTask.ts

@@ -0,0 +1,25 @@
+import { RectNode, RectNodeModel, h } from "@logicflow/core";
+
+class TaskNode extends RectNode {
+    getShare() {
+        const { model } = this.props;
+        const { width, height, x, y } = model;
+        const position = {
+            x: x - width / 2,
+            y: y - height / 2,
+        }
+        const style = model.getNodeStyle();
+        return h('rect', { ...style, ...position });
+    }
+}
+class TaskNodeModel extends RectNodeModel {
+    constructor(data, graphModel) {
+        super(data, graphModel);
+        this.radius = 20;
+    }
+}
+export default {
+    type: 'task-node',
+    view: TaskNode,
+    model: TaskNodeModel,
+}

+ 110 - 0
Web/src/views/approvalFlow/component/LogicFlow/Register/Nodes/NodeUser.ts

@@ -0,0 +1,110 @@
+import { PolygonNode, PolygonNodeModel, h } from "@logicflow/core";
+
+class UserNode extends PolygonNode {
+    getIconShape() {
+        const { model } = this.props
+        const { stroke } = model.getNodeStyle()
+        return h(
+            'svg',
+            {
+                x: 20,
+                y: 18,
+                width: 30,
+                height: 30,
+                viewBox: '0 0 1126 1024'
+            },
+            h(
+                'path',
+                {
+                    fill: stroke,
+                    d: 'M792.576 379.392a25.6 25.6 0 0 0 25.2928 25.8048h283.2384A25.6 25.6 0 0 0 1126.4 379.392a25.6 25.6 0 0 0-25.2928-25.8048h-283.2384a25.6 25.6 0 0 0-25.344 25.8048z m303.9232 80.7424H761.856c-16.5376 0-29.9008 11.6224-29.9008 25.7536 0 14.1824 13.312 25.7536 29.9008 25.7536h334.6432c16.4864 0 29.9008-11.5712 29.9008-25.7536 0-14.1312-13.4144-25.7536-29.9008-25.7536z m4.608 106.496h-283.2384a25.6 25.6 0 0 0-25.344 25.7536 25.6 25.6 0 0 0 25.344 25.7536h283.2384A25.6 25.6 0 0 0 1126.4 592.384a25.6 25.6 0 0 0-25.2928-25.8048zM543.0272 1024H341.6576C150.8352 1024 0 1024 0 923.648v-20.1216c0-188.16 153.2928-341.1968 341.7088-341.1968h201.2672c188.416 0 341.76 153.0368 341.76 341.1968v20.0704C884.6848 1024 726.3232 1024 542.976 1024z m-203.1616-405.1456c-158.464 0-287.4368 128.4096-287.4368 286.208v20.48c0 40.9088 166.0928 40.9088 287.4368 40.9088h204.9536c100.4544 0 287.4368 0 287.4368-40.96v-20.3776c0-157.8496-128.9728-286.208-287.4368-286.208H339.8656z m92.416-76.7488a271.4112 271.4112 0 0 1-271.2064-271.0528A271.36 271.36 0 0 1 432.2816 0a271.36 271.36 0 0 1 271.2064 271.0528 271.4624 271.4624 0 0 1-271.2064 271.0528z m-215.3472-271.872c0 118.1696 96.6144 214.3232 215.3472 214.3232 118.784 0 215.3984-96.1536 215.3984-214.3232 0-118.2208-96.6144-214.3232-215.3984-214.3232S216.9344 152.0128 216.9344 270.2336z'
+                }
+            )
+        )
+    }
+    getShape() {
+        const { model } = this.props
+        const { width, height, x, y, points } = model
+        const { fill, fillOpacity, strokeWidth, stroke, strokeOpacity, } = model.getNodeStyle()
+        const transform = `matrix(1 0 0 1 ${x - width / 2} ${y - height / 2})`
+        const pointsPath = points.map(point => point.join(',')).join(' ')
+        return h(
+            'g',
+            {
+                transform
+            },
+            [
+                h(
+                    'polygon',
+                    {
+                        points: pointsPath,
+                        fill,
+                        stroke,
+                        strokeWidth,
+                        strokeOpacity,
+                        fillOpacity
+                    }
+                ),
+                this.getIconShape()
+            ]
+        )
+    }
+}
+class UserNodeModel extends PolygonNodeModel {
+    constructor(data, graphModel) {
+        data.text = {
+            value: (data.text && data.text.value) || '',
+            x: data.x,
+            y: data.y + 50
+        }
+        super(data, graphModel)
+        // 右键菜单自由配置,也可以通过边的properties或者其他属性条件更换不同菜单
+        this.menu = [
+            {
+                className: 'lf-menu-delete',
+                text: 'delete',
+                callback(node) {
+                    // const comfirm = window.confirm('你确定要删除吗?')
+                    lf.deleteNode(node.id)
+                }
+            },
+            {
+                text: 'edit',
+                className: 'lf-menu-item',
+                callback(node) {
+                    lf.editText(node.id)
+                }
+            },
+            {
+                text: 'copy',
+                className: 'lf-menu-item',
+                callback(node) {
+                    lf.cloneNode(node.id)
+                }
+            }
+        ]
+    }
+    initNodeData(data) {
+        super.initNodeData(data)
+        const lenght = 35
+        this.points = [
+            [lenght, 0],
+            [lenght * 2, lenght],
+            [lenght, lenght * 2],
+            [0, lenght]
+        ]
+    }
+    // 自定义锚点样式
+    getAnchorStyle() {
+        const style = super.getAnchorStyle();
+        style.hover.r = 8;
+        style.hover.fill = "rgb(24, 125, 255)";
+        style.hover.stroke = "rgb(24, 125, 255)";
+        return style;
+    }
+}
+export default {
+    type: 'user-node',
+    view: UserNode,
+    model: UserNodeModel
+}

+ 9 - 0
Web/src/views/approvalFlow/component/LogicFlow/Register/RegisterEdge.ts

@@ -0,0 +1,9 @@
+import LogicFlow from "@logicflow/core";
+// 引入自定义的边
+import edgeSql from './Edges/EdgeSql';
+// 注册边
+const Register = (lf: LogicFlow) => {
+    lf.register(edgeSql);
+};
+
+export default { Register };

+ 17 - 0
Web/src/views/approvalFlow/component/LogicFlow/Register/RegisterNode.ts

@@ -0,0 +1,17 @@
+import LogicFlow from "@logicflow/core";
+// 引入自定义节点
+import nodeStart from './Nodes/NodeStart';
+import nodeEnd from './Nodes/NodeEnd';
+import nodeTask from './Nodes/NodeTask';
+import nodeUser from './Nodes/NodeUser';
+import nodeSql from './Nodes/NodeSql';
+// 注册节点
+const Register = (lf: LogicFlow) => {
+    lf.register(nodeStart);
+    lf.register(nodeEnd);
+    lf.register(nodeTask);
+    lf.register(nodeUser);
+    lf.register(nodeSql);
+};
+
+export default { Register };

+ 125 - 0
Web/src/views/approvalFlow/component/detailDialog.vue

@@ -0,0 +1,125 @@
+<template lang="">
+	<div class="flow-container">
+		<el-dialog v-model="state.isShowDialog" :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>
+				</div>
+			</template>
+			<el-timeline>
+				<el-timeline-item
+					v-for="(activity, index) in state.activities"
+					:key="index"
+					:icon="activity.icon"
+					:type="activity.type"
+					:color="activity.color"
+					:size="activity.size"
+					:hollow="activity.hollow"
+					:timestamp="activity.timestamp"
+					placement="top"
+				>
+					<el-card>
+						<h4>{{ activity.content }}</h4>
+						<br />
+						<div class="demo-type">
+							<el-avatar :icon="UserFilled" />
+						</div>
+					</el-card>
+				</el-timeline-item>
+			</el-timeline>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="cancel">取 消</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref, onMounted } from 'vue';
+import type { FormRules } from 'element-plus';
+
+var props = defineProps({
+	title: {
+		type: String,
+		default: '',
+	},
+});
+
+const emit = defineEmits(['reloadTable']);
+const ruleFormRef = ref();
+
+const state = reactive({
+	loading: false,
+	isShowDialog: false,
+	ruleForm: {},
+	circleUrl: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
+	activities: [
+		{
+			content: 'Event start',
+			timestamp: '2018-04-12 20:46',
+			size: 'large',
+			type: 'primary',
+			user: 'admin',
+		},
+		{
+			content: 'Approved',
+			timestamp: '2018-04-13',
+			color: '#0bbd87',
+			user: 'admin',
+		},
+		{
+			content: 'Approved',
+			timestamp: '2018-04-11',
+			color: '#0bbd87',
+			user: 'admin',
+		},
+		{
+			content: 'Success',
+			timestamp: '2018-04-11 20:46',
+			hollow: true,
+			user: 'admin',
+		},
+	],
+});
+
+const rules = ref<FormRules>({});
+
+onMounted(() => {});
+
+const openDialog = (row: any) => {
+	state.ruleForm = JSON.parse(JSON.stringify(row));
+	state.isShowDialog = true;
+};
+
+const closeDialog = () => {
+	emit('reloadTable');
+	state.isShowDialog = false;
+};
+
+const cancel = () => {
+	state.isShowDialog = false;
+};
+
+defineExpose({ openDialog });
+</script>
+
+<style scoped lang="scss">
+:deep(.el-select),
+:deep(.el-input-number) {
+	width: 100%;
+}
+.demo-type {
+	display: flex;
+}
+.demo-type > div {
+	flex: 1;
+	text-align: center;
+}
+
+.demo-type > div:not(:last-child) {
+	border-right: 1px solid var(--el-border-color);
+}
+</style>

+ 162 - 0
Web/src/views/approvalFlow/component/editDialog.vue

@@ -0,0 +1,162 @@
+<template>
+	<div class="labApprovalFlow-container">
+		<el-dialog v-model="state.isShowDialog" :width="800" draggable="">
+			<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>
+				</div>
+			</template>
+			<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto" :rules="rules">
+				<el-tabs>
+					<el-tab-pane label="基本信息">
+						<el-row :gutter="35">
+							<el-form-item v-show="false">
+								<el-input v-model="state.ruleForm.id" />
+							</el-form-item>
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+								<el-form-item label="编号" prop="code">
+									<el-input v-model="state.ruleForm.code" placeholder="请输入编号" maxlength="32" show-word-limit clearable />
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+								<el-form-item label="名称" prop="name" :rules="[{ required: true, message: '名称不能为空', trigger: 'blur' }]">
+									<el-input v-model="state.ruleForm.name" placeholder="请输入名称" maxlength="32" show-word-limit clearable />
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+								<el-form-item label="状态" prop="status" :rules="[{ required: true, message: '状态不能为空', trigger: 'blur' }]">
+									<el-select clearable v-model="state.ruleForm.status" placeholder="请选择状态">
+										<el-option v-for="(item, index) in getEnumStatusData" :key="index" :value="item.value" :label="`${item.describe} [${item.value}]`"></el-option>
+									</el-select>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+								<el-form-item label="备注" prop="remark">
+									<el-input v-model="state.ruleForm.remark" placeholder="请输入备注" type="textarea" maxlength="255" show-word-limit clearable />
+								</el-form-item>
+							</el-col>
+						</el-row>
+					</el-tab-pane>
+					<el-tab-pane label="扩展信息">
+						<el-row :gutter="35">
+							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+								<el-form-item label="表单" prop="formJson">
+									<el-input v-model="state.ruleForm.formJson" placeholder="请输入表单" type="textarea" maxlength="0" show-word-limit clearable />
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+								<el-form-item label="流程" prop="flowJson">
+									<el-input v-model="state.ruleForm.flowJson" placeholder="请输入流程" type="textarea" maxlength="0" show-word-limit clearable />
+								</el-form-item>
+							</el-col>
+						</el-row>
+					</el-tab-pane>
+				</el-tabs>
+			</el-form>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="cancel">取 消</el-button>
+					<el-button type="primary" @click="submit">确 定</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref, onMounted } from 'vue';
+import { ElMessage } from 'element-plus';
+import type { FormRules } from 'element-plus';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { ApprovalFlowApi } from '/@/api-services/_approvalFlow/api';
+import { SysEnumApi } from '/@/api-services/api';
+
+const getEnumStatusData = ref<any>([]);
+
+// 父级传递来的参数
+var props = defineProps({
+	title: {
+		type: String,
+		default: '',
+	},
+	labStatus: {
+		type: Array,
+		default: () => [],
+	},
+});
+// 父级传递来的函数,用于回调
+const emit = defineEmits(['reloadTable']);
+
+// 定义变量内容
+const ruleFormRef = ref();
+const state = reactive({
+	loading: false,
+	isShowDialog: false,
+	ruleForm: {} as any,
+});
+
+// 自行添加其他规则
+const rules = ref<FormRules>({
+	name: [
+		{
+			pattern: /^(?!^[0-9].*$).*/,
+			message: '不能以数字开头',
+			trigger: 'blur',
+		},
+	],
+});
+
+// 页面加载时
+onMounted(async () => {
+	// getEnumStatusData.value = (await getAPI(SysEnumApi).apiSysEnumEnumDataListGet('LabStatusEnum')).data.result ?? [];
+});
+
+// 打开弹窗
+const openDialog = async (row: any) => {
+	let rowData = JSON.parse(JSON.stringify(row));
+	state.ruleForm = rowData.id ? (await getAPI(ApprovalFlowApi).apiApprovalFlowDetailGet(rowData.id)).data.result : rowData;
+	state.isShowDialog = true;
+};
+
+// 关闭弹窗
+const closeDialog = () => {
+	emit('reloadTable');
+	state.isShowDialog = false;
+};
+
+// 取消
+const cancel = () => {
+	state.isShowDialog = false;
+};
+
+// 提交
+const submit = async () => {
+	ruleFormRef.value.validate(async (isValid: boolean, fields?: any) => {
+		if (isValid) {
+			if (state.ruleForm.id == undefined || state.ruleForm.id == null || state.ruleForm.id == 0) {
+				await getAPI(ApprovalFlowApi).apiApprovalFlowAddPost(state.ruleForm);
+			} else {
+				await getAPI(ApprovalFlowApi).apiApprovalFlowUpdatePost(state.ruleForm);
+			}
+			closeDialog();
+		} else {
+			ElMessage({
+				message: `表单有${Object.keys(fields).length}处验证失败,请修改后再提交`,
+				type: 'error',
+			});
+		}
+	});
+};
+
+// 将属性或者函数暴露给父组件
+defineExpose({ openDialog });
+</script>
+
+<style scoped lang="scss">
+:deep(.el-select),
+:deep(.el-input-number) {
+	width: 100%;
+}
+</style>

+ 264 - 0
Web/src/views/approvalFlow/component/editFlowDialog.vue

@@ -0,0 +1,264 @@
+<template>
+	<div class="flow-container">
+		<el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" fullscreen>
+			<template #header>
+				<div style="color: #fff">
+					<span>{{ props.title }}</span>
+				</div>
+			</template>
+			<div class="f-content">
+				<div class="f-container">
+					<div class="f-switch">
+						<el-switch v-model="state.value2" @change="change" class="mb-2" active-text="打开框选" inactive-text="关闭框选" />
+					</div>
+					<PanelControl v-if="lf" :lf="lf" @catData="getData"></PanelControl>
+					<div class="f-container-c" ref="container" id="container"></div>
+					<PanelNode v-if="lf" :lf="lf"></PanelNode>
+					<el-drawer title="属性" v-model="drawer" :direction="direction" size="500px" :before-close="handleClose">
+						<PropertyDialog v-if="drawer" :nodeData="state.nodeData" :lf="lf" @setPropertiesFinish="handleClose"></PropertyDialog>
+					</el-drawer>
+					<el-dialog title="数据" v-model="dataVisible" width="50%">
+						<PanelDataDialog :graphData="state.graphData"></PanelDataDialog>
+					</el-dialog>
+				</div>
+			</div>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="cancel">取 消</el-button>
+					<el-button type="primary" @click="submit">确 定</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref, nextTick } from 'vue';
+import { ElMessageBox } from 'element-plus';
+
+import LogicFlow from '@logicflow/core';
+import { BpmnElement, InsertNodeInPolyline, Menu, MiniMap, SelectionSelect, Snapshot } from '@logicflow/extension';
+import '@logicflow/core/dist/style/index.css';
+import '@logicflow/extension/lib/style/index.css';
+
+import RegisterEdge from './LogicFlow/Register/RegisterEdge';
+import RegisterNode from './LogicFlow/Register/RegisterNode';
+import PanelNode from './LogicFlow/Panel/PanelNode.vue';
+import PanelControl from './LogicFlow/Panel/PanelControl.vue';
+import PanelDataDialog from './LogicFlow/Panel/PanelDataDialog.vue';
+import PropertyDialog from './LogicFlow/Property/PropertyDialog.vue';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { ApprovalFlowApi } from '/@/api-services/_approvalFlow/api';
+import { ApprovalFlowOutput, UpdateApprovalFlowInput } from '/@/api-services/_approvalFlow/models';
+
+var props = defineProps({
+	title: {
+		type: String,
+		default: '',
+	},
+});
+
+const emit = defineEmits(['reloadTable', 'updateFlow']);
+const flowData = ref({});
+const lf = ref<InstanceType<typeof LogicFlow>>();
+
+const drawer = ref(false);
+const direction = ref('rtl');
+const dataVisible = ref(false);
+
+const state = reactive({
+	loading: false,
+	isShowDialog: false,
+	ruleSource: {} as UpdateApprovalFlowInput,
+	nodeData: {},
+	graphData: {},
+});
+
+const openDialog = (row: ApprovalFlowOutput) => {
+	state.ruleSource = row as UpdateApprovalFlowInput;
+	// 初始化数据
+	if (state.ruleSource.flowJson) {
+		flowData.value = JSON.parse(state.ruleSource.flowJson);
+	} else {
+		flowData.value = {
+			nodes: [],
+			edges: [],
+		};
+	}
+	state.isShowDialog = true;
+	nextTick(() => {
+		// 初始化画布
+		initGraph();
+	});
+	console.log('open');
+};
+
+const closeDialog = () => {
+	emit('reloadTable');
+	state.isShowDialog = false;
+	console.log('close');
+};
+
+const cancel = () => {
+	state.isShowDialog = false;
+	console.log('cancel');
+};
+
+// 保存流程设计
+const submit = async () => {
+	flowData.value = lf.value?.getGraphData();
+	state.ruleSource.flowJson = JSON.stringify(flowData.value);
+	await getAPI(ApprovalFlowApi).apiApprovalFlowUpdatePost(state.ruleSource);
+	emit('updateFlow', flowData.value);
+	closeDialog();
+};
+
+const initGraph = () => {
+	// 初始化画布
+	const container: HTMLElement = document.querySelector('#container')!;
+	// 配置项
+	const config = {
+		stopScrollGraph: true, // 禁止鼠标滚动移动画布
+		stopZoomGraph: true, // 禁止缩放
+		metaKeyMultipleSelected: true,
+		// 背景网格大小
+		grid: {
+			size: 10,
+			type: 'dot',
+		},
+		// 快捷键
+		keyboard: {
+			enabled: true,
+		},
+		// 辅助线
+		snapline: true,
+	};
+	lf.value = new LogicFlow({
+		...config,
+		plugins: [
+			BpmnElement,
+			// 作栋节点自动插入边
+			InsertNodeInPolyline,
+			// 右键菜单
+			Menu,
+			// 迷你图
+			MiniMap,
+			// 框选
+			SelectionSelect,
+			// 快照
+			Snapshot,
+		],
+		container: container,
+		width: container.clientWidth,
+		height: container.clientHeight,
+	});
+	// 设置主题
+	lf.value.setTheme({
+		snapline: {
+			stroke: '#1E90FF', // 对齐线颜色
+			strokeWidth: 1, // 对齐线宽度
+		},
+	});
+	// 注册自定义节点
+	RegisterNode.Register(lf.value);
+	// 注册自定义边
+	RegisterEdge.Register(lf.value);
+	// 监听节点点击事件
+	lf.value.on('node:click', ({ data }) => {
+		state.nodeData = data;
+		drawer.value = true;
+	});
+	// 监听边点击事件
+	lf.value.on('edge:click', ({ data }) => {
+		state.nodeData = data;
+		drawer.value = true;
+	});
+	// 渲染数据
+	lf.value.render(flowData.value);
+	// 画布居中
+	lf.value.focusOn({ coordinate: { x: 300, y: 300 } });
+};
+
+// 框选
+const change = (val: boolean) => {
+	if (val) {
+		lf.value?.extension.selectionSelect.openSelectionSelect();
+	} else {
+		lf.value?.extension.selectionSelect.closeSelectionSelect();
+	}
+};
+
+// 获取数据
+const getData = () => {
+	var data = lf.value?.getGraphData();
+	state.graphData = data;
+	dataVisible.value = true;
+};
+
+// 关闭属性界面提醒
+const handleClose = (done: () => void) => {
+	ElMessageBox.confirm('确认要关闭当前属性编辑?')
+		.then(() => {
+			done();
+		})
+		.catch(() => {
+			// catch error
+		});
+};
+
+defineExpose({ openDialog });
+</script>
+
+<style scoped lang="scss">
+:deep(.el-tabs__nav-scroll) {
+	width: 70%;
+	margin: 0 auto;
+}
+.flow-container {
+	:deep(.el-dialog) {
+		.el-dialog__header {
+			display: none !important;
+		}
+		.el-dialog__body {
+			max-height: calc(100vh - 45px) !important;
+		}
+	}
+}
+.f-content {
+	display: flex;
+	flex-grow: 1;
+	z-index: 1;
+	margin: -2px -19px -20px -19px;
+	height: calc(100vh - 100px) !important;
+
+	.f-container {
+		flex-grow: 1;
+		position: relative;
+
+		.f-switch {
+			position: absolute;
+			z-index: 2;
+			top: -22px;
+			left: 5px;
+
+			.el-switch {
+				margin-right: 10px;
+			}
+		}
+
+		.el-drawer {
+			height: 80%;
+			overflow: auto;
+			margin-top: -30px;
+			z-index: !important;
+		}
+
+		.f-container-c {
+			position: absolute;
+			width: 100%;
+			height: 100%;
+		}
+	}
+}
+</style>

+ 154 - 0
Web/src/views/approvalFlow/component/editFormDialog.vue

@@ -0,0 +1,154 @@
+<template lang="">
+	<div class="flow-container">
+		<el-dialog v-model="state.isShowDialog" :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>
+				</div>
+			</template>
+			<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="库定位器" prop="configId">
+							<el-select v-model="state.ruleForm.configId" placeholder="库名" filterable @change="dbChanged()" class="w100">
+								<el-option v-for="item in state.dbData" :key="item.configId" :label="item.configId" :value="item.configId" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="表定位器" prop="tableName" :rules="[{ required: true, message: '表定位器不能为空', trigger: 'blur' }]">
+							<el-select v-model="state.ruleForm.tableName" @change="tableChanged" value-key="value" filterable clearable class="w100">
+								<el-option v-for="item in state.tableData" :key="item.name" :label="item.name + ' [ ' + item.description + ' ]'" :value="item" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="操作" prop="typeName" :rules="[{ required: true, message: '操作不能为空', trigger: 'blur' }]">
+							<el-select v-model="state.ruleForm.typeName" @change="typeChanged" value-key="value" filterable clearable class="w100">
+								<el-option v-for="item in state.typeData" :key="item.name" :label="item.name + ' [ ' + item.description + ' ]'" :value="item" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="cancel">取 消</el-button>
+					<el-button type="primary" @click="submit">确 定</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref, onMounted } from 'vue';
+import type { FormRules } from 'element-plus';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { ApprovalFlowApi } from '/@/api-services/_approvalFlow/api';
+import { ApprovalFlowOutput, UpdateApprovalFlowInput } from '/@/api-services/_approvalFlow/models';
+import { SysDatabaseApi, SysCodeGenApi } from '/@/api-services/api';
+import { DbTableInfo } from '/@/api-services/models';
+
+var props = defineProps({
+	title: {
+		type: String,
+		default: '',
+	},
+});
+
+const emit = defineEmits(['reloadTable']);
+const ruleFormRef = ref();
+
+const state = reactive({
+	loading: false,
+	isShowDialog: false,
+	ruleSource: {} as UpdateApprovalFlowInput,
+	ruleForm: {} as any,
+	dbData: [] as any,
+	tableData: [] as Array<DbTableInfo>,
+	typeData: [
+		{
+			name: 'add',
+			value: 'add',
+			description: '新增',
+		},
+		{
+			name: 'update',
+			value: 'update',
+			description: '更新',
+		},
+		{
+			name: 'delete',
+			value: 'delete',
+			description: '删除',
+		},
+		{
+			name: 'select',
+			value: 'select',
+			description: '查询',
+		},
+		{
+			name: 'export',
+			value: 'export',
+			description: '导出',
+		},
+	],
+});
+
+const rules = ref<FormRules>({});
+
+onMounted(async () => {
+	var resDb = await getAPI(SysCodeGenApi).apiSysCodeGenDatabaseListGet();
+	state.dbData = resDb.data.result;
+});
+
+const openDialog = (row: ApprovalFlowOutput) => {
+	state.ruleSource = row as UpdateApprovalFlowInput;
+	state.ruleForm = row.formJson ? JSON.parse(row.formJson) : {};
+	state.isShowDialog = true;
+};
+
+const closeDialog = () => {
+	emit('reloadTable');
+	state.isShowDialog = false;
+};
+
+const cancel = () => {
+	state.isShowDialog = false;
+};
+
+const submit = () => {
+	state.ruleSource.formJson = JSON.stringify(state.ruleForm);
+	closeDialog();
+};
+
+// db改变
+const dbChanged = async () => {
+	if (state.ruleForm.configId === '') return;
+
+	var res = await getAPI(SysDatabaseApi).apiSysDatabaseTableListConfigIdGet(state.ruleForm.configId);
+	state.tableData = res.data.result ?? [];
+};
+
+// table改变
+const tableChanged = (item: any) => {
+	state.ruleForm.tableName = item.name;
+};
+
+// type改变
+const typeChanged = (item: any) => {
+	state.ruleForm.typeName = item.name;
+};
+
+defineExpose({ openDialog });
+</script>
+
+<style scoped lang="scss">
+:deep(.el-select),
+:deep(.el-input-number) {
+	width: 100%;
+}
+</style>

+ 219 - 0
Web/src/views/approvalFlow/index.vue

@@ -0,0 +1,219 @@
+<template>
+	<div class="labApprovalFlow-container">
+		<el-card shadow="hover" :body-style="{ paddingBottom: '0' }">
+			<el-form :model="state.queryParams" ref="queryForm">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10">
+						<el-form-item label="关键字">
+							<el-input v-model="state.queryParams.searchKey" placeholder="请输入模糊查询关键字" clearable />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI">
+						<el-form-item label="编号">
+							<el-input v-model="state.queryParams.code" placeholder="请输入编号" clearable />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI">
+						<el-form-item label="名称">
+							<el-input v-model="state.queryParams.name" placeholder="请输入名称" clearable />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI">
+						<el-form-item label="备注">
+							<el-input v-model="state.queryParams.remark" placeholder="请输入备注" clearable />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
+						<el-form-item>
+							<el-button-group>
+								<el-button type="primary" icon="ele-Search" @click="handleQuery"> 查询 </el-button>
+								<el-button icon="ele-Refresh" @click="() => (state.queryParams = {})"> 重置 </el-button>
+							</el-button-group>
+							<el-button type="primary" icon="ele-Plus" @click="openAddApprovalFlow" style="margin-left: 30px"> 新增 </el-button>
+							<el-button icon="ele-ArrowDown" @click="changeAdvanceQueryUI" v-if="!showAdvanceQueryUI" style="margin-left: 5px" text> </el-button>
+							<el-button icon="ele-ArrowUp" @click="changeAdvanceQueryUI" v-if="showAdvanceQueryUI" style="margin-left: 5px" text> </el-button>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+		</el-card>
+		<el-card class="full-table" shadow="hover" style="margin-top: 5px">
+			<el-table :data="state.tableData" style="width: 100%" v-loading="state.loading" row-key="id" border="">
+				<el-table-column type="index" label="序号" width="55" align="center" />
+				<el-table-column prop="code" label="编号" width="140" show-overflow-tooltip="" />
+				<el-table-column prop="name" label="名称" show-overflow-tooltip="" />
+				<el-table-column prop="formJson" label="表单" align="center" show-overflow-tooltip="">
+					<template #default="scope">
+						<el-button icon="ele-Edit" size="small" text="" type="primary" @click="openEditFormDialog(scope.row)"> 表单 </el-button>
+					</template>
+				</el-table-column>
+				<el-table-column prop="flowJson" label="流程" align="center" show-overflow-tooltip="">
+					<template #default="scope">
+						<el-button icon="ele-Edit" size="small" text="" type="primary" @click="openEditFlowDialog(scope.row)"> 流程 </el-button>
+					</template>
+				</el-table-column>
+				<el-table-column label="修改记录" width="100" align="center" show-overflow-tooltip>
+					<template #default="scope">
+						<ModifyRecord :data="scope.row" />
+					</template>
+				</el-table-column>
+				<el-table-column label="操作" width="200" align="center" fixed="right" show-overflow-tooltip="">
+					<template #default="scope">
+						<el-button icon="ele-View" size="small" text="" type="primary" @click="openDetailDialog(scope.row)"> 查看 </el-button>
+						<el-button icon="ele-Edit" size="small" text="" type="primary" @click="openEditApprovalFlow(scope.row)"> 编辑 </el-button>
+						<el-button icon="ele-Delete" size="small" text="" type="primary" @click="delApprovalFlow(scope.row)"> 删除 </el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<el-pagination
+				v-model:currentPage="state.tableParams.page"
+				v-model:page-size="state.tableParams.pageSize"
+				:total="state.tableParams.total"
+				:page-sizes="[10, 20, 50, 100, 200, 500]"
+				small=""
+				background=""
+				@size-change="handleSizeChange"
+				@current-change="handleCurrentChange"
+				layout="total, sizes, prev, pager, next, jumper"
+			/>
+		</el-card>
+
+		<detailDialog ref="detailDialogRef" :title="state.dialogTitle" @reloadTable="handleQuery" />
+		<printDialog ref="printDialogRef" :title="state.dialogTitle" @reloadTable="handleQuery" />
+		<editDialog ref="editDialogRef" :title="state.dialogTitle" @reloadTable="handleQuery" />
+		<editFormDialog ref="editFormDialogRef" :title="state.dialogTitle" @reloadTable="handleQuery" />
+		<editFlowDialog ref="editFlowDialogRef" :title="state.dialogTitle" @updateFlow="handleFlow" @reloadTable="handleQuery" />
+	</div>
+</template>
+
+<script lang="ts" setup="" name="approvalFlow">
+import { onMounted, reactive, ref } from 'vue';
+import { ElMessageBox, ElMessage } from 'element-plus';
+// import { auth } from '/@/utils/authFunction';
+
+import printDialog from '/@/views/system/print/component/hiprint/preview.vue';
+import editFormDialog from './component/editFormDialog.vue';
+import detailDialog from './component/detailDialog.vue';
+import editFlowDialog from './component/editFlowDialog.vue';
+import editDialog from './component/editDialog.vue';
+import ModifyRecord from '/@/components/table/modifyRecord.vue';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { ApprovalFlowApi } from '/@/api-services/_approvalFlow/api';
+import { ApprovalFlowInput, ApprovalFlowOutput } from '/@/api-services/_approvalFlow/models';
+
+const showAdvanceQueryUI = ref(false);
+
+const detailDialogRef = ref();
+const editFormDialogRef = ref();
+const editFlowDialogRef = ref();
+const printDialogRef = ref();
+const editDialogRef = ref();
+
+const state = reactive({
+	loading: false,
+	tableData: [] as Array<ApprovalFlowOutput>,
+	queryParams: {} as ApprovalFlowInput,
+	tableParams: {
+		page: 1,
+		pageSize: 20,
+		total: 0 as any,
+	},
+	dialogTitle: '',
+});
+
+onMounted(async () => {
+	handleQuery();
+});
+
+// 改变高级查询的控件显示状态
+const changeAdvanceQueryUI = () => {
+	showAdvanceQueryUI.value = !showAdvanceQueryUI.value;
+};
+
+// 查询操作
+const handleQuery = async () => {
+	state.loading = true;
+	let params = Object.assign(state.queryParams, state.tableParams);
+	var res = await getAPI(ApprovalFlowApi).apiApprovalFlowPagePost(params);
+	state.tableData = res.data.result?.items ?? [];
+	state.tableParams.total = res.data.result?.total;
+	state.loading = false;
+};
+
+// 打开新增页面
+const openAddApprovalFlow = () => {
+	state.dialogTitle = '添加审批流';
+	editDialogRef.value.openDialog({ status: 1 });
+};
+
+// 打开编辑页面
+const openEditApprovalFlow = (row: ApprovalFlowOutput) => {
+	state.dialogTitle = '编辑审批流';
+	editDialogRef.value.openDialog(row);
+};
+
+// 打开打印页面
+const openEditDialog = (row: ApprovalFlowOutput) => {
+	state.dialogTitle = '编辑审批流';
+	editDialogRef.value.openDialog(row);
+};
+
+// 打开打印页面
+const openDetailDialog = (row: ApprovalFlowOutput) => {
+	state.dialogTitle = '查看审批流';
+	detailDialogRef.value.openDialog(row);
+};
+
+const openEditFormDialog = (row: ApprovalFlowOutput) => {
+	state.dialogTitle = '编辑表单';
+	editFormDialogRef.value.openDialog(row);
+};
+
+const openEditFlowDialog = (row: ApprovalFlowOutput) => {
+	state.dialogTitle = '编辑流程';
+	editFlowDialogRef.value.openDialog(row);
+};
+
+const handleFlow = (json: string) => {
+	console.log(JSON.stringify(json));
+	handleQuery();
+};
+
+// 删除
+const delApprovalFlow = (row: ApprovalFlowOutput) => {
+	ElMessageBox.confirm(`确定要删除吗?`, '提示', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	})
+		.then(async () => {
+			if (row.id) {
+				await getAPI(ApprovalFlowApi).apiApprovalFlowDeletePost({ id: row.id });
+				handleQuery();
+				ElMessage.success('删除成功');
+			}
+		})
+		.catch(() => {});
+};
+
+// 改变页面容量
+const handleSizeChange = (val: number) => {
+	state.tableParams.pageSize = val;
+	handleQuery();
+};
+
+// 改变页码序号
+const handleCurrentChange = (val: number) => {
+	state.tableParams.page = val;
+	handleQuery();
+};
+</script>
+
+<style scoped>
+:deep(.el-ipnut),
+:deep(.el-select),
+:deep(.el-input-number) {
+	width: 100%;
+}
+</style>

+ 4 - 3
Web/src/views/system/dict/index.vue

@@ -24,7 +24,7 @@
 						</el-form-item>
 					</el-form>
 
-					<el-table :data="state.dictTypeData" style="width: 100%" v-loading="state.loading" @row-click="handleDictType" highlight-current-row border>
+					<el-table :data="state.dictTypeData" style="width: 100%" v-loading="state.typeLoading" @row-click="handleDictType" highlight-current-row border>
 						<el-table-column type="index" label="序号" width="55" align="center" />
 						<el-table-column prop="name" label="字典名称" min-width="120" header-align="center" show-overflow-tooltip />
 						<el-table-column prop="code" label="字典编码" min-width="140" header-align="center" show-overflow-tooltip />
@@ -166,6 +166,7 @@ const editDictTypeRef = ref<InstanceType<typeof EditDictType>>();
 const editDictDataRef = ref<InstanceType<typeof EditDictData>>();
 const state = reactive({
 	loading: false,
+	typeLoading: false,
 	dictTypeData: [] as Array<SysDictType>,
 	dictDataData: [] as Array<SysDictData>,
 	queryDictTypeParams: {
@@ -198,12 +199,12 @@ onMounted(async () => {
 
 // 查询字典操作
 const handleDictTypeQuery = async () => {
-	state.loading = true;
+	state.typeLoading = true;
 	let params = Object.assign(state.queryDictTypeParams, state.tableDictTypeParams);
 	var res = await getAPI(SysDictTypeApi).apiSysDictTypePagePost(params);
 	state.dictTypeData = res.data.result?.items ?? [];
 	state.tableDictTypeParams.total = res.data.result?.total;
-	state.loading = false;
+	state.typeLoading = false;
 };
 
 // 查询字典值操作

+ 153 - 0
Web/src/views/system/openAccess/component/generateSign.vue

@@ -0,0 +1,153 @@
+<template>
+	<div class="sys-open-access-container">
+		<el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" width="600px">
+			<template #header>
+				<div style="color: #fff">
+					<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Key /> </el-icon>
+					<span> 生成签名 </span>
+				</div>
+			</template>
+			<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="身份标识" prop="accessKey">
+							<el-input v-model="state.ruleForm.accessKey" placeholder="身份标识" readonly />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="密钥" prop="accessSecret">
+							<el-input v-model="state.ruleForm.accessSecret" placeholder="密钥" readonly> </el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="接口请求地址" prop="url">
+							<el-input v-model="state.ruleForm.url" placeholder="接口请求地址" class="input-with-select" clearable>
+								<template #prepend>
+									<el-select v-model="state.ruleForm.method" placeholder="请求方法" style="width: 100px">
+										<el-option label="Get" :value="HttpMethodEnum.NUMBER_0" />
+										<el-option label="Post" :value="HttpMethodEnum.NUMBER_1" />
+										<el-option label="Put" :value="HttpMethodEnum.NUMBER_2" />
+										<el-option label="Delete" :value="HttpMethodEnum.NUMBER_3" />
+										<el-option label="Patch" :value="HttpMethodEnum.NUMBER_4" />
+										<el-option label="Head" :value="HttpMethodEnum.NUMBER_5" />
+										<el-option label="Options" :value="HttpMethodEnum.NUMBER_6" />
+										<el-option label="Trace" :value="HttpMethodEnum.NUMBER_7" />
+										<el-option label="Connect" :value="HttpMethodEnum.NUMBER_8" />
+									</el-select>
+								</template>
+							</el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="时间戳" prop="timestamp">
+							<el-input v-model="state.ruleForm.timestamp" placeholder="输入或获取时间戳" clearable>
+								<template #append>
+									<el-button @click="getTimeStamp">获取</el-button>
+								</template>
+							</el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="随机数" prop="nonce">
+							<el-input v-model="state.ruleForm.nonce" placeholder="输入或获取随机数" clearable>
+								<template #append>
+									<el-button @click="getNonce">获取</el-button>
+								</template>
+							</el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="签名" prop="sign">
+							<el-input v-model="state.sign" placeholder="填写信息后自动生成" readonly> </el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup name="sysOpenAccessEdit">
+import { reactive, ref, watch } from 'vue';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysOpenAccessApi } from '/@/api-services/api';
+import { GenerateSignatureInput, HttpMethodEnum } from '/@/api-services/models';
+
+const props = defineProps({
+	title: String,
+});
+const emits = defineEmits(['handleQuery']);
+const ruleFormRef = ref();
+const state = reactive({
+	isShowDialog: false,
+	ruleForm: {} as GenerateSignatureInput,
+	sign: '', // 生成的签名
+});
+
+watch([() => state.ruleForm.method, () => state.ruleForm.url, () => state.ruleForm.timestamp, () => state.ruleForm.nonce], () => {
+	if (
+		state.ruleForm.method == undefined ||
+		state.ruleForm.method == null ||
+		!state.ruleForm.url ||
+		!state.ruleForm.timestamp ||
+		!state.ruleForm.nonce ||
+		/^\d+$/.test(state.ruleForm.timestamp as unknown as string) == false // 时间戳必须为数字
+	) {
+		state.sign = '';
+		return;
+	}
+
+	generateSign();
+});
+
+// 打开弹窗
+const openDialog = (row: any) => {
+	state.ruleForm = {
+		accessKey: row?.accessKey,
+		accessSecret: row?.accessSecret,
+		method: HttpMethodEnum.NUMBER_0,
+		url: '',
+	};
+	state.isShowDialog = true;
+	ruleFormRef.value?.resetFields();
+};
+
+/** 生成密钥 */
+const createSecret = async () => {
+	var res = await getAPI(SysOpenAccessApi).apiSysOpenAccessSecretPost();
+	state.ruleForm.accessSecret = res.data.result!;
+};
+
+/** 获取当前时间戳(精确到秒) */
+const getTimeStamp = () => {
+	const timestamp = Math.floor(Date.now() / 1000);
+	state.ruleForm.timestamp = timestamp;
+};
+
+/** 获取随机数 */
+const getNonce = () => {
+	var nonce = '';
+	for (var i = 0; i < 6; i++) {
+		nonce += Math.floor(Math.random() * 10);
+	}
+	state.ruleForm.nonce = nonce;
+};
+
+/** 生成签名 */
+const generateSign = async () => {
+	var res = await getAPI(SysOpenAccessApi).apiSysOpenAccessGenerateSignaturePost(state.ruleForm);
+	state.sign = res.data.result!;
+};
+
+// 导出对象
+defineExpose({ openDialog });
+</script>
+
+<style lang="scss" scoped>
+:deep(.input-with-select) {
+	.el-input-group__prepend {
+		background-color: var(--el-fill-color-blank);
+	}
+}
+</style>

+ 0 - 5
Web/src/views/system/openAccess/component/helpView.vue

@@ -7,11 +7,6 @@
 					<span> 说明 </span>
 				</div>
 			</template>
-			<!-- <template #footer>
-				<span class="dialog-footer">
-					<el-button @click="close">关 闭</el-button>
-				</span>
-			</template> -->
 			<div class="text-content">
 				<h2>OpenAPI 使用</h2>
 				<ul>

+ 9 - 0
Web/src/views/system/openAccess/index.vue

@@ -34,6 +34,7 @@
 					<template #default="scope">
 						<el-button icon="ele-Edit" size="small" text type="primary" @click="openEditOpenAccess(scope.row)" v-auth="'sysOpenAccess:update'" :disabled="scope.row.status === 1"> 编辑 </el-button>
 						<el-button icon="ele-Delete" size="small" text type="danger" @click="delOpenAccess(scope.row)" v-auth="'sysOpenAccess:delete'" :disabled="scope.row.status === 1"> 删除 </el-button>
+						<el-button size="small" text @click="openGenerateSign(scope.row)"> 生成签名 </el-button>
 					</template>
 				</el-table-column>
 			</el-table>
@@ -52,6 +53,7 @@
 
 		<EditOpenAccess ref="editOpenAccessRef" :title="state.editOpenAccessTitle" @handleQuery="handleQuery" />
 		<HelpView ref="helpViewRef" />
+		<GenerateSign ref="generateSignRef" />
 	</div>
 </template>
 
@@ -61,6 +63,7 @@ import { ElMessageBox, ElMessage } from 'element-plus';
 import EditOpenAccess from '/@/views/system/openAccess/component/editOpenAccess.vue';
 import HelpView from '/@/views/system/openAccess/component/helpView.vue';
 import ModifyRecord from '/@/components/table/modifyRecord.vue';
+import GenerateSign from '/@/views/system/openAccess/component/generateSign.vue';
 
 import { getAPI } from '/@/utils/axios-utils';
 import { SysOpenAccessApi } from '/@/api-services/api';
@@ -68,6 +71,7 @@ import { OpenAccessOutput } from '/@/api-services/models';
 
 const editOpenAccessRef = ref<InstanceType<typeof EditOpenAccess>>();
 const helpViewRef = ref<InstanceType<typeof HelpView>>();
+const generateSignRef = ref<InstanceType<typeof GenerateSign>>();
 const state = reactive({
 	loading: false,
 	openAccessData: [] as Array<OpenAccessOutput>,
@@ -145,4 +149,9 @@ const handleCurrentChange = (val: number) => {
 const openHelp = () => {
 	helpViewRef.value?.openDialog();
 };
+
+// 打开生成签名
+const openGenerateSign = (row: any) => {
+	generateSignRef.value?.openDialog(row);
+};
 </script>

+ 1 - 1
Web/src/views/system/print/component/editPrint.vue

@@ -147,7 +147,7 @@ defineExpose({ openDialog });
 			display: none !important;
 		}
 		.el-dialog__body {
-			max-height: calc(100vh - 45px) !important;
+			max-height: calc(100vh - 80px) !important;
 		}
 	}
 }

+ 1 - 3
Web/src/views/system/print/component/hiprint/index.vue

@@ -28,7 +28,7 @@
 					</template>
 				</el-popover>
 			</el-button-group>
-			<el-input-number style="margin-right: 8px" :value="state.scaleValue" :precision="2" :step="0.1" :min="state.scaleMin" :max="state.scaleMax" @change="changeScale"></el-input-number>
+			<el-input-number style="margin-right: 8px" v-model="state.scaleValue" :precision="2" :step="0.1" :min="state.scaleMin" :max="state.scaleMax" @change="changeScale"></el-input-number>
 
 			<el-button-group>
 				<el-tooltip content="左对齐" placement="bottom">
@@ -241,10 +241,8 @@ const changeScale = (currentValue: number, oldValue: number) => {
 
 	let scaleVal = state.scaleValue;
 	if (big) {
-		scaleVal += 0.1;
 		if (scaleVal > state.scaleMax) scaleVal = 5;
 	} else {
-		scaleVal -= 0.1;
 		if (scaleVal < state.scaleMin) scaleVal = 0.5;
 	}
 	if (hiprintTemplate.value) {

+ 15 - 0
Web/tailwind.config.js

@@ -0,0 +1,15 @@
+module.exports = {
+	mode: 'jit',
+	purge: ['./src/**/*.{vue,js,ts,jsx,tsx}', './index.html'],
+	corePlugins: {
+		preflight: false, // 防止和已有UI框架样式冲突
+	},
+	darkMode: false, // or 'media' or 'class'
+	theme: {
+		extend: {},
+	},
+	variants: {
+		extend: {},
+	},
+	plugins: [],
+};

+ 1 - 0
docker/README.md

@@ -12,6 +12,7 @@
 2.  *app/Configuration/App.json* 主要配置了api端口5050,如果你的端口也是这个可以覆盖
 2.  *Web/.env.production* 文件配置接口地址配置为 VITE\_API\_URL = '/prod-api'
 3.  nginx,mysql配置文件无需修改
+4.  redis/redis.conf 中配置密码,如果不设密码REDIS_PASSWORD置空,app/Configuration/Cache.json中server=redis:6379,password没有的话置空
 
 ***
 

+ 35 - 0
docker/app/Configuration/Cache.json

@@ -0,0 +1,35 @@
+{
+  "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
+
+  "Cache": {
+    "Prefix": "adminnet_", // 全局缓存前缀
+    "CacheType": "Redis", // Memory、Redis
+    "Redis": {
+      "Configuration": "server=redis:6379;password=123456;db=5;", // Redis连接字符串
+      "Prefix": "adminnet_", // Redis前缀(目前没用)
+      "MaxMessageSize": "1048576" // 最大消息大小 默认1024 * 1024
+    }
+  },
+  "Cluster": { // 集群配置
+    "Enabled": true, // 启用集群:前提开启Redis缓存模式
+    "ServerId": "adminnet", // 服务器标识
+    "ServerIp": "", // 服务器IP
+    "SignalR": {
+      "RedisConfiguration": "redis:6379,ssl=false,password=123456,defaultDatabase=5",
+      "ChannelPrefix": "signalrPrefix_"
+    },
+    "DataProtecteKey": "AdminNet:DataProtection-Keys",
+    "IsSentinel": false, // 是否哨兵模式
+    "SentinelConfig": {
+      "DefaultDb": "4",
+      "EndPoints": [ // 哨兵端口
+        // "10.10.0.124:26380"
+      ],
+      "MainPrefix": "adminNet:",
+      "Password": "123456",
+      "SentinelPassword": "adminNet",
+      "ServiceName": "adminNet",
+      "SignalRChannelPrefix": "signalR:"
+    }
+  }
+}

+ 14 - 0
docker/docker-compose.yml

@@ -38,6 +38,19 @@ services:
     volumes:
       - ./mysql/mysql:/var/lib/mysql
       - ./mysql/mysql.cnf:/etc/mysql/conf.d/mysql.cnf
+  redis:
+    image: 'redis:latest' # 使用最新版本的 Redis 镜像,也可以指定特定版本如 'redis:6.2.7'
+    container_name: my-redis # 自定义容器名称
+    ports:
+      - '6379:6379' # 映射宿主机的 6379 端口到容器的 6379 端口
+    volumes: # 持久化数据
+      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
+      - ./redis/data:/data:rw
+      - ./redis/logs:/logs
+    #command: ['redis-server', '--appendonly', 'yes'] # 启用AOF持久化
+    command: ['redis-server','/usr/local/etc/redis/redis.conf']
+    environment: # 设置环境变量,例如密码
+      - REDIS_PASSWORD=123456
   adminNet:
     image: mcr.microsoft.com/dotnet/aspnet:6.0
     ports:
@@ -50,4 +63,5 @@ services:
     command: ["/app/wait-for-it.sh", "mysql:3306", "-t", "120", "--","dotnet", "Admin.NET.Web.Entry.dll"]
     depends_on:
       - mysql
+      - redis
 

+ 15 - 0
docker/redis/redis.conf

@@ -0,0 +1,15 @@
+bind 0.0.0.0
+protected-mode no
+port 6379
+timeout 0
+save 900 1 # 900s内至少一次写操作则执行bgsave进行RDB持久化
+save 300 10
+save 60 10000
+rdbcompression yes
+dbfilename dump.rdb
+# dir data
+# 开启数据持久化[aof]
+appendonly yes
+appendfsync everysec
+# 开启密码验证
+requirepass 123456