Bladeren bron

!1063 审批流前端 以及 后端模块
Merge pull request !1063 from IMaster/next

zuohuaijun 1 jaar geleden
bovenliggende
commit
09a042b218
44 gewijzigde bestanden met toevoegingen van 3848 en 0 verwijderingen
  1. 7 0
      Admin.NET/Admin.NET.sln
  2. 27 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Admin.NET.Plugin.Flow.csproj
  3. 12 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Const/FlowConst.cs
  4. 47 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Entity/ApprovalFlow.cs
  5. 12 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Enum/FlowTypeEnum.cs
  6. 19 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/GlobalUsings.cs
  7. 45 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Middleware/ApprovalFlowMiddleware.cs
  8. 138 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Service/ApprovalFlow/ApprovalFlowService.cs
  9. 83 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Service/ApprovalFlow/Dto/ApprovalFlowDto.cs
  10. 149 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Service/ApprovalFlow/Dto/ApprovalFlowInput.cs
  11. 85 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Service/ApprovalFlow/Dto/ApprovalFlowOutput.cs
  12. 49 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Service/SysApprovalFlow/SysApprovalService.cs
  13. 26 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/SpeedData/SysMenuSeedData.cs
  14. 19 0
      Admin.NET/Plugins/Admin.NET.Plugin.Flow/Startup.cs
  15. 2 0
      Web/package.json
  16. 1 0
      Web/src/api-services/api.ts
  17. 661 0
      Web/src/api-services/apis/approval-flow-api.ts
  18. 104 0
      Web/src/api-services/models/add-approval-flow-input.ts
  19. 57 0
      Web/src/api-services/models/admin-result-approval-flow.ts
  20. 57 0
      Web/src/api-services/models/admin-result-sql-sugar-paged-list-approval-flow-output.ts
  21. 74 0
      Web/src/api-services/models/approval-flow-input.ts
  22. 110 0
      Web/src/api-services/models/approval-flow-output.ts
  23. 116 0
      Web/src/api-services/models/approval-flow.ts
  24. 26 0
      Web/src/api-services/models/delete-approval-flow-input.ts
  25. 63 0
      Web/src/api-services/models/sql-sugar-paged-list-approval-flow-output.ts
  26. 110 0
      Web/src/api-services/models/update-approval-flow-input.ts
  27. 80 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Panel/PanelControl.vue
  28. 14 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Panel/PanelDataDialog.vue
  29. 114 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Panel/PanelNode.vue
  30. 48 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Property/PropertyCommon.vue
  31. 32 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Property/PropertyDialog.vue
  32. 55 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Register/Edges/EdgeSql.ts
  33. 79 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Register/Nodes/NodeEnd.ts
  34. 150 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Register/Nodes/NodeSql.ts
  35. 64 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Register/Nodes/NodeStart.ts
  36. 25 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Register/Nodes/NodeTask.ts
  37. 110 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Register/Nodes/NodeUser.ts
  38. 9 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Register/RegisterEdge.ts
  39. 17 0
      Web/src/views/pages/approvalFlow/component/LogicFlow/Register/RegisterNode.ts
  40. 127 0
      Web/src/views/pages/approvalFlow/component/detailDialog.vue
  41. 167 0
      Web/src/views/pages/approvalFlow/component/editDialog.vue
  42. 263 0
      Web/src/views/pages/approvalFlow/component/editFlowDialog.vue
  43. 155 0
      Web/src/views/pages/approvalFlow/component/editFormDialog.vue
  44. 240 0
      Web/src/views/pages/approvalFlow/index.vue

+ 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.Flow", "Plugins\Admin.NET.Plugin.Flow\Admin.NET.Plugin.Flow.csproj", "{C7B66502-7083-4831-8730-40CCC96C3BB4}"
+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
+		{C7B66502-7083-4831-8730-40CCC96C3BB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C7B66502-7083-4831-8730-40CCC96C3BB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C7B66502-7083-4831-8730-40CCC96C3BB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C7B66502-7083-4831-8730-40CCC96C3BB4}.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}
+		{C7B66502-7083-4831-8730-40CCC96C3BB4} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {5CD801D7-984A-4F5C-8FA2-211B7A5EA9F3}

+ 27 - 0
Admin.NET/Plugins/Admin.NET.Plugin.Flow/Admin.NET.Plugin.Flow.csproj

@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>disable</Nullable>
+    <GenerateDocumentationFile>True</GenerateDocumentationFile>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <NoWarn>1701;1702;1591;8632</NoWarn>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+    <NoWarn>1701;1702;1591;8632</NoWarn>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Admin.NET.Core\Admin.NET.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Update="Flow.json">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+</Project>

+ 12 - 0
Admin.NET/Plugins/Admin.NET.Plugin.Flow/Const/FlowConst.cs

@@ -0,0 +1,12 @@
+namespace Admin.NET.Plugin.Flow.Const;
+
+/// <summary>
+/// 业务应用相关常量
+/// </summary>
+public class FlowConst
+{
+    /// <summary>
+    /// API分组名称
+    /// </summary>
+    public const string GroupName = "审批流";
+}

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

@@ -0,0 +1,47 @@
+using Admin.NET.Core;
+
+namespace Admin.NET.Plugin.Flow.Entity;
+
+/// <summary>
+/// 审批流信息表
+/// </summary>
+[SugarTable("ApprovalFlow", "审批流信息表")]
+public class ApprovalFlow : EntityBaseData
+{
+    /// <summary>
+    /// 编号
+    /// </summary>
+    [SugarColumn(ColumnName = "Code", ColumnDescription = "编号", Length = 32)]
+    public string? Code { get; set; }
+    
+    /// <summary>
+    /// 名称
+    /// </summary>
+    [SugarColumn(ColumnName = "Name", ColumnDescription = "名称", Length = 32)]
+    public string? Name { get; set; }
+    
+    /// <summary>
+    /// 表单
+    /// </summary>
+    [SugarColumn(ColumnName = "FormJson", ColumnDescription = "表单", Length = 0)]
+    public string? FormJson { get; set; }
+    
+    /// <summary>
+    /// 流程
+    /// </summary>
+    [SugarColumn(ColumnName = "FlowJson", ColumnDescription = "流程", Length = 0)]
+    public string? FlowJson { get; set; }
+    
+    /// <summary>
+    /// 状态
+    /// </summary>
+    [SugarColumn(ColumnName = "Status", ColumnDescription = "状态")]
+    public int? Status { get; set; }
+    
+    /// <summary>
+    /// 备注
+    /// </summary>
+    [SugarColumn(ColumnName = "Remark", ColumnDescription = "备注", Length = 255)]
+    public string? Remark { get; set; }
+    
+}

+ 12 - 0
Admin.NET/Plugins/Admin.NET.Plugin.Flow/Enum/FlowTypeEnum.cs

@@ -0,0 +1,12 @@
+using System;
+
+namespace Admin.NET.Plugin.Flow;
+
+/// <summary>
+/// 流类型枚举
+/// </summary>
+public enum FlowTypeEnum
+{
+
+}
+

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

@@ -0,0 +1,19 @@
+// 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证。
+//
+// 必须在法律法规允许的范围内正确使用,严禁将其用于非法、欺诈、恶意或侵犯他人合法权益的目的。
+
+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.Authorization;
+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;
+global using System.ComponentModel.DataAnnotations;
+global using System.Threading.Tasks;

+ 45 - 0
Admin.NET/Plugins/Admin.NET.Plugin.Flow/Middleware/ApprovalFlowMiddleware.cs

@@ -0,0 +1,45 @@
+// 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证。
+//
+// 必须在法律法规允许的范围内正确使用,严禁将其用于非法、欺诈、恶意或侵犯他人合法权益的目的。
+
+using Admin.NET.Plugin.Flow.Service;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+
+namespace Admin.NET.Plugin.Flow;
+
+/// <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);
+    }
+}

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

@@ -0,0 +1,138 @@
+using Admin.NET.Core.Service;
+using Admin.NET.Plugin.Flow.Const;
+using Admin.NET.Plugin.Flow.Entity;
+using Microsoft.AspNetCore.Http;
+
+namespace Admin.NET.Plugin.Flow;
+
+/// <summary>
+/// 审批流服务
+/// </summary>
+[ApiDescriptionSettings(FlowConst.GroupName, Order = 100)]
+public class ApprovalFlowService : IDynamicApiController, ITransient
+{
+    private readonly SqlSugarRepository<ApprovalFlow> _rep;
+    public ApprovalFlowService(SqlSugarRepository<ApprovalFlow> rep)
+    {
+        _rep = rep;
+    }
+
+    /// <summary>
+    /// 分页查询审批流
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Page")]
+    public async Task<SqlSugarPagedList<ApprovalFlowOutput>> Page(ApprovalFlowInput input)
+    {
+        var query = _rep.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>();
+        return await query.ToPagedListAsync(input.Page, input.PageSize);
+    }
+
+    /// <summary>
+    /// 增加审批流
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Add")]
+    public async Task<long> Add(AddApprovalFlowInput input)
+    {
+        var entity = input.Adapt<ApprovalFlow>();
+        if (input.Code == null)
+        {
+            entity.Code = await LastCode("");
+        }
+        await _rep.InsertAsync(entity);
+        return entity.Id;
+    }
+
+    /// <summary>
+    /// 删除审批流
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Delete")]
+    public async Task Delete(DeleteApprovalFlowInput input)
+    {
+        var entity = await _rep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        await _rep.FakeDeleteAsync(entity);   //假删除
+        //await _rep.DeleteAsync(entity);   //真删除
+    }
+
+    /// <summary>
+    /// 更新审批流
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Update")]
+    public async Task Update(UpdateApprovalFlowInput input)
+    {
+        var entity = input.Adapt<ApprovalFlow>();
+        await _rep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
+    }
+
+    /// <summary>
+    /// 获取审批流
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpGet]
+    [ApiDescriptionSettings(Name = "Detail")]
+    public async Task<ApprovalFlow> Detail([FromQuery] QueryByIdApprovalFlowInput input)
+    {
+        return await _rep.GetFirstAsync(u => u.Id == input.Id);
+    }
+
+    /// <summary>
+    /// 通过 code 获取审批流信息
+    /// </summary>
+    /// <param name="code"></param>
+    /// <returns></returns>
+    [HttpGet]
+    [ApiDescriptionSettings(Name = "Info")]
+    public async Task<ApprovalFlow> Info([FromQuery] string code)
+    {
+        return await _rep.GetFirstAsync(u => u.Code == code);
+    }
+
+    /// <summary>
+    /// 获取审批流列表
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpGet]
+    [ApiDescriptionSettings(Name = "List")]
+    public async Task<List<ApprovalFlowOutput>> List([FromQuery] ApprovalFlowInput input)
+    {
+        return await _rep.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 _rep.AsQueryable().Where(u => u.CreateTime >= today).CountAsync();
+        return prefix + DateTime.Now.ToString("yyMMdd") + string.Format("{0:d2}", count + 1);
+    }
+}
+

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

@@ -0,0 +1,83 @@
+namespace Admin.NET.Plugin.Flow;
+
+/// <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; }
+    
+}

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

@@ -0,0 +1,149 @@
+using Admin.NET.Core;
+using System.ComponentModel.DataAnnotations;
+
+namespace Admin.NET.Plugin.Flow;
+
+/// <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
+{
+
+}

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

@@ -0,0 +1,85 @@
+namespace Admin.NET.Plugin.Flow;
+
+/// <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; }
+    
+}
+ 
+

+ 49 - 0
Admin.NET/Plugins/Admin.NET.Plugin.Flow/Service/SysApprovalFlow/SysApprovalService.cs

@@ -0,0 +1,49 @@
+// 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证。
+//
+// 必须在法律法规允许的范围内正确使用,严禁将其用于非法、欺诈、恶意或侵犯他人合法权益的目的。
+
+using System.IO;
+using System.Linq;
+using System.Text;
+using Admin.NET.Plugin.Flow.Entity;
+using Furion.Logging.Extensions;
+using Microsoft.AspNetCore.Http;
+using NewLife;
+using RazorEngine.Compilation.ImpromptuInterface.InvokeExt;
+
+namespace Admin.NET.Plugin.Flow.Service;
+
+[ApiDescriptionSettings(Order = 300)]
+public class SysApprovalService : IDynamicApiController, ITransient
+{
+    public SysApprovalService()
+    {
+
+    }
+
+    /// <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;
+
+        path.Join(",").LogTrace();
+        
+        await Task.CompletedTask;
+    }
+}

+ 26 - 0
Admin.NET/Plugins/Admin.NET.Plugin.Flow/SpeedData/SysMenuSeedData.cs

@@ -0,0 +1,26 @@
+// 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证。
+//
+// 必须在法律法规允许的范围内正确使用,严禁将其用于非法、欺诈、恶意或侵犯他人合法权益的目的。
+
+using Admin.NET.Core;
+
+namespace Admin.NET.Plugin.Report;
+
+/// <summary>
+/// 审批流菜单表种子数据
+/// </summary>
+public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
+{
+    /// <summary>
+    /// 种子数据
+    /// </summary>
+    /// <returns></returns>
+    public IEnumerable<SysMenu> HasData()
+    {
+        return new[]
+        {
+            new SysMenu{ Id=1300000000201, Pid=0, Title="平台功能", Path="/pages", Name="Pages", Component="Layout", Icon="ele-MagicStick", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=140 },
+            new SysMenu{ Id=1300000000211, Pid=1300000000201, Title="审批流管理", Path="/pages/approvalFlow", Name="approvalFlow", Component="/pages/approvalFlow/index", Icon="ele-Menu", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },  
+        };
+    }
+}

+ 19 - 0
Admin.NET/Plugins/Admin.NET.Plugin.Flow/Startup.cs

@@ -0,0 +1,19 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+
+namespace Admin.NET.Plugin.Flow;
+
+[AppStartup(100)]
+public class Startup : AppStartup
+{
+    public void ConfigureServices(IServiceCollection services)
+    {
+    }
+
+    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+    {
+        // 使用审批流
+        app.UseApprovalFlow(); 
+    }
+}
+

+ 2 - 0
Web/package.json

@@ -12,6 +12,8 @@
 	},
 	"dependencies": {
 		"@element-plus/icons-vue": "^2.3.1",
+		"@logicflow/core": "^1.2.26",
+		"@logicflow/extension": "^1.2.26",
 		"@microsoft/signalr": "^8.0.0",
 		"@vue-office/docx": "^1.6.1",
 		"@vue-office/excel": "^1.7.8",

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

@@ -12,6 +12,7 @@
  * Do not edit the class manually.
  */
 export * from './apis/apijsonapi';
+export * from './apis/approval-flow-api';
 export * from './apis/sys-auth-api';
 export * from './apis/sys-cache-api';
 export * from './apis/sys-code-gen-api';

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

@@ -0,0 +1,661 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .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 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 { AdminResultInt64 } from '../models';
+import { AdminResultApprovalFlow } from '../models';
+import { AdminResultListApprovalFlowOutput } from '../models';
+import { AdminResultSqlSugarPagedListApprovalFlowOutput } from '../models';
+import { DeleteApprovalFlowInput } from '../models';
+import { ApprovalFlowInput } 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/labApprovalFlow/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
+
+            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/labApprovalFlow/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
+
+            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/labApprovalFlow/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
+
+            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 通过 code 获取审批流信息
+         * @param {string} [code] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiApprovalFlowInfoGet: async (code?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/labApprovalFlow/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
+
+            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/labApprovalFlow/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
+
+            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/labApprovalFlow/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
+
+            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/labApprovalFlow/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
+
+            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 通过 code 获取审批流信息
+         * @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 通过 code 获取审批流信息
+         * @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 通过 code 获取审批流信息
+     * @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));
+    }
+}

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

@@ -0,0 +1,104 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .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.
+ */
+/**
+ * 审批流增加输入参数
+ * @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;
+}

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

@@ -0,0 +1,57 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .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 { 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;
+}

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

@@ -0,0 +1,57 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .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 { 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;
+}

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

@@ -0,0 +1,74 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .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.
+ */
+/**
+ * 审批流分页查询输入参数
+ * @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;
+}

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

@@ -0,0 +1,110 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .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.
+ */
+/**
+ * 审批流输出参数
+ * @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;
+}

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

@@ -0,0 +1,116 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .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.
+ */
+/**
+ * 审批流信息表
+ * @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;
+}

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

@@ -0,0 +1,26 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .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.
+ */
+/**
+ * 审批流删除输入参数
+ * @export
+ * @interface DeleteApprovalFlowInput
+ */
+export interface DeleteApprovalFlowInput {
+    /**
+     * 主键Id
+     * @type {number}
+     * @memberof DeleteApprovalFlowInput
+     */
+    id: number;
+}

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

@@ -0,0 +1,63 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .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 { ApprovalFlowOutput } from './lab-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;
+}

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

@@ -0,0 +1,110 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .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.
+ */
+/**
+ * 审批流更新输入参数
+ * @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;
+}

+ 80 - 0
Web/src/views/pages/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/pages/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/pages/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/pages/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>

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

@@ -0,0 +1,32 @@
+<template>
+	<div class="property-dialog">
+		<PropertyCommon :nodeData="nodeData" @setProperties="setProperties"></PropertyCommon>
+	</div>
+</template>
+
+<script setup>
+import { reactive, ref } from 'vue';
+
+import PropertyCommon from '/@/views/pages/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/pages/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/pages/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/pages/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/pages/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/pages/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/pages/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/pages/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/pages/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 };

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

@@ -0,0 +1,127 @@
+<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";
+	import { MoreFilled, UserFilled } from '@element-plus/icons-vue'
+
+	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>({
+	});
+
+	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;
+    };
+
+    onMounted(() => {
+
+    });
+
+	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>

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

@@ -0,0 +1,167 @@
+<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 { getDictDataItem as di, getDictDataList as dl } from '/@/utils/dict-utils';
+	import { ElMessage } from "element-plus";
+	import type { FormRules } from "element-plus";
+
+	// 接口
+	import { getAPI } from '/@/utils/axios-utils';
+
+	import { addApprovalFlow, updateApprovalFlow, detailApprovalFlow } from "/@/api/main/labApprovalFlow";
+	// Api Services
+	import { ApprovalFlowApi } from '/@/api-services/api';
+	import { UpdateApprovalFlowInput } from "/@/api-services/models";
+
+	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"
+	   }]
+	});
+
+	// 打开弹窗
+	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",
+				});
+			}
+		});
+	};
+
+	// 页面加载时
+	onMounted(async () => {
+        getEnumStatusData.value = (await getAPI(SysEnumApi).apiSysEnumEnumDataListGet('LabStatusEnum')).data.result ?? [];
+	});
+
+	// 将属性或者函数暴露给父组件
+	defineExpose({ openDialog });
+</script>
+
+<style scoped lang="scss">
+:deep(.el-select),
+:deep(.el-input-number) {
+	width: 100%;
+}
+</style>
+

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

@@ -0,0 +1,263 @@
+<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 LogicFlow from '@logicflow/core';
+import '@logicflow/core/dist/style/index.css';
+import { BpmnElement, InsertNodeInPolyline, Menu, MiniMap, SelectionSelect, Snapshot } from '@logicflow/extension';
+import '@logicflow/extension/lib/style/index.css';
+import { ElMessageBox } from 'element-plus';
+
+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/api';
+import { ApprovalFlowOutput, UpdateApprovalFlowInput } from '/@/api-services/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>

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

@@ -0,0 +1,155 @@
+<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, SysDatabaseApi, SysCodeGenApi } from '/@/api-services/api';
+    import { DbTableInfo, ApprovalFlowOutput, UpdateApprovalFlowInput } from '/@/api-services/models';
+	import { stat } from "fs";
+
+	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>({
+	});
+
+	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;
+	};
+
+    onMounted(async () => {
+		var resDb = await getAPI(SysCodeGenApi).apiSysCodeGenDatabaseListGet();
+		state.dbData = resDb.data.result;
+    });
+
+	defineExpose({ openDialog });
+</script>
+
+<style scoped lang="scss">
+:deep(.el-select),
+:deep(.el-input-number) {
+	width: 100%;
+}
+</style>

+ 240 - 0
Web/src/views/pages/approvalFlow/index.vue

@@ -0,0 +1,240 @@
+<template>
+    <div class="labApprovalFlow-container">
+        <el-card shadow="hover" :body-style="{ paddingBottom: '0' }">
+            <el-form :model="state.queryParams" ref="queryForm" labelWidth="90">
+                <el-row>
+                    <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" clearable="" placeholder="请输入模糊查询关键字"/>
+                        </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" clearable="" placeholder="请输入编号"/>
+                        </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" clearable="" placeholder="请输入名称"/>
+                        </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" clearable="" placeholder="请输入备注"/>
+                        </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 style="display: flex; align-items: center;">
+                                <el-button type="primary"  icon="ele-Search" @click="handleQuery" v-auth="'labApprovalFlow:page'"> 查询 </el-button>
+                                <el-button icon="ele-Refresh" @click="() => state.queryParams = {}"> 重置 </el-button>
+                                <el-button icon="ele-ZoomIn" @click="changeAdvanceQueryUI" v-if="!showAdvanceQueryUI" style="margin-left:5px;"> 高级查询 </el-button>
+                                <el-button icon="ele-ZoomOut" @click="changeAdvanceQueryUI" v-if="showAdvanceQueryUI" style="margin-left:5px;"> 隐藏 </el-button>
+                                <el-button type="primary" style="margin-left:5px;" icon="ele-Plus" @click="openAddLabApprovalFlow" v-auth="'labApprovalFlow:add'"> 新增 </el-button>
+                            </el-button-group>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+        </el-card>
+        <el-card class="full-table" shadow="hover" style="margin-top: 8px">
+            <el-table
+                :data="state.tableData"
+                style="width: 100%"
+                v-loading="state.loading"
+                tooltip-effect="light"
+                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="名称" width="120" show-overflow-tooltip="" />
+                <el-table-column prop="formJson" label="表单" align="center" width="120" show-overflow-tooltip="" >
+                    <template #default="scope">
+                        <el-button icon="ele-Edit" size="small" text="" type="primary" @click="openEditFormDialog(scope.row)" v-auth="'labCellInspectRecord:update'"> 表单 </el-button>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="flowJson" label="流程" align="center" width="120" show-overflow-tooltip="" >
+                    <template #default="scope">
+                        <el-button icon="ele-Edit" size="small" text="" type="primary" @click="openEditFlowDialog(scope.row)" v-auth="'labCellInspectRecord:update'"> 流程 </el-button>
+                    </template>
+                </el-table-column>
+                <el-table-column label="修改记录" 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="" v-if="auth('approvalFlow:update') || auth('approvalFlow:delete')">
+                    <template #default="scope">
+                        <el-button icon="ele-View" size="small" text="" type="primary" @click="openDetailDialog(scope.row)" v-auth="'approvalFlow:detail'"> 查看 </el-button>
+                        <el-button icon="ele-Edit" size="small" text="" type="primary" @click="openEditApprovalFlow(scope.row)" v-auth="'approvalFlow:update'"> 编辑 </el-button>
+                        <el-button icon="ele-Delete" size="small" text="" type="primary" @click="delApprovalFlow(scope.row)" v-auth="'approvalFlow:delete'"> 删除 </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" />
+            <detailDialog
+                ref="detailDialogRef"
+                :title="editTitle"
+                @reloadTable="handleQuery" />
+            <printDialog
+                ref="printDialogRef"
+                :title="editTitle"
+                @reloadTable="handleQuery" />
+            <editDialog
+                ref="editDialogRef"
+                :title="editTitle"
+                @reloadTable="handleQuery" />
+            <editFormDialog
+                ref="editFormDialogRef"
+                :title="editTitle"
+                @reloadTable="handleQuery" />
+            <editFlowDialog
+                ref="editFlowDialogRef"
+                :title="editTitle"
+                @updateFlow="handleFlow"
+                @reloadTable="handleQuery" />
+        </el-card>
+    </div>
+</template>
+
+<script lang="ts" setup="" name="approvalFlow">
+    import { reactive, ref } from "vue";
+    import { ElMessageBox, ElMessage } from "element-plus";
+    import { auth } from '/@/utils/authFunction';
+    import { formatDate } from '/@/utils/formatTime';
+
+    import printDialog from '/@/views/system/print/component/hiprint/preview.vue';
+    import editFormDialog from '/@/views/pages/approvalFlow/component/editFormDialog.vue';
+    import detailDialog from '/@/views/pages/approvalFlow/component/detailDialog.vue';
+    import editFlowDialog from '/@/views/pages/approvalFlow/component/editFlowDialog.vue';
+    import editDialog from '/@/views/pages/approvalFlow/component/editDialog.vue';
+    import ModifyRecord from '/@/components/table/modifyRecord.vue';
+
+    // 接口
+    import { getAPI } from '/@/utils/axios-utils';
+    
+    import { ApprovalFlowApi } from '/@/api-services/api';
+    import { ApprovalFlowInput, ApprovalFlowOutput } from '/@/api-services/models';
+
+    import commonFunction from '/@/utils/commonFunction';
+
+    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,
+        }
+	});
+    const detailTitle = ref("");
+    const editTitle = ref("");
+
+    // 改变高级查询的控件显示状态
+    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 = () => {
+        editTitle.value = '添加审批流';
+        editDialogRef.value.openDialog({ status: 1 });
+    };
+
+    // 打开编辑页面
+    const openEditApprovalFlow = (row: ApprovalFlowOutput) => {
+        editTitle.value = '编辑审批流';
+        editDialogRef.value.openDialog(row);
+     };
+    
+    // 打开打印页面
+    const openEditDialog = (row: ApprovalFlowOutput) => {
+        editTitle.value = '编辑审批流';
+        editDialogRef.value.openDialog(row);
+    };
+
+    // 打开打印页面
+    const openDetailDialog = (row: ApprovalFlowOutput) => {
+        detailTitle.value = '查看审批流';
+        detailDialogRef.value.openDialog(row);
+    };
+    const openEditFormDialog = (row: ApprovalFlowOutput) => {
+        editFormTitle.value = '编辑表单';
+        editFormDialogRef.value.openDialog(row);
+    };
+
+    const openEditFlowDialog = (row: ApprovalFlowOutput) => {
+        editFlowTitle.value = '编辑流程';
+        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();
+    };
+
+    handleQuery();
+</script>
+
+<style scoped>
+:deep(.el-ipnut),
+:deep(.el-select),
+:deep(.el-input-number) {
+	width: 100%;
+}
+</style>
+