ALANE\Sevy.Jiang 3 anni fa
parent
commit
a8864bd585
56 ha cambiato i file con 5204 aggiunte e 1948 eliminazioni
  1. 52 52
      Admin.NET/Admin.NET.Application/AppConfig.json
  2. 3 3
      Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
  3. 46 14
      Admin.NET/Admin.NET.Core/Admin.NET.Core.xml
  4. 1 1
      Admin.NET/Admin.NET.Core/Const/SqlSugarConst.cs
  5. 11 4
      Admin.NET/Admin.NET.Core/Entity/SysUser.cs
  6. 5 4
      Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs
  7. 18 5
      Admin.NET/Admin.NET.Core/SeedData/SysRoleMenuSeedData.cs
  8. 15 5
      Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginUserOutput.cs
  9. 8 0
      Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs
  10. 1 0
      Admin.NET/Admin.NET.Core/Service/DataBase/SysDataBaseService.cs
  11. 47 11
      Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs
  12. 0 5
      Admin.NET/Admin.NET.Core/Service/User/Dto/UserInput.cs
  13. 15 4
      Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs
  14. 0 0
      Admin.NET/Admin.NET.Web.Entry/Test.db
  15. 189 546
      Web/package-lock.json
  16. 4 2
      Web/package.json
  17. 1 0
      Web/plugins.d.ts
  18. 2705 0
      Web/pnpm-lock.yaml
  19. 175 7
      Web/src/api-services/apis/sys-file-api.ts
  20. 118 49
      Web/src/api-services/apis/sys-user-api.ts
  21. 6 0
      Web/src/api-services/models/add-user-input.ts
  22. 2 0
      Web/src/api-services/models/index.ts
  23. 18 6
      Web/src/api-services/models/login-user-output.ts
  24. 26 0
      Web/src/api-services/models/sys-file-upload-avatar-body.ts
  25. 26 0
      Web/src/api-services/models/sys-file-upload-signature-body.ts
  26. 6 0
      Web/src/api-services/models/sys-user.ts
  27. 1 0
      Web/src/api-services/models/update-db-table-input.ts
  28. 6 0
      Web/src/api-services/models/update-user-input.ts
  29. 99 0
      Web/src/api/system/admin.ts
  30. 2 5
      Web/src/layout/navBars/breadcrumb/user.vue
  31. 1 1
      Web/src/layout/navBars/tagsView/tagsView.vue
  32. 6 3
      Web/src/main.ts
  33. 1 1
      Web/src/router/index.ts
  34. 1 1
      Web/src/router/route.ts
  35. 5 0
      Web/src/stores/interface/index.ts
  36. 7 9
      Web/src/stores/userInfo.ts
  37. 71 28
      Web/src/utils/base64Conver.ts
  38. 0 0
      Web/src/utils/http/requset.js
  39. 175 21
      Web/src/utils/request.ts
  40. 236 0
      Web/src/utils/ruoyi.js
  41. 0 302
      Web/src/views/dashboard/analysis/index.vue
  42. 0 302
      Web/src/views/dashboard/workbench/index.vue
  43. 1 10
      Web/src/views/home/index.vue
  44. 0 387
      Web/src/views/personal/index.vue
  45. 0 66
      Web/src/views/personal/mock.ts
  46. 361 0
      Web/src/views/system/database/component/addTable.vue
  47. 285 1
      Web/src/views/system/database/component/editTable.vue
  48. 17 7
      Web/src/views/system/database/index.vue
  49. 1 1
      Web/src/views/system/menu/index.vue
  50. 1 1
      Web/src/views/system/onlineUser/index.vue
  51. 1 1
      Web/src/views/system/org/index.vue
  52. 0 81
      Web/src/views/system/password/index.vue
  53. 96 0
      Web/src/views/system/user/component/orgTree.vue
  54. 320 0
      Web/src/views/system/user/component/userCenter.vue
  55. 6 2
      Web/src/views/system/user/index.vue
  56. 6 0
      vue-next-admin/.vscode/settings.json

+ 52 - 52
Admin.NET/Admin.NET.Application/AppConfig.json

@@ -1,55 +1,55 @@
 {
-    "$schema": "https://gitee.com/dotnetchina/Furion/raw/net6/schemas/v3/furion-schema.json",
+  "$schema": "https://gitee.com/dotnetchina/Furion/raw/net6/schemas/v3/furion-schema.json",
+  "DbConnection": {
+    "ConnectionConfigs": [
+      // 默认第一个为主库
+      {
+        "ConfigId": "eolane",
+        "DbType": "MySql", // MySql、SqlServer、Sqlite、Oracle、PostgreSQL、Dm、Kdbndp、Oscar、MySqlConnector、Access
+        "ConnectionString": "Server=localhost;uid=root;pwd=123456;Database=eolane_platform;pooling=true;min pool size=5;max pool size=32767;connect timeout=30000;charset=utf8;", // DataSource=C:\\db\\Admin.NET.db
+        "EnableInitDb": false, // 启用库表初始化
+        "EnableDiffLog": false // 启用库表差异日志
+      },
+      // 其他业务库
+      {
+        "ConfigId": "Test",
+        "DbType": "Sqlite",
+        "ConnectionString": "DataSource=./Test.db",
+        "EnableInitDb": false, // 启用库表初始化
+        "EnableDiffLog": false // 启用库表差异日志
+      }
+    ]
+  },
+  // 代码生成配置项-程序集名称集合
+  "CodeGen": {
+    "EntityAssemblyNames": [ "Admin.NET.Core", "Admin.NET.Application" ],
+    "FrontRootPath": "Vben2", //前端文件根目录
+    "BackendApplicationNamespace": "Admin.NET.Application" //后端生成到的项目
+  },
+  "Wechat": {
+    // 公众号
+    "WechatAppId": "",
+    "WechatAppSecret": "",
+    "EncodingAESKey": "",
+    "Token": "",
+    // 小程序
+    "WxOpenAppId": "",
+    "WxOpenAppSecret": "",
+    "WxOpenToken": "",
+    "WxOpenEncodingAESKey": ""
+  },
+  "WechatPay": {
+    "AppId": "", // 微信公众平台AppId、开放平台AppId、小程序AppId、企业微信CorpId
+    "MerchantId": "", // 商户平台的商户号
+    "MerchantV3Secret": "", // 商户平台的APIv3密钥
+    "MerchantCertificateSerialNumber": "", // 商户平台的证书序列号
+    "MerchantCertificatePrivateKey": "\\WxPayCert\\apiclient_key.pem" // 商户平台的API证书私钥(apiclient_key.pem文件内容)
+  },
+  "PayCallBack": {
+    "WechatPayUrl": "/notify/weChatPay/unifiedorder", // 微信支付回调
+    "WechatRefundUrl": "/notify/weChatPay/refund", // 微信退款回调
+    "AlipayUrl": "", // 支付宝支付回调
+    "AlipayRefundUrl": "" // 支付宝退款回调
+  }
 
-    "DbConnection": {
-        "ConnectionConfigs": [
-            // 默认第一个为主库
-            {
-                "ConfigId": "Dilon",
-                "DbType": "Sqlite", // MySql、SqlServer、Sqlite、Oracle、PostgreSQL、Dm、Kdbndp、Oscar、MySqlConnector、Access
-                "ConnectionString": "DataSource=./Admin.NET.db",
-                "EnableInitDb": true, // 启用库表初始化
-                "EnableDiffLog": false // 启用库表差异日志
-            },
-            // 其他业务库
-            {
-                "ConfigId": "Test",
-                "DbType": "Sqlite",
-                "ConnectionString": "DataSource=./Test.db",
-                "EnableInitDb": true, // 启用库表初始化
-                "EnableDiffLog": false // 启用库表差异日志
-            }
-        ]
-    },
-    // 代码生成配置项-程序集名称集合
-    "CodeGen": {
-        "EntityAssemblyNames": [ "Admin.NET.Core", "Admin.NET.Application" ],
-        "FrontRootPath": "Vben2", //前端文件根目录
-        "BackendApplicationNamespace": "Admin.NET.Application" //后端生成到的项目
-    },
-    "Wechat": {
-        // 公众号
-        "WechatAppId": "",
-        "WechatAppSecret": "",
-        "EncodingAESKey": "",
-        "Token": "",
-        // 小程序
-        "WxOpenAppId": "",
-        "WxOpenAppSecret": "",
-        "WxOpenToken": "",
-        "WxOpenEncodingAESKey": ""
-    },
-    "WechatPay": {
-        "AppId": "", // 微信公众平台AppId、开放平台AppId、小程序AppId、企业微信CorpId
-        "MerchantId": "", // 商户平台的商户号
-        "MerchantV3Secret": "", // 商户平台的APIv3密钥
-        "MerchantCertificateSerialNumber": "", // 商户平台的证书序列号
-        "MerchantCertificatePrivateKey": "\\WxPayCert\\apiclient_key.pem" // 商户平台的API证书私钥(apiclient_key.pem文件内容)
-    },
-    "PayCallBack": {
-        "WechatPayUrl": "/notify/weChatPay/unifiedorder", // 微信支付回调
-        "WechatRefundUrl": "/notify/weChatPay/refund", // 微信退款回调
-        "AlipayUrl": "", // 支付宝支付回调
-        "AlipayRefundUrl": "" // 支付宝退款回调
-    }
 }

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

@@ -25,9 +25,9 @@
   <ItemGroup>
     <PackageReference Include="AngleSharp" Version="0.17.1" />
     <PackageReference Include="AspNetCoreRateLimit" Version="4.0.2" />
-    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.7.1" />
-    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.7.1" />
-    <PackageReference Include="Furion.Pure" Version="4.7.1" />
+    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.7.2" />
+    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.7.2" />
+    <PackageReference Include="Furion.Pure" Version="4.7.2" />
     <PackageReference Include="Lazy.Captcha.Core" Version="1.1.6" />
     <PackageReference Include="Magicodes.IE.Excel" Version="2.6.9" />
     <PackageReference Include="Magicodes.IE.Pdf" Version="2.6.9" />

+ 46 - 14
Admin.NET/Admin.NET.Core/Admin.NET.Core.xml

@@ -1623,6 +1623,11 @@
             最新登录设备
             </summary>
         </member>
+        <member name="P:Admin.NET.Core.SysUser.Signature">
+            <summary>
+            电子签名
+            </summary>
+        </member>
         <member name="T:Admin.NET.Core.SysUserExtOrg">
             <summary>
             系统用户扩展机构表
@@ -3815,11 +3820,6 @@
             用户登录信息
             </summary>
         </member>
-        <member name="P:Admin.NET.Core.Service.LoginUserOutput.UserId">
-            <summary>
-            用户Id
-            </summary>
-        </member>
         <member name="P:Admin.NET.Core.Service.LoginUserOutput.Account">
             <summary>
             账号名称
@@ -3840,6 +3840,16 @@
             个人简介
             </summary>
         </member>
+        <member name="P:Admin.NET.Core.Service.LoginUserOutput.Address">
+            <summary>
+            地址
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.LoginUserOutput.Signature">
+            <summary>
+            电子签名
+            </summary>
+        </member>
         <member name="P:Admin.NET.Core.Service.LoginUserOutput.OrgId">
             <summary>
             机构Id
@@ -3850,6 +3860,11 @@
             机构名称
             </summary>
         </member>
+        <member name="P:Admin.NET.Core.Service.LoginUserOutput.PosName">
+            <summary>
+            职位名称
+            </summary>
+        </member>
         <member name="P:Admin.NET.Core.Service.LoginUserOutput.Buttons">
             <summary>
             按钮权限集合
@@ -5052,11 +5067,12 @@
             <param name="input"></param>
             <returns></returns>
         </member>
-        <member name="M:Admin.NET.Core.Service.SysFileService.UploadFile(Microsoft.AspNetCore.Http.IFormFile)">
+        <member name="M:Admin.NET.Core.Service.SysFileService.UploadFile(Microsoft.AspNetCore.Http.IFormFile,System.String)">
             <summary>
             上传文件
             </summary>
             <param name="file"></param>
+            <param name="path"></param>
             <returns></returns>
         </member>
         <member name="M:Admin.NET.Core.Service.SysFileService.UploadFiles(System.Collections.Generic.List{Microsoft.AspNetCore.Http.IFormFile})">
@@ -5087,11 +5103,12 @@
             <param name="input"></param>
             <returns></returns>
         </member>
-        <member name="M:Admin.NET.Core.Service.SysFileService.HandleUploadFile(Microsoft.AspNetCore.Http.IFormFile)">
+        <member name="M:Admin.NET.Core.Service.SysFileService.HandleUploadFile(Microsoft.AspNetCore.Http.IFormFile,System.String)">
             <summary>
             上传文件
             </summary>
             <param name="file">文件</param>
+            <param name="savePath">路径</param>
             <returns></returns>
         </member>
         <member name="M:Admin.NET.Core.Service.SysFileService.GetMinioPreviewFileUrl(System.String,System.String)">
@@ -5102,6 +5119,20 @@
             <param name="fileName">文件名</param>
             <returns></returns>
         </member>
+        <member name="M:Admin.NET.Core.Service.SysFileService.UploadAvatar(Microsoft.AspNetCore.Http.IFormFile)">
+            <summary>
+            上传头像
+            </summary>
+            <param name="file"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysFileService.UploadSignature(Microsoft.AspNetCore.Http.IFormFile)">
+            <summary>
+            上传电子签名
+            </summary>
+            <param name="file"></param>
+            <returns></returns>
+        </member>
         <member name="P:Admin.NET.Core.Service.PageLogInput.StartTime">
             <summary>
             开始时间
@@ -6473,11 +6504,6 @@
             真实姓名
             </summary>
         </member>
-        <member name="P:Admin.NET.Core.Service.AddUserInput.Password">
-            <summary>
-            账号
-            </summary>
-        </member>
         <member name="P:Admin.NET.Core.Service.AddUserInput.RoleIdList">
             <summary>
             角色集合
@@ -6646,9 +6672,15 @@
             <param name="input"></param>
             <returns></returns>
         </member>
-        <member name="M:Admin.NET.Core.Service.SysUserService.GetUser(System.Int64)">
+        <member name="M:Admin.NET.Core.Service.SysUserService.GetUserBase">
+            <summary>
+            查看用户基本信息
+            </summary>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysUserService.UpdateUserBase(Admin.NET.Core.SysUser)">
             <summary>
-            查看用户
+            设置用户基本信息
             </summary>
             <returns></returns>
         </member>

+ 1 - 1
Admin.NET/Admin.NET.Core/Const/SqlSugarConst.cs

@@ -8,7 +8,7 @@ public class SqlSugarConst
     /// <summary>
     /// 默认数据库标识
     /// </summary>
-    public const string ConfigId = "Dilon";
+    public const string ConfigId = "eolane";
 
     /// <summary>
     /// 默认表主键

+ 11 - 4
Admin.NET/Admin.NET.Core/Entity/SysUser.cs

@@ -17,10 +17,10 @@ public class SysUser : EntityTenant
     /// 密码(MD5加密)
     /// </summary>
     [SugarColumn(ColumnDescription = "密码", Length = 64)]
-    [Required, MaxLength(64)]
+    [MaxLength(64)]
     [System.Text.Json.Serialization.JsonIgnore]
     [JsonIgnore]
-    public virtual string Password { get; set; }
+    public virtual string Password { get; set; }  = CommonConst.SysPassword;
 
     /// <summary>
     /// 真实姓名
@@ -39,8 +39,8 @@ public class SysUser : EntityTenant
     /// <summary>
     /// 头像
     /// </summary>
-    [SugarColumn(ColumnDescription = "头像", Length = 256)]
-    [MaxLength(256)]
+    [SugarColumn(ColumnDescription = "头像", Length = 512)]
+    [MaxLength(512)]
     public string Avatar { get; set; }
 
     /// <summary>
@@ -254,4 +254,11 @@ public class SysUser : EntityTenant
     [SugarColumn(ColumnDescription = "最新登录设备", Length = 128)]
     [MaxLength(128)]
     public string LastLoginDevice { get; set; }
+
+    /// <summary>
+    /// 电子签名
+    /// </summary>
+    [SugarColumn(ColumnDescription = "电子签名", Length = 512)]
+    [MaxLength(512)]
+    public string Signature { get; set; }
 }

+ 5 - 4
Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs

@@ -13,9 +13,8 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
     {
         return new[]
         {
-            new SysMenu{ Id=252885263002100, Pid=0, Title="数据面板", Path="/dashboard", Name="dashboard", Component="Layout", Redirect="/dashboard/workbench", Icon="ele-HomeFilled", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=10 },
-            new SysMenu{ Id=252885263002110, Pid=252885263002100, Title="工作台", Path="/dashboard/workbench", Name="workbench", Component="/dashboard/workbench/index", IsAffix=true, Icon="ele-DataAnalysis", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
-            // new SysMenu{ Id=252885263002111, Pid=252885263002100, Title="分析页", Path="/dashboard/analysis", Name="analysis", Component="/dashboard/analysis/index", Icon="ele-DataLine", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=101 },
+            new SysMenu{ Id=252885263002100, Pid=0, Title="数据面板", Path="/dashboard", Name="dashboard", Component="Layout", Redirect="/dashboard/home", Icon="ele-HomeFilled", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=10 },
+            new SysMenu{ Id=252885263002110, Pid=252885263002100, Title="工作台", Path="/dashboard/home", Name="home", Component="/home/index", IsAffix=true, Icon="ele-HomeFilled", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
 
             new SysMenu{ Id=252885263002200, Pid=0, Title="系统管理", Path="/system", Name="system", Component="Layout", Redirect="/system/user", Icon="ele-Setting", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
 
@@ -58,8 +57,10 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
             new SysMenu{ Id=252885263002253, Pid=252885263002250, Title="职位增加", Permission="sysPos:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
             new SysMenu{ Id=252885263002254, Pid=252885263002250, Title="职位删除", Permission="sysPos:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
 
-            new SysMenu{ Id=252885263002260, Pid=252885263002200, Title="修改密码", Path="/system/password", Name="sysPassword", Component="/system/password/index",Icon="ele-Hide", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=150 },
+            new SysMenu{ Id=252885263002260, Pid=252885263002200, Title="个人中心", Path="/system/userCenter", Name="sysUserCenter", Component="/system/user/component/userCenter",Icon="ele-Medal", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=150 },
             new SysMenu{ Id=252885263002261, Pid=252885263002260, Title="修改密码", Permission="sysUser:changePwd", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
+            new SysMenu{ Id=252885263002262, Pid=252885263002260, Title="更新信息", Permission="sysUser:updateBase", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
+            new SysMenu{ Id=252885263002263, Pid=252885263002260, Title="电子签名", Permission="sysUser:signature", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
 
             new SysMenu{ Id=252885263002300, Pid=0, Title="平台管理", Path="/platform", Name="platform", Component="Layout", Redirect="/platform/tenant", Icon="ele-Menu", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=200 },
 

+ 18 - 5
Admin.NET/Admin.NET.Core/SeedData/SysRoleMenuSeedData.cs

@@ -59,8 +59,11 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=252885263003143, RoleId=252885263003721, MenuId=252885263002252 },
             new SysRoleMenu{ Id=252885263003144, RoleId=252885263003721, MenuId=252885263002253 },
             new SysRoleMenu{ Id=252885263003145, RoleId=252885263003721, MenuId=252885263002254 },
-            // 修改密码
+            // 个人中心
             new SysRoleMenu{ Id=252885263003151, RoleId=252885263003721, MenuId=252885263002260 },
+            new SysRoleMenu{ Id=252885263003152, RoleId=252885263003721, MenuId=252885263002261 },
+            new SysRoleMenu{ Id=252885263003153, RoleId=252885263003721, MenuId=252885263002262 },
+            new SysRoleMenu{ Id=252885263003154, RoleId=252885263003721, MenuId=252885263002263 },
 
             // 平台管理
             new SysRoleMenu{ Id=252885263003200, RoleId=252885263003721, MenuId=252885263002300 },
@@ -87,12 +90,13 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=252885263003241, RoleId=252885263003721, MenuId=252885263002370 },
             new SysRoleMenu{ Id=252885263003242, RoleId=252885263003721, MenuId=252885263002371 },
             new SysRoleMenu{ Id=252885263003243, RoleId=252885263003721, MenuId=252885263002372 },
-            // 数据资源
+            // 行政区域
             new SysRoleMenu{ Id=252885263003251, RoleId=252885263003721, MenuId=252885263002380 },
             new SysRoleMenu{ Id=252885263003252, RoleId=252885263003721, MenuId=252885263002381 },
             new SysRoleMenu{ Id=252885263003253, RoleId=252885263003721, MenuId=252885263002382 },
             new SysRoleMenu{ Id=252885263003254, RoleId=252885263003721, MenuId=252885263002383 },
             new SysRoleMenu{ Id=252885263003255, RoleId=252885263003721, MenuId=252885263002384 },
+            new SysRoleMenu{ Id=252885263003256, RoleId=252885263003721, MenuId=252885263002385 },
             // 文件管理
             new SysRoleMenu{ Id=252885263003261, RoleId=252885263003721, MenuId=252885263002390 },
             new SysRoleMenu{ Id=252885263003262, RoleId=252885263003721, MenuId=252885263002391 },
@@ -122,8 +126,11 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             //new SysRoleMenu{ Id=252885263004002, RoleId=252885263003722, MenuId=252885263002111 },
             // 系统管理
             new SysRoleMenu{ Id=252885263004100, RoleId=252885263003722, MenuId=252885263002200 },
-            // 密码修改
+            // 个人中心
             new SysRoleMenu{ Id=252885263004151, RoleId=252885263003722, MenuId=252885263002260 },
+            new SysRoleMenu{ Id=252885263004152, RoleId=252885263003722, MenuId=252885263002261 },
+            new SysRoleMenu{ Id=252885263004153, RoleId=252885263003722, MenuId=252885263002262 },
+            new SysRoleMenu{ Id=252885263004154, RoleId=252885263003722, MenuId=252885263002263 },
 
             // 数据面板【user3/252885263003724】
             new SysRoleMenu{ Id=252885263005000, RoleId=252885263003724, MenuId=252885263002100 },
@@ -131,8 +138,11 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             //new SysRoleMenu{ Id=252885263005002, RoleId=252885263003724, MenuId=252885263002111 },
             // 系统管理
             new SysRoleMenu{ Id=252885263005100, RoleId=252885263003724, MenuId=252885263002200 },
-            // 密码修改
+            // 个人中心
             new SysRoleMenu{ Id=252885263005151, RoleId=252885263003724, MenuId=252885263002260},
+            new SysRoleMenu{ Id=252885263005152, RoleId=252885263003724, MenuId=252885263002261 },
+            new SysRoleMenu{ Id=252885263005153, RoleId=252885263003724, MenuId=252885263002262 },
+            new SysRoleMenu{ Id=252885263005154, RoleId=252885263003724, MenuId=252885263002263 },
 
             // 其他租户
             // 数据面板【admin1/252885263004721】
@@ -180,8 +190,11 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=252885263103143, RoleId=252885263004721, MenuId=252885263002252 },
             new SysRoleMenu{ Id=252885263103144, RoleId=252885263004721, MenuId=252885263002253 },
             new SysRoleMenu{ Id=252885263103145, RoleId=252885263004721, MenuId=252885263002254 },
-            // 密码修改
+            // 个人中心
             new SysRoleMenu{ Id=252885263103151, RoleId=252885263004721, MenuId=252885263002260 },
+            new SysRoleMenu{ Id=252885263103152, RoleId=252885263004721, MenuId=252885263002261 },
+            new SysRoleMenu{ Id=252885263103153, RoleId=252885263004721, MenuId=252885263002262 },
+            new SysRoleMenu{ Id=252885263103154, RoleId=252885263004721, MenuId=252885263002263 },
         };
     }
 }

+ 15 - 5
Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginUserOutput.cs

@@ -5,11 +5,6 @@
 /// </summary>
 public class LoginUserOutput
 {
-    /// <summary>
-    /// 用户Id
-    /// </summary>
-    public long UserId { get; set; }
-
     /// <summary>
     /// 账号名称
     /// </summary>
@@ -30,6 +25,16 @@ public class LoginUserOutput
     /// </summary>
     public string Introduction { get; set; }
 
+    /// <summary>
+    /// 地址
+    /// </summary>
+    public string Address { get; set; }
+
+    /// <summary>
+    /// 电子签名
+    /// </summary>
+    public string Signature { get; set; }
+
     /// <summary>
     /// 机构Id
     /// </summary>
@@ -40,6 +45,11 @@ public class LoginUserOutput
     /// </summary>
     public string OrgName { get; set; }
 
+    /// <summary>
+    /// 职位名称
+    /// </summary>
+    public string PosName { get; set; }
+
     /// <summary>
     /// 按钮权限集合
     /// </summary>

+ 8 - 0
Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs

@@ -110,6 +110,9 @@ public class SysAuthService : IDynamicApiController, ITransient
         if (user == null)
             throw Oops.Oh(ErrorCodeEnum.D1011);
 
+        var org = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysOrg>>().GetFirstAsync(u => u.Id == user.OrgId);
+        var pos = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysPos>>().GetFirstAsync(u => u.Id == user.PosId);
+
         // 按钮权限集合
         var buttons = await _sysMenuService.GetPermCodeList();
 
@@ -136,6 +139,11 @@ public class SysAuthService : IDynamicApiController, ITransient
             Account = user.Account,
             RealName = user.RealName,
             Avatar = user.Avatar,
+            Address = user.Address,
+            Signature = user.Signature,
+            OrgId = user.OrgId,
+            OrgName = org?.Name,
+            PosName = pos?.Name,
             Buttons = buttons
         };
     }

+ 1 - 0
Admin.NET/Admin.NET.Core/Service/DataBase/SysDataBaseService.cs

@@ -32,6 +32,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
     /// <param name="configId">ConfigId</param>
     /// <returns></returns>
     [HttpGet("/sysDatabase/columnList")]
+    [AllowAnonymous]
     public List<DbColumnOutput> GetColumnList(string tableName, string configId = SqlSugarConst.ConfigId)
     {
         var db = _db.AsTenant().GetConnectionScope(configId);

+ 47 - 11
Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs

@@ -49,11 +49,12 @@ public class SysFileService : IDynamicApiController, ITransient
     /// 上传文件
     /// </summary>
     /// <param name="file"></param>
+    /// <param name="path"></param>
     /// <returns></returns>
     [HttpPost("/sysFile/upload")]
-    public async Task<FileOutput> UploadFile([Required] IFormFile file)
+    public async Task<FileOutput> UploadFile([Required] IFormFile file, string path = "")
     {
-        var sysFile = await HandleUploadFile(file);
+        var sysFile = await HandleUploadFile(file, path);
         return new FileOutput
         {
             Id = sysFile.Id,
@@ -145,19 +146,24 @@ public class SysFileService : IDynamicApiController, ITransient
     /// 上传文件
     /// </summary>
     /// <param name="file">文件</param>
+    /// <param name="savePath">路径</param>
     /// <returns></returns>
-    private async Task<SysFile> HandleUploadFile(IFormFile file)
+    private async Task<SysFile> HandleUploadFile(IFormFile file, string savePath = "")
     {
         if (file == null) throw Oops.Oh(ErrorCodeEnum.D8000);
 
-        string path = _uploadOptions.Path;
-        var reg = new Regex(@"(\{.+?})");
-        var match = reg.Matches(path);
-        match.ToList().ForEach(a =>
+        var path = savePath;
+        if (string.IsNullOrWhiteSpace(savePath))
         {
-            var str = DateTime.Now.ToString(a.ToString().Substring(1, a.Length - 2));
-            path = path.Replace(a.ToString(), str);
-        });
+            path = _uploadOptions.Path;
+            var reg = new Regex(@"(\{.+?})");
+            var match = reg.Matches(path);
+            match.ToList().ForEach(a =>
+            {
+                var str = DateTime.Now.ToString(a.ToString().Substring(1, a.Length - 2)); // 每天一个目录
+                path = path.Replace(a.ToString(), str);
+            });
+        }
 
         if (!_uploadOptions.ContentType.Contains(file.ContentType))
             throw Oops.Oh(ErrorCodeEnum.D8001);
@@ -210,7 +216,7 @@ public class SysFileService : IDynamicApiController, ITransient
 
             var realFile = Path.Combine(filePath, finalName);
             IDetector detector;
-            using (var stream = File.Create(realFile)) 
+            using (var stream = File.Create(realFile))
             {
                 await file.CopyToAsync(stream);
                 detector = stream.DetectFiletype();
@@ -242,4 +248,34 @@ public class SysFileService : IDynamicApiController, ITransient
     {
         return await _OSSService.PresignedGetObjectAsync(bucketName, fileName, 7);
     }
+
+    /// <summary>
+    /// 上传头像
+    /// </summary>
+    /// <param name="file"></param>
+    /// <returns></returns>
+    [HttpPost("/sysFile/uploadAvatar")]
+    public async Task<FileOutput> UploadAvatar([Required] IFormFile file)
+    {
+        var res = await UploadFile(file, "Avatar");
+        var userId = App.GetService<UserManager>().UserId;
+        await _sysFileRep.ChangeRepository<SqlSugarRepository<SysUser>>()
+            .UpdateAsync(u => new SysUser() { Avatar = res.Url }, u => u.Id == userId);
+        return res;
+    }
+
+    /// <summary>
+    /// 上传电子签名
+    /// </summary>
+    /// <param name="file"></param>
+    /// <returns></returns>
+    [HttpPost("/sysFile/uploadSignature")]
+    public async Task<FileOutput> UploadSignature([Required] IFormFile file)
+    {
+        var res = await UploadFile(file, "Signature");
+        var userId = App.GetService<UserManager>().UserId;
+        await _sysFileRep.ChangeRepository<SqlSugarRepository<SysUser>>()
+            .UpdateAsync(u => new SysUser() { Signature = res.Url }, u => u.Id == userId);
+        return res;
+    }
 }

+ 0 - 5
Admin.NET/Admin.NET.Core/Service/User/Dto/UserInput.cs

@@ -46,11 +46,6 @@ public class AddUserInput : SysUser
     [Required(ErrorMessage = "真实姓名不能为空")]
     public override string RealName { get; set; }
 
-    /// <summary>
-    /// 账号
-    /// </summary>
-    public override string Password { get; set; } = CommonConst.SysPassword;
-
     /// <summary>
     /// 角色集合
     /// </summary>

+ 15 - 4
Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs

@@ -122,13 +122,24 @@ public class SysUserService : IDynamicApiController, ITransient
     }
 
     /// <summary>
-    /// 查看用户
+    /// 查看用户基本信息
     /// </summary>
     /// <returns></returns>
-    [HttpGet("/sysUser/detail")]
-    public async Task<SysUser> GetUser(long id)
+    [HttpGet("/sysUser/base")]
+    public async Task<SysUser> GetUserBase()
     {
-        return await _sysUserRep.GetFirstAsync(u => u.Id == id);
+        return await _sysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId);
+    }
+
+    /// <summary>
+    /// 设置用户基本信息
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost("/sysUser/base")]
+    public async Task<int> UpdateUserBase(SysUser user)
+    {
+        return await _sysUserRep.AsUpdateable(user)
+            .IgnoreColumns(u => new { u.CreateTime, u.Account, u.Password, u.AccountType, u.OrgId, u.PosId }).ExecuteCommandAsync();
     }
 
     /// <summary>

+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/Test.db


File diff suppressed because it is too large
+ 189 - 546
Web/package-lock.json


+ 4 - 2
Web/package.json

@@ -36,10 +36,12 @@
 		"vue-grid-layout": "^3.0.0-beta1",
 		"vue-i18n": "^9.2.2",
 		"vue-router": "^4.1.2",
-		"vue-json-pretty": "^2.2.2"
+		"vue-json-pretty": "^2.2.2",
+		"vue-signature-pad": "^3.0.2",
+		"vue3-tree-org": "^4.1.1"
 	},
 	"devDependencies": {
-		"@types/node": "^18.0.6",
+		"@types/node": "^18.8.4",
 		"@types/nprogress": "^0.2.0",
 		"@types/sortablejs": "^1.13.0",
 		"@typescript-eslint/eslint-plugin": "^5.30.7",

+ 1 - 0
Web/plugins.d.ts

@@ -3,3 +3,4 @@ declare module 'qrcodejs2-fixes';
 declare module 'splitpanes';
 declare module 'js-cookie';
 declare module 'vform3-builds'
+declare module 'vue-signature-pad'

+ 2705 - 0
Web/pnpm-lock.yaml

@@ -0,0 +1,2705 @@
+lockfileVersion: 5.4
+
+specifiers:
+  '@element-plus/icons-vue': ^2.0.6
+  '@types/node': ^18.0.6
+  '@types/nprogress': ^0.2.0
+  '@types/sortablejs': ^1.13.0
+  '@typescript-eslint/eslint-plugin': ^5.30.7
+  '@typescript-eslint/parser': ^5.30.7
+  '@vitejs/plugin-vue': ^3.1.0
+  '@vue/compiler-sfc': ^3.2.37
+  '@wangeditor/editor': ^5.1.11
+  axios: ^0.27.2
+  countup.js: ^2.3.2
+  cropperjs: ^1.5.12
+  dotenv: ^16.0.1
+  echarts: ^5.3.3
+  echarts-gl: ^2.0.9
+  echarts-wordcloud: ^2.0.0
+  element-plus: ^2.2.9
+  eslint: ^8.20.0
+  eslint-plugin-vue: ^9.2.0
+  js-cookie: ^3.0.1
+  jsplumb: ^2.15.6
+  mitt: ^3.0.0
+  nprogress: ^0.2.0
+  pinia: ^2.0.16
+  prettier: ^2.7.1
+  print-js: ^1.6.0
+  qrcodejs2-fixes: ^0.0.2
+  sass: ^1.53.0
+  sass-loader: ^13.0.2
+  screenfull: ^6.0.2
+  sortablejs: ^1.15.0
+  splitpanes: ^3.1.1
+  typescript: ^4.7.4
+  vite: ^3.0.4
+  vue: ^3.2.37
+  vue-clipboard3: ^2.0.0
+  vue-eslint-parser: ^9.0.3
+  vue-grid-layout: ^3.0.0-beta1
+  vue-i18n: ^9.2.2
+  vue-router: ^4.1.2
+
+dependencies:
+  '@element-plus/icons-vue': 2.0.10_vue@3.2.40
+  '@wangeditor/editor': 5.1.21
+  axios: 0.27.2
+  countup.js: 2.3.2
+  cropperjs: 1.5.12
+  echarts: 5.4.0
+  echarts-gl: 2.0.9_echarts@5.4.0
+  echarts-wordcloud: 2.0.0_echarts@5.4.0
+  element-plus: 2.2.17_vue@3.2.40
+  js-cookie: 3.0.1
+  jsplumb: 2.15.6
+  mitt: 3.0.0
+  nprogress: 0.2.0
+  pinia: 2.0.23_bfjwoga25wxjazzogo7o372nwq
+  print-js: 1.6.0
+  qrcodejs2-fixes: 0.0.2
+  screenfull: 6.0.2
+  sortablejs: 1.15.0
+  splitpanes: 3.1.1
+  vue: 3.2.40
+  vue-clipboard3: 2.0.0
+  vue-grid-layout: 3.0.0-beta1
+  vue-i18n: 9.2.2_vue@3.2.40
+  vue-router: 4.1.5_vue@3.2.40
+
+devDependencies:
+  '@types/node': 18.8.4
+  '@types/nprogress': 0.2.0
+  '@types/sortablejs': 1.15.0
+  '@typescript-eslint/eslint-plugin': 5.40.0_25sstg4uu2sk4pm7xcyzuov7xq
+  '@typescript-eslint/parser': 5.40.0_z4bbprzjrhnsfa24uvmcbu7f5q
+  '@vitejs/plugin-vue': 3.1.2_vite@3.1.7+vue@3.2.40
+  '@vue/compiler-sfc': 3.2.40
+  dotenv: 16.0.3
+  eslint: 8.25.0
+  eslint-plugin-vue: 9.6.0_eslint@8.25.0
+  prettier: 2.7.1
+  sass: 1.55.0
+  sass-loader: 13.1.0_sass@1.55.0
+  typescript: 4.8.4
+  vite: 3.1.7_sass@1.55.0
+  vue-eslint-parser: 9.1.0_eslint@8.25.0
+
+packages:
+
+  /@babel/helper-string-parser/7.19.4:
+    resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/helper-validator-identifier/7.19.1:
+    resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/parser/7.19.4:
+    resolution: {integrity: sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+    dependencies:
+      '@babel/types': 7.19.4
+
+  /@babel/runtime/7.19.4:
+    resolution: {integrity: sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      regenerator-runtime: 0.13.9
+    dev: false
+
+  /@babel/types/7.19.4:
+    resolution: {integrity: sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-string-parser': 7.19.4
+      '@babel/helper-validator-identifier': 7.19.1
+      to-fast-properties: 2.0.0
+
+  /@ctrl/tinycolor/3.4.1:
+    resolution: {integrity: sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==}
+    engines: {node: '>=10'}
+    dev: false
+
+  /@element-plus/icons-vue/2.0.10_vue@3.2.40:
+    resolution: {integrity: sha512-ygEZ1mwPjcPo/OulhzLE7mtDrQBWI8vZzEWSNB2W/RNCRjoQGwbaK4N8lV4rid7Ts4qvySU3njMN7YCiSlSaTQ==}
+    peerDependencies:
+      vue: ^3.2.0
+    dependencies:
+      vue: 3.2.40
+    dev: false
+
+  /@esbuild/android-arm/0.15.10:
+    resolution: {integrity: sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-loong64/0.15.10:
+    resolution: {integrity: sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@eslint/eslintrc/1.3.3:
+    resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      ajv: 6.12.6
+      debug: 4.3.4
+      espree: 9.4.0
+      globals: 13.17.0
+      ignore: 5.2.0
+      import-fresh: 3.3.0
+      js-yaml: 4.1.0
+      minimatch: 3.1.2
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@floating-ui/core/1.0.1:
+    resolution: {integrity: sha512-bO37brCPfteXQfFY0DyNDGB3+IMe4j150KFQcgJ5aBP295p9nBGeHEs/p0czrRbtlHq4Px/yoPXO/+dOCcF4uA==}
+    dev: false
+
+  /@floating-ui/dom/1.0.2:
+    resolution: {integrity: sha512-5X9WSvZ8/fjy3gDu8yx9HAA4KG1lazUN2P4/VnaXLxTO9Dz53HI1oYoh1OlhqFNlHgGDiwFX5WhFCc2ljbW3yA==}
+    dependencies:
+      '@floating-ui/core': 1.0.1
+    dev: false
+
+  /@humanwhocodes/config-array/0.10.7:
+    resolution: {integrity: sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==}
+    engines: {node: '>=10.10.0'}
+    dependencies:
+      '@humanwhocodes/object-schema': 1.2.1
+      debug: 4.3.4
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@humanwhocodes/module-importer/1.0.1:
+    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+    engines: {node: '>=12.22'}
+    dev: true
+
+  /@humanwhocodes/object-schema/1.2.1:
+    resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
+    dev: true
+
+  /@interactjs/actions/1.10.17:
+    resolution: {integrity: sha512-wyB1ZqpaZy5gmz6VDqK9KWh98xKnFgL7VyLvxHODFi9V0IYX4HJAAOBlhtfze0D1R1f1cY+gqPDK+dLaHMlE+w==}
+    peerDependencies:
+      '@interactjs/core': 1.10.17
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/actions/1.10.17_farzh4kmmmdsqeu7trbjloi3zi:
+    resolution: {integrity: sha512-wyB1ZqpaZy5gmz6VDqK9KWh98xKnFgL7VyLvxHODFi9V0IYX4HJAAOBlhtfze0D1R1f1cY+gqPDK+dLaHMlE+w==}
+    peerDependencies:
+      '@interactjs/core': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/core': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/auto-scroll/1.10.17_@interactjs+utils@1.10.17:
+    resolution: {integrity: sha512-IQcW7N3xOaoL8RnAGOGMk0Y2gue7L4S3BT6Id4VBBu8so163DtLiZVW6jXu9rKVntzbluaAeqNZlfAVyu3kIWg==}
+    peerDependencies:
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/auto-start/1.10.17:
+    resolution: {integrity: sha512-qYVxhAbYnwxjD/NLEegUoAST7WASJ4VmWNjsyWRx/js5Op+I4E2zteARIeZGgrutcGIXMCcQzhCMgE3PjOpbpw==}
+    peerDependencies:
+      '@interactjs/core': 1.10.17
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/auto-start/1.10.17_farzh4kmmmdsqeu7trbjloi3zi:
+    resolution: {integrity: sha512-qYVxhAbYnwxjD/NLEegUoAST7WASJ4VmWNjsyWRx/js5Op+I4E2zteARIeZGgrutcGIXMCcQzhCMgE3PjOpbpw==}
+    peerDependencies:
+      '@interactjs/core': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/core': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/core/1.10.17_@interactjs+utils@1.10.17:
+    resolution: {integrity: sha512-rL9w+83HDRuXub8Ezqs+97CYLl/ne7bLT/sAeduUWaxYhsW9iOqBoob9JnkkCZOaOsYizWI1EWy0+fNc5ibtLQ==}
+    peerDependencies:
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/utils': 1.10.17
+    dev: false
+
+  /@interactjs/dev-tools/1.10.17_cn5slzagijafj3kjb52cs5znym:
+    resolution: {integrity: sha512-Oi9nEw3FfSwkNmW+V0WwdHqvzEkVHc24mH1v5EjRn60sqgrGLK9nTQ+NSuqcnUY8GxC3TkyuxnsOodxiadIRmA==}
+    peerDependencies:
+      '@interactjs/modifiers': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/modifiers': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/dev-tools/1.10.17_xbmse7bb75tf4dexy2xpppeyve:
+    resolution: {integrity: sha512-Oi9nEw3FfSwkNmW+V0WwdHqvzEkVHc24mH1v5EjRn60sqgrGLK9nTQ+NSuqcnUY8GxC3TkyuxnsOodxiadIRmA==}
+    peerDependencies:
+      '@interactjs/modifiers': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/modifiers': 1.10.17_farzh4kmmmdsqeu7trbjloi3zi
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/inertia/1.10.17_orxel6eubagr4ggpgjxuetzvmi:
+    resolution: {integrity: sha512-41vbYUjZIDCKt2/yhmjPrEW5+0uoL/hldFsll9pkvnLhmm12Xk0VXOlmR2zXKAmsTK3fJlKMyBYUX92qHLkyVQ==}
+    peerDependencies:
+      '@interactjs/core': 1.10.17
+      '@interactjs/modifiers': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/core': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/modifiers': 1.10.17_farzh4kmmmdsqeu7trbjloi3zi
+      '@interactjs/offset': 1.10.17_farzh4kmmmdsqeu7trbjloi3zi
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/interact/1.10.17:
+    resolution: {integrity: sha512-NyKsf8EFudvdahBjPz1Gt5QnynVwa/2LUfBc2/w8QOnOBiyzUm0HLloJSaB8a50QbQkSWN243/Lgpd8GTMQvuQ==}
+    dependencies:
+      '@interactjs/core': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/types': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dev: false
+
+  /@interactjs/interactjs/1.10.17:
+    resolution: {integrity: sha512-hHmiukARbZhiM12zNKx0yQlFVl4C+NMeYNAYh6Mf9U3ZziQ47C+JEW8Gr7Zr/MxfNZyPu5nLKCpVQjh/JvBO9g==}
+    dependencies:
+      '@interactjs/actions': 1.10.17_farzh4kmmmdsqeu7trbjloi3zi
+      '@interactjs/auto-scroll': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/auto-start': 1.10.17_farzh4kmmmdsqeu7trbjloi3zi
+      '@interactjs/core': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/dev-tools': 1.10.17_xbmse7bb75tf4dexy2xpppeyve
+      '@interactjs/inertia': 1.10.17_orxel6eubagr4ggpgjxuetzvmi
+      '@interactjs/interact': 1.10.17
+      '@interactjs/modifiers': 1.10.17_farzh4kmmmdsqeu7trbjloi3zi
+      '@interactjs/offset': 1.10.17_farzh4kmmmdsqeu7trbjloi3zi
+      '@interactjs/pointer-events': 1.10.17_farzh4kmmmdsqeu7trbjloi3zi
+      '@interactjs/reflow': 1.10.17_farzh4kmmmdsqeu7trbjloi3zi
+      '@interactjs/utils': 1.10.17
+    dev: false
+
+  /@interactjs/modifiers/1.10.17:
+    resolution: {integrity: sha512-Dxw8kv9VBIxnhNvQncR6CKAGMzKXczLvuAUIdSPFYtyerX/XiDulJUqhR+jVKNp/WjF1DvdBxWo0kGGLbM84LQ==}
+    peerDependencies:
+      '@interactjs/core': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/snappers': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/modifiers/1.10.17_farzh4kmmmdsqeu7trbjloi3zi:
+    resolution: {integrity: sha512-Dxw8kv9VBIxnhNvQncR6CKAGMzKXczLvuAUIdSPFYtyerX/XiDulJUqhR+jVKNp/WjF1DvdBxWo0kGGLbM84LQ==}
+    peerDependencies:
+      '@interactjs/core': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/core': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/snappers': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/offset/1.10.17_farzh4kmmmdsqeu7trbjloi3zi:
+    resolution: {integrity: sha512-wWBnIQWgLrmJNTBbd/FdxHxAJjiXl/5ND8Jbw2DuP9gIGDxhFSdEt62Fgqimn9ICb8v8ycvSLObEmcvJF/8hQQ==}
+    peerDependencies:
+      '@interactjs/core': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/core': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/pointer-events/1.10.17_farzh4kmmmdsqeu7trbjloi3zi:
+    resolution: {integrity: sha512-VsfluouEKb8QRGyH6jQATCW+QdAd/3dkENS7rj2m+EcVUhz2Ob5mpMRopjALi4pwltMowqTfuJ4LtwMSX2G29A==}
+    peerDependencies:
+      '@interactjs/core': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/core': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/reflow/1.10.17_farzh4kmmmdsqeu7trbjloi3zi:
+    resolution: {integrity: sha512-ncpWP5k93FRQptEhjzPZsbuRRajd4rkW17lDavCrEjrDi/LHnYekWGqZTaFzfJ80n1x8xUm9ujDjxCTylNqEIA==}
+    peerDependencies:
+      '@interactjs/core': 1.10.17
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/core': 1.10.17_@interactjs+utils@1.10.17
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/snappers/1.10.17:
+    resolution: {integrity: sha512-m753DGsNOts797e3zDT6wqELoc+BlpIC1w+TyMyISRxU6n1RlS8Q6LHBGgwAgV79LHLaahv/a5haFF9H1VG0FQ==}
+    peerDependencies:
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/snappers/1.10.17_@interactjs+utils@1.10.17:
+    resolution: {integrity: sha512-m753DGsNOts797e3zDT6wqELoc+BlpIC1w+TyMyISRxU6n1RlS8Q6LHBGgwAgV79LHLaahv/a5haFF9H1VG0FQ==}
+    peerDependencies:
+      '@interactjs/utils': 1.10.17
+    dependencies:
+      '@interactjs/utils': 1.10.17
+    optionalDependencies:
+      '@interactjs/interact': 1.10.17
+    dev: false
+
+  /@interactjs/types/1.10.17:
+    resolution: {integrity: sha512-X2JpoM7xUw0p9Me0tMaI0HNfcF/Hd07ZZlzpnpEMpGerUZOLoyeThrV9P+CrBHxZrluWJrigJbcdqXliFd0YMA==}
+    dev: false
+
+  /@interactjs/utils/1.10.17:
+    resolution: {integrity: sha512-sZAW08CkqgvqRjUIaLRjScjObcCzN9D75yekLA21EClYAZIhi4A+GEt2z/WqOCOksTaEPLYmQyhkpXcboc0LhQ==}
+    dev: false
+
+  /@intlify/core-base/9.2.2:
+    resolution: {integrity: sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/devtools-if': 9.2.2
+      '@intlify/message-compiler': 9.2.2
+      '@intlify/shared': 9.2.2
+      '@intlify/vue-devtools': 9.2.2
+    dev: false
+
+  /@intlify/devtools-if/9.2.2:
+    resolution: {integrity: sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/shared': 9.2.2
+    dev: false
+
+  /@intlify/message-compiler/9.2.2:
+    resolution: {integrity: sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/shared': 9.2.2
+      source-map: 0.6.1
+    dev: false
+
+  /@intlify/shared/9.2.2:
+    resolution: {integrity: sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==}
+    engines: {node: '>= 14'}
+    dev: false
+
+  /@intlify/vue-devtools/9.2.2:
+    resolution: {integrity: sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/core-base': 9.2.2
+      '@intlify/shared': 9.2.2
+    dev: false
+
+  /@nodelib/fs.scandir/2.1.5:
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      run-parallel: 1.2.0
+    dev: true
+
+  /@nodelib/fs.stat/2.0.5:
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /@nodelib/fs.walk/1.2.8:
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.scandir': 2.1.5
+      fastq: 1.13.0
+    dev: true
+
+  /@sxzz/popperjs-es/2.11.7:
+    resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
+    dev: false
+
+  /@transloadit/prettier-bytes/0.0.7:
+    resolution: {integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==}
+    dev: false
+
+  /@types/event-emitter/0.3.3:
+    resolution: {integrity: sha512-UfnOK1pIxO7P+EgPRZXD9jMpimd8QEFcEZ5R67R1UhGbv4zghU5+NE7U8M8G9H5Jc8FI51rqDWQs6FtUfq2e/Q==}
+    dev: false
+
+  /@types/json-schema/7.0.11:
+    resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
+    dev: true
+
+  /@types/lodash-es/4.17.6:
+    resolution: {integrity: sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==}
+    dependencies:
+      '@types/lodash': 4.14.186
+    dev: false
+
+  /@types/lodash/4.14.186:
+    resolution: {integrity: sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==}
+    dev: false
+
+  /@types/node/18.8.4:
+    resolution: {integrity: sha512-WdlVphvfR/GJCLEMbNA8lJ0lhFNBj4SW3O+O5/cEGw9oYrv0al9zTwuQsq+myDUXgNx2jgBynoVgZ2MMJ6pbow==}
+    dev: true
+
+  /@types/nprogress/0.2.0:
+    resolution: {integrity: sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==}
+    dev: true
+
+  /@types/sortablejs/1.15.0:
+    resolution: {integrity: sha512-qrhtM7M41EhH4tZQTNw2/RJkxllBx3reiJpTbgWCM2Dx0U1sZ6LwKp9lfNln9uqE26ZMKUaPEYaD4rzvOWYtZw==}
+    dev: true
+
+  /@types/web-bluetooth/0.0.15:
+    resolution: {integrity: sha512-w7hEHXnPMEZ+4nGKl/KDRVpxkwYxYExuHOYXyzIzCDzEZ9ZCGMAewulr9IqJu2LR4N37fcnb1XVeuZ09qgOxhA==}
+    dev: false
+
+  /@typescript-eslint/eslint-plugin/5.40.0_25sstg4uu2sk4pm7xcyzuov7xq:
+    resolution: {integrity: sha512-FIBZgS3DVJgqPwJzvZTuH4HNsZhHMa9SjxTKAZTlMsPw/UzpEjcf9f4dfgDJEHjK+HboUJo123Eshl6niwEm/Q==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^5.0.0
+      eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/parser': 5.40.0_z4bbprzjrhnsfa24uvmcbu7f5q
+      '@typescript-eslint/scope-manager': 5.40.0
+      '@typescript-eslint/type-utils': 5.40.0_z4bbprzjrhnsfa24uvmcbu7f5q
+      '@typescript-eslint/utils': 5.40.0_z4bbprzjrhnsfa24uvmcbu7f5q
+      debug: 4.3.4
+      eslint: 8.25.0
+      ignore: 5.2.0
+      regexpp: 3.2.0
+      semver: 7.3.8
+      tsutils: 3.21.0_typescript@4.8.4
+      typescript: 4.8.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/parser/5.40.0_z4bbprzjrhnsfa24uvmcbu7f5q:
+    resolution: {integrity: sha512-Ah5gqyX2ySkiuYeOIDg7ap51/b63QgWZA7w6AHtFrag7aH0lRQPbLzUjk0c9o5/KZ6JRkTTDKShL4AUrQa6/hw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/scope-manager': 5.40.0
+      '@typescript-eslint/types': 5.40.0
+      '@typescript-eslint/typescript-estree': 5.40.0_typescript@4.8.4
+      debug: 4.3.4
+      eslint: 8.25.0
+      typescript: 4.8.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/scope-manager/5.40.0:
+    resolution: {integrity: sha512-d3nPmjUeZtEWRvyReMI4I1MwPGC63E8pDoHy0BnrYjnJgilBD3hv7XOiETKLY/zTwI7kCnBDf2vWTRUVpYw0Uw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      '@typescript-eslint/types': 5.40.0
+      '@typescript-eslint/visitor-keys': 5.40.0
+    dev: true
+
+  /@typescript-eslint/type-utils/5.40.0_z4bbprzjrhnsfa24uvmcbu7f5q:
+    resolution: {integrity: sha512-nfuSdKEZY2TpnPz5covjJqav+g5qeBqwSHKBvz7Vm1SAfy93SwKk/JeSTymruDGItTwNijSsno5LhOHRS1pcfw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '*'
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/typescript-estree': 5.40.0_typescript@4.8.4
+      '@typescript-eslint/utils': 5.40.0_z4bbprzjrhnsfa24uvmcbu7f5q
+      debug: 4.3.4
+      eslint: 8.25.0
+      tsutils: 3.21.0_typescript@4.8.4
+      typescript: 4.8.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/types/5.40.0:
+    resolution: {integrity: sha512-V1KdQRTXsYpf1Y1fXCeZ+uhjW48Niiw0VGt4V8yzuaDTU8Z1Xl7yQDyQNqyAFcVhpYXIVCEuxSIWTsLDpHgTbw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dev: true
+
+  /@typescript-eslint/typescript-estree/5.40.0_typescript@4.8.4:
+    resolution: {integrity: sha512-b0GYlDj8TLTOqwX7EGbw2gL5EXS2CPEWhF9nGJiGmEcmlpNBjyHsTwbqpyIEPVpl6br4UcBOYlcI2FJVtJkYhg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/types': 5.40.0
+      '@typescript-eslint/visitor-keys': 5.40.0
+      debug: 4.3.4
+      globby: 11.1.0
+      is-glob: 4.0.3
+      semver: 7.3.8
+      tsutils: 3.21.0_typescript@4.8.4
+      typescript: 4.8.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/utils/5.40.0_z4bbprzjrhnsfa24uvmcbu7f5q:
+    resolution: {integrity: sha512-MO0y3T5BQ5+tkkuYZJBjePewsY+cQnfkYeRqS6tPh28niiIwPnQ1t59CSRcs1ZwJJNOdWw7rv9pF8aP58IMihA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
+    dependencies:
+      '@types/json-schema': 7.0.11
+      '@typescript-eslint/scope-manager': 5.40.0
+      '@typescript-eslint/types': 5.40.0
+      '@typescript-eslint/typescript-estree': 5.40.0_typescript@4.8.4
+      eslint: 8.25.0
+      eslint-scope: 5.1.1
+      eslint-utils: 3.0.0_eslint@8.25.0
+      semver: 7.3.8
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+    dev: true
+
+  /@typescript-eslint/visitor-keys/5.40.0:
+    resolution: {integrity: sha512-ijJ+6yig+x9XplEpG2K6FUdJeQGGj/15U3S56W9IqXKJqleuD7zJ2AX/miLezwxpd7ZxDAqO87zWufKg+RPZyQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      '@typescript-eslint/types': 5.40.0
+      eslint-visitor-keys: 3.3.0
+    dev: true
+
+  /@uppy/companion-client/2.2.2:
+    resolution: {integrity: sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==}
+    dependencies:
+      '@uppy/utils': 4.1.3
+      namespace-emitter: 2.0.1
+    dev: false
+
+  /@uppy/core/2.3.4:
+    resolution: {integrity: sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==}
+    dependencies:
+      '@transloadit/prettier-bytes': 0.0.7
+      '@uppy/store-default': 2.1.1
+      '@uppy/utils': 4.1.3
+      lodash.throttle: 4.1.1
+      mime-match: 1.0.2
+      namespace-emitter: 2.0.1
+      nanoid: 3.3.4
+      preact: 10.11.1
+    dev: false
+
+  /@uppy/store-default/2.1.1:
+    resolution: {integrity: sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==}
+    dev: false
+
+  /@uppy/utils/4.1.3:
+    resolution: {integrity: sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==}
+    dependencies:
+      lodash.throttle: 4.1.1
+    dev: false
+
+  /@uppy/xhr-upload/2.1.3_@uppy+core@2.3.4:
+    resolution: {integrity: sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==}
+    peerDependencies:
+      '@uppy/core': ^2.3.3
+    dependencies:
+      '@uppy/companion-client': 2.2.2
+      '@uppy/core': 2.3.4
+      '@uppy/utils': 4.1.3
+      nanoid: 3.3.4
+    dev: false
+
+  /@vitejs/plugin-vue/3.1.2_vite@3.1.7+vue@3.2.40:
+    resolution: {integrity: sha512-3zxKNlvA3oNaKDYX0NBclgxTQ1xaFdL7PzwF6zj9tGFziKwmBa3Q/6XcJQxudlT81WxDjEhHmevvIC4Orc1LhQ==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      vite: ^3.0.0
+      vue: ^3.2.25
+    dependencies:
+      vite: 3.1.7_sass@1.55.0
+      vue: 3.2.40
+    dev: true
+
+  /@vue/compiler-core/3.2.40:
+    resolution: {integrity: sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==}
+    dependencies:
+      '@babel/parser': 7.19.4
+      '@vue/shared': 3.2.40
+      estree-walker: 2.0.2
+      source-map: 0.6.1
+
+  /@vue/compiler-dom/3.2.40:
+    resolution: {integrity: sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==}
+    dependencies:
+      '@vue/compiler-core': 3.2.40
+      '@vue/shared': 3.2.40
+
+  /@vue/compiler-sfc/3.2.40:
+    resolution: {integrity: sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==}
+    dependencies:
+      '@babel/parser': 7.19.4
+      '@vue/compiler-core': 3.2.40
+      '@vue/compiler-dom': 3.2.40
+      '@vue/compiler-ssr': 3.2.40
+      '@vue/reactivity-transform': 3.2.40
+      '@vue/shared': 3.2.40
+      estree-walker: 2.0.2
+      magic-string: 0.25.9
+      postcss: 8.4.17
+      source-map: 0.6.1
+
+  /@vue/compiler-ssr/3.2.40:
+    resolution: {integrity: sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==}
+    dependencies:
+      '@vue/compiler-dom': 3.2.40
+      '@vue/shared': 3.2.40
+
+  /@vue/devtools-api/6.4.4:
+    resolution: {integrity: sha512-Ku31WzpOV/8cruFaXaEZKF81WkNnvCSlBY4eOGtz5WMSdJvX1v1WWlSMGZeqUwPtQ27ZZz7B62erEMq8JDjcXw==}
+    dev: false
+
+  /@vue/reactivity-transform/3.2.40:
+    resolution: {integrity: sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==}
+    dependencies:
+      '@babel/parser': 7.19.4
+      '@vue/compiler-core': 3.2.40
+      '@vue/shared': 3.2.40
+      estree-walker: 2.0.2
+      magic-string: 0.25.9
+
+  /@vue/reactivity/3.2.40:
+    resolution: {integrity: sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==}
+    dependencies:
+      '@vue/shared': 3.2.40
+
+  /@vue/runtime-core/3.2.40:
+    resolution: {integrity: sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==}
+    dependencies:
+      '@vue/reactivity': 3.2.40
+      '@vue/shared': 3.2.40
+
+  /@vue/runtime-dom/3.2.40:
+    resolution: {integrity: sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==}
+    dependencies:
+      '@vue/runtime-core': 3.2.40
+      '@vue/shared': 3.2.40
+      csstype: 2.6.21
+
+  /@vue/server-renderer/3.2.40_vue@3.2.40:
+    resolution: {integrity: sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==}
+    peerDependencies:
+      vue: 3.2.40
+    dependencies:
+      '@vue/compiler-ssr': 3.2.40
+      '@vue/shared': 3.2.40
+      vue: 3.2.40
+
+  /@vue/shared/3.2.40:
+    resolution: {integrity: sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==}
+
+  /@vueuse/core/9.3.0_vue@3.2.40:
+    resolution: {integrity: sha512-64Rna8IQDWpdrJxgitDg7yv1yTp41ZmvV8zlLEylK4QQLWAhz1OFGZDPZ8bU4lwcGgbEJ2sGi2jrdNh4LttUSQ==}
+    dependencies:
+      '@types/web-bluetooth': 0.0.15
+      '@vueuse/metadata': 9.3.0
+      '@vueuse/shared': 9.3.0_vue@3.2.40
+      vue-demi: 0.13.11_vue@3.2.40
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+    dev: false
+
+  /@vueuse/metadata/9.3.0:
+    resolution: {integrity: sha512-GnnfjbzIPJIh9ngL9s9oGU1+Hx/h5/KFqTfJykzh/1xjaHkedV9g0MASpdmPZIP+ynNhKAcEfA6g5i8KXwtoMA==}
+    dev: false
+
+  /@vueuse/shared/9.3.0_vue@3.2.40:
+    resolution: {integrity: sha512-caGUWLY0DpPC6l31KxeUy6vPVNA0yKxx81jFYLoMpyP6cF84FG5Dkf69DfSUqL57wX8JcUkJDMnQaQIZPWFEQQ==}
+    dependencies:
+      vue-demi: 0.13.11_vue@3.2.40
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+    dev: false
+
+  /@wangeditor/basic-modules/1.1.6_r2od6lkt4jeumi2rq32wnno7pu:
+    resolution: {integrity: sha512-wckcFm/kEAHpTn7dTmN0+7POFoygqt9bZdNHJUkdKObXtAerml8RdjrkHRcwJFCkSELbrNK63fvkwS0+FsabfA==}
+    peerDependencies:
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      lodash.throttle: ^4.1.1
+      nanoid: ^3.2.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+    dependencies:
+      '@wangeditor/core': 1.1.17_qokc4m5r26t2nkvzejrgzroa7e
+      dom7: 3.0.0
+      is-url: 1.2.4
+      lodash.throttle: 4.1.1
+      nanoid: 3.3.4
+      slate: 0.72.8
+      snabbdom: 3.5.1
+    dev: false
+
+  /@wangeditor/code-highlight/1.0.3_bhxad6qx4irfgcgbuqicoe77z4:
+    resolution: {integrity: sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==}
+    peerDependencies:
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+    dependencies:
+      '@wangeditor/core': 1.1.17_qokc4m5r26t2nkvzejrgzroa7e
+      dom7: 3.0.0
+      prismjs: 1.29.0
+      slate: 0.72.8
+      snabbdom: 3.5.1
+    dev: false
+
+  /@wangeditor/core/1.1.17_qokc4m5r26t2nkvzejrgzroa7e:
+    resolution: {integrity: sha512-u0jThy5qrpZo969LNykhb2fpHxEO6p3eKIeouZVnWpA2k9WjbOwtG35bmsUSQhjI5eb316t0ijJhaO4aDfwpuw==}
+    peerDependencies:
+      '@uppy/core': ^2.1.1
+      '@uppy/xhr-upload': ^2.0.3
+      dom7: ^3.0.0
+      is-hotkey: ^0.2.0
+      lodash.camelcase: ^4.3.0
+      lodash.clonedeep: ^4.5.0
+      lodash.debounce: ^4.0.8
+      lodash.foreach: ^4.5.0
+      lodash.isequal: ^4.5.0
+      lodash.throttle: ^4.1.1
+      lodash.toarray: ^4.4.0
+      nanoid: ^3.2.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+    dependencies:
+      '@types/event-emitter': 0.3.3
+      '@uppy/core': 2.3.4
+      '@uppy/xhr-upload': 2.1.3_@uppy+core@2.3.4
+      dom7: 3.0.0
+      event-emitter: 0.3.5
+      html-void-elements: 2.0.1
+      i18next: 20.6.1
+      is-hotkey: 0.2.0
+      lodash.camelcase: 4.3.0
+      lodash.clonedeep: 4.5.0
+      lodash.debounce: 4.0.8
+      lodash.foreach: 4.5.0
+      lodash.isequal: 4.5.0
+      lodash.throttle: 4.1.1
+      lodash.toarray: 4.4.0
+      nanoid: 3.3.4
+      scroll-into-view-if-needed: 2.2.29
+      slate: 0.72.8
+      slate-history: 0.66.0_slate@0.72.8
+      snabbdom: 3.5.1
+    dev: false
+
+  /@wangeditor/editor/5.1.21:
+    resolution: {integrity: sha512-fGMWXA3w/i72lRFvScQ/CaEiWE5nGlAzej6R33vOhJCArQZk8EMgCVEGh+jiqKA900NeXDTXRW+4mfuYjGv1ow==}
+    dependencies:
+      '@uppy/core': 2.3.4
+      '@uppy/xhr-upload': 2.1.3_@uppy+core@2.3.4
+      '@wangeditor/basic-modules': 1.1.6_r2od6lkt4jeumi2rq32wnno7pu
+      '@wangeditor/code-highlight': 1.0.3_bhxad6qx4irfgcgbuqicoe77z4
+      '@wangeditor/core': 1.1.17_qokc4m5r26t2nkvzejrgzroa7e
+      '@wangeditor/list-module': 1.0.5_bhxad6qx4irfgcgbuqicoe77z4
+      '@wangeditor/table-module': 1.1.4_kjpiouw2pn7pcp6kyhk2ialgaa
+      '@wangeditor/upload-image-module': 1.0.2_gnwmms55wok3k7ver5osxhqrn4
+      '@wangeditor/video-module': 1.1.4_hshvde7ra5hzmg3cnxpopqyu5e
+      dom7: 3.0.0
+      is-hotkey: 0.2.0
+      lodash.camelcase: 4.3.0
+      lodash.clonedeep: 4.5.0
+      lodash.debounce: 4.0.8
+      lodash.foreach: 4.5.0
+      lodash.isequal: 4.5.0
+      lodash.throttle: 4.1.1
+      lodash.toarray: 4.4.0
+      nanoid: 3.3.4
+      slate: 0.72.8
+      snabbdom: 3.5.1
+    dev: false
+
+  /@wangeditor/list-module/1.0.5_bhxad6qx4irfgcgbuqicoe77z4:
+    resolution: {integrity: sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==}
+    peerDependencies:
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+    dependencies:
+      '@wangeditor/core': 1.1.17_qokc4m5r26t2nkvzejrgzroa7e
+      dom7: 3.0.0
+      slate: 0.72.8
+      snabbdom: 3.5.1
+    dev: false
+
+  /@wangeditor/table-module/1.1.4_kjpiouw2pn7pcp6kyhk2ialgaa:
+    resolution: {integrity: sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==}
+    peerDependencies:
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      lodash.isequal: ^4.5.0
+      lodash.throttle: ^4.1.1
+      nanoid: ^3.2.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+    dependencies:
+      '@wangeditor/core': 1.1.17_qokc4m5r26t2nkvzejrgzroa7e
+      dom7: 3.0.0
+      lodash.isequal: 4.5.0
+      lodash.throttle: 4.1.1
+      nanoid: 3.3.4
+      slate: 0.72.8
+      snabbdom: 3.5.1
+    dev: false
+
+  /@wangeditor/upload-image-module/1.0.2_gnwmms55wok3k7ver5osxhqrn4:
+    resolution: {integrity: sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==}
+    peerDependencies:
+      '@uppy/core': ^2.0.3
+      '@uppy/xhr-upload': ^2.0.3
+      '@wangeditor/basic-modules': 1.x
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      lodash.foreach: ^4.5.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+    dependencies:
+      '@uppy/core': 2.3.4
+      '@uppy/xhr-upload': 2.1.3_@uppy+core@2.3.4
+      '@wangeditor/basic-modules': 1.1.6_r2od6lkt4jeumi2rq32wnno7pu
+      '@wangeditor/core': 1.1.17_qokc4m5r26t2nkvzejrgzroa7e
+      dom7: 3.0.0
+      lodash.foreach: 4.5.0
+      slate: 0.72.8
+      snabbdom: 3.5.1
+    dev: false
+
+  /@wangeditor/video-module/1.1.4_hshvde7ra5hzmg3cnxpopqyu5e:
+    resolution: {integrity: sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==}
+    peerDependencies:
+      '@uppy/core': ^2.1.4
+      '@uppy/xhr-upload': ^2.0.7
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      nanoid: ^3.2.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+    dependencies:
+      '@uppy/core': 2.3.4
+      '@uppy/xhr-upload': 2.1.3_@uppy+core@2.3.4
+      '@wangeditor/core': 1.1.17_qokc4m5r26t2nkvzejrgzroa7e
+      dom7: 3.0.0
+      nanoid: 3.3.4
+      slate: 0.72.8
+      snabbdom: 3.5.1
+    dev: false
+
+  /acorn-jsx/5.3.2_acorn@8.8.0:
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+    dependencies:
+      acorn: 8.8.0
+    dev: true
+
+  /acorn/8.8.0:
+    resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+    dev: true
+
+  /ajv/6.12.6:
+    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+    dependencies:
+      fast-deep-equal: 3.1.3
+      fast-json-stable-stringify: 2.1.0
+      json-schema-traverse: 0.4.1
+      uri-js: 4.4.1
+    dev: true
+
+  /ansi-regex/5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /ansi-styles/4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      color-convert: 2.0.1
+    dev: true
+
+  /anymatch/3.1.2:
+    resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.1
+    dev: true
+
+  /argparse/2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+    dev: true
+
+  /array-union/2.1.0:
+    resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /async-validator/4.2.5:
+    resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
+    dev: false
+
+  /asynckit/0.4.0:
+    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+    dev: false
+
+  /axios/0.27.2:
+    resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
+    dependencies:
+      follow-redirects: 1.15.2
+      form-data: 4.0.0
+    transitivePeerDependencies:
+      - debug
+    dev: false
+
+  /balanced-match/1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+    dev: true
+
+  /batch-processor/1.0.0:
+    resolution: {integrity: sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==}
+    dev: false
+
+  /binary-extensions/2.2.0:
+    resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /boolbase/1.0.0:
+    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+    dev: true
+
+  /brace-expansion/1.1.11:
+    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+    dev: true
+
+  /braces/3.0.2:
+    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+    engines: {node: '>=8'}
+    dependencies:
+      fill-range: 7.0.1
+    dev: true
+
+  /callsites/3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /chalk/4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+    dev: true
+
+  /chokidar/3.5.3:
+    resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+    engines: {node: '>= 8.10.0'}
+    dependencies:
+      anymatch: 3.1.2
+      braces: 3.0.2
+      glob-parent: 5.1.2
+      is-binary-path: 2.1.0
+      is-glob: 4.0.3
+      normalize-path: 3.0.0
+      readdirp: 3.6.0
+    optionalDependencies:
+      fsevents: 2.3.2
+    dev: true
+
+  /claygl/1.3.0:
+    resolution: {integrity: sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==}
+    dev: false
+
+  /clipboard/2.0.11:
+    resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==}
+    dependencies:
+      good-listener: 1.2.2
+      select: 1.1.2
+      tiny-emitter: 2.1.0
+    dev: false
+
+  /color-convert/2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+    dependencies:
+      color-name: 1.1.4
+    dev: true
+
+  /color-name/1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+    dev: true
+
+  /combined-stream/1.0.8:
+    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      delayed-stream: 1.0.0
+    dev: false
+
+  /compute-scroll-into-view/1.0.17:
+    resolution: {integrity: sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==}
+    dev: false
+
+  /concat-map/0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+    dev: true
+
+  /countup.js/2.3.2:
+    resolution: {integrity: sha512-dQ7F/CmKGjaO6cDfhtEXwsKVlXIpJ89dFs8PvkaZH9jBVJ2Z8GU4iwG/qP7MgY8qwr+1skbwR6qecWWQLUzB8Q==}
+    dev: false
+
+  /cropperjs/1.5.12:
+    resolution: {integrity: sha512-re7UdjE5UnwdrovyhNzZ6gathI4Rs3KGCBSc8HCIjUo5hO42CtzyblmWLj6QWVw7huHyDMfpKxhiO2II77nhDw==}
+    dev: false
+
+  /cross-spawn/7.0.3:
+    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+    engines: {node: '>= 8'}
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+    dev: true
+
+  /cssesc/3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
+  /csstype/2.6.21:
+    resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
+
+  /d/1.0.1:
+    resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==}
+    dependencies:
+      es5-ext: 0.10.62
+      type: 1.2.0
+    dev: false
+
+  /dayjs/1.11.5:
+    resolution: {integrity: sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==}
+    dev: false
+
+  /debug/4.3.4:
+    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.2
+    dev: true
+
+  /deep-is/0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+    dev: true
+
+  /delayed-stream/1.0.0:
+    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+    engines: {node: '>=0.4.0'}
+    dev: false
+
+  /delegate/3.2.0:
+    resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==}
+    dev: false
+
+  /dir-glob/3.0.1:
+    resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+    engines: {node: '>=8'}
+    dependencies:
+      path-type: 4.0.0
+    dev: true
+
+  /doctrine/3.0.0:
+    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      esutils: 2.0.3
+    dev: true
+
+  /dom7/3.0.0:
+    resolution: {integrity: sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==}
+    dependencies:
+      ssr-window: 3.0.0
+    dev: false
+
+  /dotenv/16.0.3:
+    resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /echarts-gl/2.0.9_echarts@5.4.0:
+    resolution: {integrity: sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==}
+    peerDependencies:
+      echarts: ^5.1.2
+    dependencies:
+      claygl: 1.3.0
+      echarts: 5.4.0
+      zrender: 5.4.0
+    dev: false
+
+  /echarts-wordcloud/2.0.0_echarts@5.4.0:
+    resolution: {integrity: sha512-K7l6pTklqdW7ZWzT/1CS0KhBSINr/cd7c5N1fVMzZMwLQHEwT7x+nivK7g5hkVh7WNcAv4Dn6/ZS5zMKRozC1g==}
+    peerDependencies:
+      echarts: ^5.0.1
+    dependencies:
+      echarts: 5.4.0
+    dev: false
+
+  /echarts/5.4.0:
+    resolution: {integrity: sha512-uPsO9VRUIKAdFOoH3B0aNg7NRVdN7aM39/OjovjO9MwmWsAkfGyeXJhK+dbRi51iDrQWliXV60/XwLA7kg3z0w==}
+    dependencies:
+      tslib: 2.3.0
+      zrender: 5.4.0
+    dev: false
+
+  /element-plus/2.2.17_vue@3.2.40:
+    resolution: {integrity: sha512-MGwMIE/q+FFD3kgS23x8HIe5043tmD1cTRwjhIX9o6fim1avFnUkrsfYRvybbz4CkyqSb185EheZS5AUPpXh2g==}
+    peerDependencies:
+      vue: ^3.2.0
+    dependencies:
+      '@ctrl/tinycolor': 3.4.1
+      '@element-plus/icons-vue': 2.0.10_vue@3.2.40
+      '@floating-ui/dom': 1.0.2
+      '@popperjs/core': /@sxzz/popperjs-es/2.11.7
+      '@types/lodash': 4.14.186
+      '@types/lodash-es': 4.17.6
+      '@vueuse/core': 9.3.0_vue@3.2.40
+      async-validator: 4.2.5
+      dayjs: 1.11.5
+      escape-html: 1.0.3
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+      lodash-unified: 1.0.2_3ib2ivapxullxkx3xftsimdk7u
+      memoize-one: 6.0.0
+      normalize-wheel-es: 1.2.0
+      vue: 3.2.40
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+    dev: false
+
+  /element-resize-detector/1.2.4:
+    resolution: {integrity: sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==}
+    dependencies:
+      batch-processor: 1.0.0
+    dev: false
+
+  /es5-ext/0.10.62:
+    resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==}
+    engines: {node: '>=0.10'}
+    requiresBuild: true
+    dependencies:
+      es6-iterator: 2.0.3
+      es6-symbol: 3.1.3
+      next-tick: 1.1.0
+    dev: false
+
+  /es6-iterator/2.0.3:
+    resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==}
+    dependencies:
+      d: 1.0.1
+      es5-ext: 0.10.62
+      es6-symbol: 3.1.3
+    dev: false
+
+  /es6-symbol/3.1.3:
+    resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==}
+    dependencies:
+      d: 1.0.1
+      ext: 1.7.0
+    dev: false
+
+  /esbuild-android-64/0.15.10:
+    resolution: {integrity: sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-android-arm64/0.15.10:
+    resolution: {integrity: sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-darwin-64/0.15.10:
+    resolution: {integrity: sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-darwin-arm64/0.15.10:
+    resolution: {integrity: sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-freebsd-64/0.15.10:
+    resolution: {integrity: sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-freebsd-arm64/0.15.10:
+    resolution: {integrity: sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-linux-32/0.15.10:
+    resolution: {integrity: sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-linux-64/0.15.10:
+    resolution: {integrity: sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-linux-arm/0.15.10:
+    resolution: {integrity: sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-linux-arm64/0.15.10:
+    resolution: {integrity: sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-linux-mips64le/0.15.10:
+    resolution: {integrity: sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-linux-ppc64le/0.15.10:
+    resolution: {integrity: sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-linux-riscv64/0.15.10:
+    resolution: {integrity: sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-linux-s390x/0.15.10:
+    resolution: {integrity: sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-netbsd-64/0.15.10:
+    resolution: {integrity: sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-openbsd-64/0.15.10:
+    resolution: {integrity: sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-sunos-64/0.15.10:
+    resolution: {integrity: sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-windows-32/0.15.10:
+    resolution: {integrity: sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-windows-64/0.15.10:
+    resolution: {integrity: sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild-windows-arm64/0.15.10:
+    resolution: {integrity: sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /esbuild/0.15.10:
+    resolution: {integrity: sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    optionalDependencies:
+      '@esbuild/android-arm': 0.15.10
+      '@esbuild/linux-loong64': 0.15.10
+      esbuild-android-64: 0.15.10
+      esbuild-android-arm64: 0.15.10
+      esbuild-darwin-64: 0.15.10
+      esbuild-darwin-arm64: 0.15.10
+      esbuild-freebsd-64: 0.15.10
+      esbuild-freebsd-arm64: 0.15.10
+      esbuild-linux-32: 0.15.10
+      esbuild-linux-64: 0.15.10
+      esbuild-linux-arm: 0.15.10
+      esbuild-linux-arm64: 0.15.10
+      esbuild-linux-mips64le: 0.15.10
+      esbuild-linux-ppc64le: 0.15.10
+      esbuild-linux-riscv64: 0.15.10
+      esbuild-linux-s390x: 0.15.10
+      esbuild-netbsd-64: 0.15.10
+      esbuild-openbsd-64: 0.15.10
+      esbuild-sunos-64: 0.15.10
+      esbuild-windows-32: 0.15.10
+      esbuild-windows-64: 0.15.10
+      esbuild-windows-arm64: 0.15.10
+    dev: true
+
+  /escape-html/1.0.3:
+    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+    dev: false
+
+  /escape-string-regexp/4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /eslint-plugin-vue/9.6.0_eslint@8.25.0:
+    resolution: {integrity: sha512-zzySkJgVbFCylnG2+9MDF7N+2Rjze2y0bF8GyUNpFOnT8mCMfqqtLDJkHBuYu9N/psW1A6DVbQhPkP92E+qakA==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
+    dependencies:
+      eslint: 8.25.0
+      eslint-utils: 3.0.0_eslint@8.25.0
+      natural-compare: 1.4.0
+      nth-check: 2.1.1
+      postcss-selector-parser: 6.0.10
+      semver: 7.3.8
+      vue-eslint-parser: 9.1.0_eslint@8.25.0
+      xml-name-validator: 4.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /eslint-scope/5.1.1:
+    resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
+    engines: {node: '>=8.0.0'}
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 4.3.0
+    dev: true
+
+  /eslint-scope/7.1.1:
+    resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+    dev: true
+
+  /eslint-utils/3.0.0_eslint@8.25.0:
+    resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
+    engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
+    peerDependencies:
+      eslint: '>=5'
+    dependencies:
+      eslint: 8.25.0
+      eslint-visitor-keys: 2.1.0
+    dev: true
+
+  /eslint-visitor-keys/2.1.0:
+    resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /eslint-visitor-keys/3.3.0:
+    resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dev: true
+
+  /eslint/8.25.0:
+    resolution: {integrity: sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    hasBin: true
+    dependencies:
+      '@eslint/eslintrc': 1.3.3
+      '@humanwhocodes/config-array': 0.10.7
+      '@humanwhocodes/module-importer': 1.0.1
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.3
+      debug: 4.3.4
+      doctrine: 3.0.0
+      escape-string-regexp: 4.0.0
+      eslint-scope: 7.1.1
+      eslint-utils: 3.0.0_eslint@8.25.0
+      eslint-visitor-keys: 3.3.0
+      espree: 9.4.0
+      esquery: 1.4.0
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 6.0.1
+      find-up: 5.0.0
+      glob-parent: 6.0.2
+      globals: 13.17.0
+      globby: 11.1.0
+      grapheme-splitter: 1.0.4
+      ignore: 5.2.0
+      import-fresh: 3.3.0
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      js-sdsl: 4.1.5
+      js-yaml: 4.1.0
+      json-stable-stringify-without-jsonify: 1.0.1
+      levn: 0.4.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.1
+      regexpp: 3.2.0
+      strip-ansi: 6.0.1
+      strip-json-comments: 3.1.1
+      text-table: 0.2.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /espree/9.4.0:
+    resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      acorn: 8.8.0
+      acorn-jsx: 5.3.2_acorn@8.8.0
+      eslint-visitor-keys: 3.3.0
+    dev: true
+
+  /esquery/1.4.0:
+    resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
+    engines: {node: '>=0.10'}
+    dependencies:
+      estraverse: 5.3.0
+    dev: true
+
+  /esrecurse/4.3.0:
+    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+    engines: {node: '>=4.0'}
+    dependencies:
+      estraverse: 5.3.0
+    dev: true
+
+  /estraverse/4.3.0:
+    resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
+    engines: {node: '>=4.0'}
+    dev: true
+
+  /estraverse/5.3.0:
+    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+    engines: {node: '>=4.0'}
+    dev: true
+
+  /estree-walker/2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  /esutils/2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /event-emitter/0.3.5:
+    resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
+    dependencies:
+      d: 1.0.1
+      es5-ext: 0.10.62
+    dev: false
+
+  /ext/1.7.0:
+    resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
+    dependencies:
+      type: 2.7.2
+    dev: false
+
+  /fast-deep-equal/3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+    dev: true
+
+  /fast-glob/3.2.12:
+    resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}
+    engines: {node: '>=8.6.0'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      '@nodelib/fs.walk': 1.2.8
+      glob-parent: 5.1.2
+      merge2: 1.4.1
+      micromatch: 4.0.5
+    dev: true
+
+  /fast-json-stable-stringify/2.1.0:
+    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+    dev: true
+
+  /fast-levenshtein/2.0.6:
+    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+    dev: true
+
+  /fastq/1.13.0:
+    resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==}
+    dependencies:
+      reusify: 1.0.4
+    dev: true
+
+  /file-entry-cache/6.0.1:
+    resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    dependencies:
+      flat-cache: 3.0.4
+    dev: true
+
+  /fill-range/7.0.1:
+    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      to-regex-range: 5.0.1
+    dev: true
+
+  /find-up/5.0.0:
+    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+    engines: {node: '>=10'}
+    dependencies:
+      locate-path: 6.0.0
+      path-exists: 4.0.0
+    dev: true
+
+  /flat-cache/3.0.4:
+    resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    dependencies:
+      flatted: 3.2.7
+      rimraf: 3.0.2
+    dev: true
+
+  /flatted/3.2.7:
+    resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
+    dev: true
+
+  /follow-redirects/1.15.2:
+    resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+    dev: false
+
+  /form-data/4.0.0:
+    resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
+    engines: {node: '>= 6'}
+    dependencies:
+      asynckit: 0.4.0
+      combined-stream: 1.0.8
+      mime-types: 2.1.35
+    dev: false
+
+  /fs.realpath/1.0.0:
+    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+    dev: true
+
+  /fsevents/2.3.2:
+    resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /function-bind/1.1.1:
+    resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+    dev: true
+
+  /glob-parent/5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+    dependencies:
+      is-glob: 4.0.3
+    dev: true
+
+  /glob-parent/6.0.2:
+    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+    engines: {node: '>=10.13.0'}
+    dependencies:
+      is-glob: 4.0.3
+    dev: true
+
+  /glob/7.2.3:
+    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+    dependencies:
+      fs.realpath: 1.0.0
+      inflight: 1.0.6
+      inherits: 2.0.4
+      minimatch: 3.1.2
+      once: 1.4.0
+      path-is-absolute: 1.0.1
+    dev: true
+
+  /globals/13.17.0:
+    resolution: {integrity: sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==}
+    engines: {node: '>=8'}
+    dependencies:
+      type-fest: 0.20.2
+    dev: true
+
+  /globby/11.1.0:
+    resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
+    engines: {node: '>=10'}
+    dependencies:
+      array-union: 2.1.0
+      dir-glob: 3.0.1
+      fast-glob: 3.2.12
+      ignore: 5.2.0
+      merge2: 1.4.1
+      slash: 3.0.0
+    dev: true
+
+  /good-listener/1.2.2:
+    resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==}
+    dependencies:
+      delegate: 3.2.0
+    dev: false
+
+  /grapheme-splitter/1.0.4:
+    resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
+    dev: true
+
+  /has-flag/4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /has/1.0.3:
+    resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+    engines: {node: '>= 0.4.0'}
+    dependencies:
+      function-bind: 1.1.1
+    dev: true
+
+  /html-void-elements/2.0.1:
+    resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==}
+    dev: false
+
+  /i18next/20.6.1:
+    resolution: {integrity: sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==}
+    dependencies:
+      '@babel/runtime': 7.19.4
+    dev: false
+
+  /ignore/5.2.0:
+    resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==}
+    engines: {node: '>= 4'}
+    dev: true
+
+  /immer/9.0.15:
+    resolution: {integrity: sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==}
+    dev: false
+
+  /immutable/4.1.0:
+    resolution: {integrity: sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==}
+    dev: true
+
+  /import-fresh/3.3.0:
+    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+    engines: {node: '>=6'}
+    dependencies:
+      parent-module: 1.0.1
+      resolve-from: 4.0.0
+    dev: true
+
+  /imurmurhash/0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+    dev: true
+
+  /inflight/1.0.6:
+    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+    dependencies:
+      once: 1.4.0
+      wrappy: 1.0.2
+    dev: true
+
+  /inherits/2.0.4:
+    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+    dev: true
+
+  /is-binary-path/2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+    dependencies:
+      binary-extensions: 2.2.0
+    dev: true
+
+  /is-core-module/2.10.0:
+    resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==}
+    dependencies:
+      has: 1.0.3
+    dev: true
+
+  /is-extglob/2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /is-glob/4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-extglob: 2.1.1
+    dev: true
+
+  /is-hotkey/0.2.0:
+    resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==}
+    dev: false
+
+  /is-number/7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+    dev: true
+
+  /is-plain-object/5.0.0:
+    resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
+    engines: {node: '>=0.10.0'}
+    dev: false
+
+  /is-url/1.2.4:
+    resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==}
+    dev: false
+
+  /isexe/2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+    dev: true
+
+  /js-cookie/3.0.1:
+    resolution: {integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==}
+    engines: {node: '>=12'}
+    dev: false
+
+  /js-sdsl/4.1.5:
+    resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==}
+    dev: true
+
+  /js-yaml/4.1.0:
+    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+    hasBin: true
+    dependencies:
+      argparse: 2.0.1
+    dev: true
+
+  /json-schema-traverse/0.4.1:
+    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+    dev: true
+
+  /json-stable-stringify-without-jsonify/1.0.1:
+    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+    dev: true
+
+  /jsplumb/2.15.6:
+    resolution: {integrity: sha512-sIpbpz5eMVM+vV+MQzFCidlaa1RsknrQs6LOTKYDjYUDdTAi2AN2bFi94TxB33TifcIsRNV1jebcaxg0tCoPzg==}
+    dev: false
+
+  /klona/2.0.5:
+    resolution: {integrity: sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /levn/0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+    dev: true
+
+  /locate-path/6.0.0:
+    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+    engines: {node: '>=10'}
+    dependencies:
+      p-locate: 5.0.0
+    dev: true
+
+  /lodash-es/4.17.21:
+    resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+    dev: false
+
+  /lodash-unified/1.0.2_3ib2ivapxullxkx3xftsimdk7u:
+    resolution: {integrity: sha512-OGbEy+1P+UT26CYi4opY4gebD8cWRDxAT6MAObIVQMiqYdxZr1g3QHWCToVsm31x2NkLS4K3+MC2qInaRMa39g==}
+    peerDependencies:
+      '@types/lodash-es': '*'
+      lodash: '*'
+      lodash-es: '*'
+    dependencies:
+      '@types/lodash-es': 4.17.6
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+    dev: false
+
+  /lodash.camelcase/4.3.0:
+    resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+    dev: false
+
+  /lodash.clonedeep/4.5.0:
+    resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==}
+    dev: false
+
+  /lodash.debounce/4.0.8:
+    resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+    dev: false
+
+  /lodash.foreach/4.5.0:
+    resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==}
+    dev: false
+
+  /lodash.isequal/4.5.0:
+    resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
+    dev: false
+
+  /lodash.merge/4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+    dev: true
+
+  /lodash.throttle/4.1.1:
+    resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
+    dev: false
+
+  /lodash.toarray/4.4.0:
+    resolution: {integrity: sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==}
+    dev: false
+
+  /lodash/4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+  /lru-cache/6.0.0:
+    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+    engines: {node: '>=10'}
+    dependencies:
+      yallist: 4.0.0
+    dev: true
+
+  /magic-string/0.25.9:
+    resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
+    dependencies:
+      sourcemap-codec: 1.4.8
+
+  /memoize-one/6.0.0:
+    resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
+    dev: false
+
+  /merge2/1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /micromatch/4.0.5:
+    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+    engines: {node: '>=8.6'}
+    dependencies:
+      braces: 3.0.2
+      picomatch: 2.3.1
+    dev: true
+
+  /mime-db/1.52.0:
+    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+    engines: {node: '>= 0.6'}
+    dev: false
+
+  /mime-match/1.0.2:
+    resolution: {integrity: sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==}
+    dependencies:
+      wildcard: 1.1.2
+    dev: false
+
+  /mime-types/2.1.35:
+    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+    engines: {node: '>= 0.6'}
+    dependencies:
+      mime-db: 1.52.0
+    dev: false
+
+  /minimatch/3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+    dependencies:
+      brace-expansion: 1.1.11
+    dev: true
+
+  /mitt/2.1.0:
+    resolution: {integrity: sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==}
+    dev: false
+
+  /mitt/3.0.0:
+    resolution: {integrity: sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==}
+    dev: false
+
+  /ms/2.1.2:
+    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+    dev: true
+
+  /namespace-emitter/2.0.1:
+    resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==}
+    dev: false
+
+  /nanoid/3.3.4:
+    resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  /natural-compare/1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+    dev: true
+
+  /neo-async/2.6.2:
+    resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+    dev: true
+
+  /next-tick/1.1.0:
+    resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
+    dev: false
+
+  /normalize-path/3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /normalize-wheel-es/1.2.0:
+    resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==}
+    dev: false
+
+  /nprogress/0.2.0:
+    resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==}
+    dev: false
+
+  /nth-check/2.1.1:
+    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+    dependencies:
+      boolbase: 1.0.0
+    dev: true
+
+  /once/1.4.0:
+    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+    dependencies:
+      wrappy: 1.0.2
+    dev: true
+
+  /optionator/0.9.1:
+    resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.4.1
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+      word-wrap: 1.2.3
+    dev: true
+
+  /p-limit/3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      yocto-queue: 0.1.0
+    dev: true
+
+  /p-locate/5.0.0:
+    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+    engines: {node: '>=10'}
+    dependencies:
+      p-limit: 3.1.0
+    dev: true
+
+  /parent-module/1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+    dependencies:
+      callsites: 3.1.0
+    dev: true
+
+  /path-exists/4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-is-absolute/1.0.1:
+    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /path-key/3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-parse/1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+    dev: true
+
+  /path-type/4.0.0:
+    resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /picocolors/1.0.0:
+    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+
+  /picomatch/2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+    dev: true
+
+  /pinia/2.0.23_bfjwoga25wxjazzogo7o372nwq:
+    resolution: {integrity: sha512-N15hFf4o5STrxpNrib1IEb1GOArvPYf1zPvQVRGOO1G1d74Ak0J0lVyalX/SmrzdT4Q0nlEFjbURsmBmIGUR5Q==}
+    peerDependencies:
+      '@vue/composition-api': ^1.4.0
+      typescript: '>=4.4.4'
+      vue: ^2.6.14 || ^3.2.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+      typescript:
+        optional: true
+    dependencies:
+      '@vue/devtools-api': 6.4.4
+      typescript: 4.8.4
+      vue: 3.2.40
+      vue-demi: 0.13.11_vue@3.2.40
+    dev: false
+
+  /postcss-selector-parser/6.0.10:
+    resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
+    engines: {node: '>=4'}
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+    dev: true
+
+  /postcss/8.4.17:
+    resolution: {integrity: sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==}
+    engines: {node: ^10 || ^12 || >=14}
+    dependencies:
+      nanoid: 3.3.4
+      picocolors: 1.0.0
+      source-map-js: 1.0.2
+
+  /preact/10.11.1:
+    resolution: {integrity: sha512-1Wz5PCRm6Fg+6BTXWJHhX4wRK9MZbZBHuwBqfZlOdVm2NqPe8/rjYpufvYCwJSGb9layyzB2jTTXfpCTynLqFQ==}
+    dev: false
+
+  /prelude-ls/1.2.1:
+    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+    engines: {node: '>= 0.8.0'}
+    dev: true
+
+  /prettier/2.7.1:
+    resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    dev: true
+
+  /print-js/1.6.0:
+    resolution: {integrity: sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg==}
+    dev: false
+
+  /prismjs/1.29.0:
+    resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
+    engines: {node: '>=6'}
+    dev: false
+
+  /punycode/2.1.1:
+    resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /qrcodejs2-fixes/0.0.2:
+    resolution: {integrity: sha512-wMUXYMOixAEJlLnjk5MbLiFaz0gQObWYm/TIFWB5+j7sTY5gPyr09Cx1EpcLYbsgfFdN3wHjrKAhZofTuCBGhg==}
+    dev: false
+
+  /queue-microtask/1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+    dev: true
+
+  /readdirp/3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+    dependencies:
+      picomatch: 2.3.1
+    dev: true
+
+  /regenerator-runtime/0.13.9:
+    resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==}
+    dev: false
+
+  /regexpp/3.2.0:
+    resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /resolve-from/4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /resolve/1.22.1:
+    resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
+    hasBin: true
+    dependencies:
+      is-core-module: 2.10.0
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+    dev: true
+
+  /reusify/1.0.4:
+    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+    dev: true
+
+  /rimraf/3.0.2:
+    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+    hasBin: true
+    dependencies:
+      glob: 7.2.3
+    dev: true
+
+  /rollup/2.78.1:
+    resolution: {integrity: sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==}
+    engines: {node: '>=10.0.0'}
+    hasBin: true
+    optionalDependencies:
+      fsevents: 2.3.2
+    dev: true
+
+  /run-parallel/1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+    dependencies:
+      queue-microtask: 1.2.3
+    dev: true
+
+  /sass-loader/13.1.0_sass@1.55.0:
+    resolution: {integrity: sha512-tZS1RJQ2n2+QNyf3CCAo1H562WjL/5AM6Gi8YcPVVoNxQX8d19mx8E+8fRrMWsyc93ZL6Q8vZDSM0FHVTJaVnQ==}
+    engines: {node: '>= 14.15.0'}
+    peerDependencies:
+      fibers: '>= 3.1.0'
+      node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+      sass: ^1.3.0
+      sass-embedded: '*'
+      webpack: ^5.0.0
+    peerDependenciesMeta:
+      fibers:
+        optional: true
+      node-sass:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+    dependencies:
+      klona: 2.0.5
+      neo-async: 2.6.2
+      sass: 1.55.0
+    dev: true
+
+  /sass/1.55.0:
+    resolution: {integrity: sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A==}
+    engines: {node: '>=12.0.0'}
+    hasBin: true
+    dependencies:
+      chokidar: 3.5.3
+      immutable: 4.1.0
+      source-map-js: 1.0.2
+    dev: true
+
+  /screenfull/6.0.2:
+    resolution: {integrity: sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw==}
+    engines: {node: ^14.13.1 || >=16.0.0}
+    dev: false
+
+  /scroll-into-view-if-needed/2.2.29:
+    resolution: {integrity: sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==}
+    dependencies:
+      compute-scroll-into-view: 1.0.17
+    dev: false
+
+  /select/1.1.2:
+    resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==}
+    dev: false
+
+  /semver/7.3.8:
+    resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      lru-cache: 6.0.0
+    dev: true
+
+  /shebang-command/2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+    dependencies:
+      shebang-regex: 3.0.0
+    dev: true
+
+  /shebang-regex/3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /slash/3.0.0:
+    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /slate-history/0.66.0_slate@0.72.8:
+    resolution: {integrity: sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==}
+    peerDependencies:
+      slate: '>=0.65.3'
+    dependencies:
+      is-plain-object: 5.0.0
+      slate: 0.72.8
+    dev: false
+
+  /slate/0.72.8:
+    resolution: {integrity: sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==}
+    dependencies:
+      immer: 9.0.15
+      is-plain-object: 5.0.0
+      tiny-warning: 1.0.3
+    dev: false
+
+  /snabbdom/3.5.1:
+    resolution: {integrity: sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA==}
+    engines: {node: '>=8.3.0'}
+    dev: false
+
+  /sortablejs/1.15.0:
+    resolution: {integrity: sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==}
+    dev: false
+
+  /source-map-js/1.0.2:
+    resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+    engines: {node: '>=0.10.0'}
+
+  /source-map/0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+
+  /sourcemap-codec/1.4.8:
+    resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
+
+  /splitpanes/3.1.1:
+    resolution: {integrity: sha512-VUkxDJfIGSvTM/fm/+OSrx8ha9URwE/9B8FPvfzoBuAxVELIHBWpsfnJXIXv77zVwuex//QQL4kTU9SDBPeHjA==}
+    dev: false
+
+  /ssr-window/3.0.0:
+    resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==}
+    dev: false
+
+  /strip-ansi/6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-regex: 5.0.1
+    dev: true
+
+  /strip-json-comments/3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /supports-color/7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+    dependencies:
+      has-flag: 4.0.0
+    dev: true
+
+  /supports-preserve-symlinks-flag/1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  /text-table/0.2.0:
+    resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+    dev: true
+
+  /tiny-emitter/2.1.0:
+    resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
+    dev: false
+
+  /tiny-warning/1.0.3:
+    resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
+    dev: false
+
+  /to-fast-properties/2.0.0:
+    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+    engines: {node: '>=4'}
+
+  /to-regex-range/5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+    dependencies:
+      is-number: 7.0.0
+    dev: true
+
+  /tslib/1.14.1:
+    resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
+    dev: true
+
+  /tslib/2.3.0:
+    resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
+    dev: false
+
+  /tsutils/3.21.0_typescript@4.8.4:
+    resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
+    engines: {node: '>= 6'}
+    peerDependencies:
+      typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
+    dependencies:
+      tslib: 1.14.1
+      typescript: 4.8.4
+    dev: true
+
+  /type-check/0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.2.1
+    dev: true
+
+  /type-fest/0.20.2:
+    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /type/1.2.0:
+    resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==}
+    dev: false
+
+  /type/2.7.2:
+    resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==}
+    dev: false
+
+  /typescript/4.8.4:
+    resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==}
+    engines: {node: '>=4.2.0'}
+    hasBin: true
+
+  /uri-js/4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+    dependencies:
+      punycode: 2.1.1
+    dev: true
+
+  /util-deprecate/1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+    dev: true
+
+  /vite/3.1.7_sass@1.55.0:
+    resolution: {integrity: sha512-5vCAmU4S8lyVdFCInu9M54f/g8qbOMakVw5xJ4pjoaDy5wgy9sLLZkGdSLN52dlsBqh0tBqxjaqqa8LgPqwRAA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    hasBin: true
+    peerDependencies:
+      less: '*'
+      sass: '*'
+      stylus: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      less:
+        optional: true
+      sass:
+        optional: true
+      stylus:
+        optional: true
+      terser:
+        optional: true
+    dependencies:
+      esbuild: 0.15.10
+      postcss: 8.4.17
+      resolve: 1.22.1
+      rollup: 2.78.1
+      sass: 1.55.0
+    optionalDependencies:
+      fsevents: 2.3.2
+    dev: true
+
+  /vue-clipboard3/2.0.0:
+    resolution: {integrity: sha512-Q9S7dzWGax7LN5iiSPcu/K1GGm2gcBBlYwmMsUc5/16N6w90cbKow3FnPmPs95sungns4yvd9/+JhbAznECS2A==}
+    dependencies:
+      clipboard: 2.0.11
+    dev: false
+
+  /vue-demi/0.13.11_vue@3.2.40:
+    resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+    dependencies:
+      vue: 3.2.40
+    dev: false
+
+  /vue-eslint-parser/9.1.0_eslint@8.25.0:
+    resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '>=6.0.0'
+    dependencies:
+      debug: 4.3.4
+      eslint: 8.25.0
+      eslint-scope: 7.1.1
+      eslint-visitor-keys: 3.3.0
+      espree: 9.4.0
+      esquery: 1.4.0
+      lodash: 4.17.21
+      semver: 7.3.8
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /vue-grid-layout/3.0.0-beta1:
+    resolution: {integrity: sha512-MsW0yfYNtnAO/uDhfZvkP6effxSJxvhAFbIL37x6Rn3vW9xf0WHVefKaSbQMLpSq3mXnR6ut0pg2Cd5lqIIZzg==}
+    dependencies:
+      '@interactjs/actions': 1.10.17
+      '@interactjs/auto-start': 1.10.17
+      '@interactjs/dev-tools': 1.10.17_cn5slzagijafj3kjb52cs5znym
+      '@interactjs/interactjs': 1.10.17
+      '@interactjs/modifiers': 1.10.17
+      element-resize-detector: 1.2.4
+      mitt: 2.1.0
+    transitivePeerDependencies:
+      - '@interactjs/core'
+      - '@interactjs/utils'
+    dev: false
+
+  /vue-i18n/9.2.2_vue@3.2.40:
+    resolution: {integrity: sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==}
+    engines: {node: '>= 14'}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      '@intlify/core-base': 9.2.2
+      '@intlify/shared': 9.2.2
+      '@intlify/vue-devtools': 9.2.2
+      '@vue/devtools-api': 6.4.4
+      vue: 3.2.40
+    dev: false
+
+  /vue-router/4.1.5_vue@3.2.40:
+    resolution: {integrity: sha512-IsvoF5D2GQ/EGTs/Th4NQms9gd2NSqV+yylxIyp/OYp8xOwxmU8Kj/74E9DTSYAyH5LX7idVUngN3JSj1X4xcQ==}
+    peerDependencies:
+      vue: ^3.2.0
+    dependencies:
+      '@vue/devtools-api': 6.4.4
+      vue: 3.2.40
+    dev: false
+
+  /vue/3.2.40:
+    resolution: {integrity: sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==}
+    dependencies:
+      '@vue/compiler-dom': 3.2.40
+      '@vue/compiler-sfc': 3.2.40
+      '@vue/runtime-dom': 3.2.40
+      '@vue/server-renderer': 3.2.40_vue@3.2.40
+      '@vue/shared': 3.2.40
+
+  /which/2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+    dependencies:
+      isexe: 2.0.0
+    dev: true
+
+  /wildcard/1.1.2:
+    resolution: {integrity: sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==}
+    dev: false
+
+  /word-wrap/1.2.3:
+    resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /wrappy/1.0.2:
+    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+    dev: true
+
+  /xml-name-validator/4.0.0:
+    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /yallist/4.0.0:
+    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+    dev: true
+
+  /yocto-queue/0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /zrender/5.4.0:
+    resolution: {integrity: sha512-rOS09Z2HSVGFs2dn/TuYk5BlCaZcVe8UDLLjj1ySYF828LATKKdxuakSZMvrDz54yiKPDYVfjdKqcX8Jky3BIA==}
+    dependencies:
+      tslib: 2.3.0
+    dev: false

+ 175 - 7
Web/src/api-services/apis/sys-file-api.ts

@@ -190,14 +190,60 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
                 options: localVarRequestOptions,
             };
         },
+        /**
+         * 
+         * @summary 上传头像
+         * @param {Blob} [file] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysFileUploadAvatarPostForm: async (file?: Blob, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysFile/uploadAvatar`;
+            // 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;
+            const localVarFormParams = new FormData();
+
+            // authentication Bearer required
+
+
+            if (file !== undefined) { 
+                localVarFormParams.append('file', file as any);
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'multipart/form-data';
+            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};
+            localVarRequestOptions.data = localVarFormParams;
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
         /**
          * 
          * @summary 上传文件
          * @param {Blob} [file] 
+         * @param {string} [path] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        sysFileUploadPostForm: async (file?: Blob, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+        sysFileUploadPostForm: async (file?: Blob, path?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
             const localVarPath = `/sysFile/upload`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
@@ -212,6 +258,55 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
 
             // authentication Bearer required
 
+            if (path !== undefined) {
+                localVarQueryParameter['path'] = path;
+            }
+
+
+            if (file !== undefined) { 
+                localVarFormParams.append('file', file as any);
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'multipart/form-data';
+            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};
+            localVarRequestOptions.data = localVarFormParams;
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 上传电子签名
+         * @param {Blob} [file] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysFileUploadSignaturePostForm: async (file?: Blob, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysFile/uploadSignature`;
+            // 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;
+            const localVarFormParams = new FormData();
+
+            // authentication Bearer required
+
 
             if (file !== undefined) { 
                 localVarFormParams.append('file', file as any);
@@ -339,15 +434,44 @@ export const SysFileApiFp = function(configuration?: Configuration) {
                 return axios.request(axiosRequestArgs);
             };
         },
+        /**
+         * 
+         * @summary 上传头像
+         * @param {Blob} [file] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultFileOutput>>> {
+            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).sysFileUploadAvatarPostForm(file, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
         /**
          * 
          * @summary 上传文件
          * @param {Blob} [file] 
+         * @param {string} [path] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async sysFileUploadPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultFileOutput>>> {
-            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).sysFileUploadPostForm(file, options);
+        async sysFileUploadPostForm(file?: Blob, path?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultFileOutput>>> {
+            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).sysFileUploadPostForm(file, path, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 上传电子签名
+         * @param {Blob} [file] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysFileUploadSignaturePostForm(file?: Blob, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultFileOutput>>> {
+            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).sysFileUploadSignaturePostForm(file, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -413,15 +537,36 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
         async sysFilePageGet(fileName?: string, startTime?: Date, endTime?: Date, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSqlSugarPagedListSysFile>> {
             return SysFileApiFp(configuration).sysFilePageGet(fileName, startTime, endTime, page, pageSize, field, order, descStr, options).then((request) => request(axios, basePath));
         },
+        /**
+         * 
+         * @summary 上传头像
+         * @param {Blob} [file] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultFileOutput>> {
+            return SysFileApiFp(configuration).sysFileUploadAvatarPostForm(file, options).then((request) => request(axios, basePath));
+        },
         /**
          * 
          * @summary 上传文件
          * @param {Blob} [file] 
+         * @param {string} [path] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysFileUploadPostForm(file?: Blob, path?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultFileOutput>> {
+            return SysFileApiFp(configuration).sysFileUploadPostForm(file, path, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 上传电子签名
+         * @param {Blob} [file] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async sysFileUploadPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultFileOutput>> {
-            return SysFileApiFp(configuration).sysFileUploadPostForm(file, options).then((request) => request(axios, basePath));
+        async sysFileUploadSignaturePostForm(file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultFileOutput>> {
+            return SysFileApiFp(configuration).sysFileUploadSignaturePostForm(file, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -483,16 +628,39 @@ export class SysFileApi extends BaseAPI {
     public async sysFilePageGet(fileName?: string, startTime?: Date, endTime?: Date, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSqlSugarPagedListSysFile>> {
         return SysFileApiFp(this.configuration).sysFilePageGet(fileName, startTime, endTime, page, pageSize, field, order, descStr, options).then((request) => request(this.axios, this.basePath));
     }
+    /**
+     * 
+     * @summary 上传头像
+     * @param {Blob} [file] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysFileApi
+     */
+    public async sysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultFileOutput>> {
+        return SysFileApiFp(this.configuration).sysFileUploadAvatarPostForm(file, options).then((request) => request(this.axios, this.basePath));
+    }
     /**
      * 
      * @summary 上传文件
      * @param {Blob} [file] 
+     * @param {string} [path] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysFileApi
+     */
+    public async sysFileUploadPostForm(file?: Blob, path?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultFileOutput>> {
+        return SysFileApiFp(this.configuration).sysFileUploadPostForm(file, path, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 上传电子签名
+     * @param {Blob} [file] 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof SysFileApi
      */
-    public async sysFileUploadPostForm(file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultFileOutput>> {
-        return SysFileApiFp(this.configuration).sysFileUploadPostForm(file, options).then((request) => request(this.axios, this.basePath));
+    public async sysFileUploadSignaturePostForm(file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultFileOutput>> {
+        return SysFileApiFp(this.configuration).sysFileUploadSignaturePostForm(file, options).then((request) => request(this.axios, this.basePath));
     }
     /**
      * 

+ 118 - 49
Web/src/api-services/apis/sys-user-api.ts

@@ -25,6 +25,7 @@ import { AdminResultSysUser } from '../models';
 import { ChangePwdInput } from '../models';
 import { DeleteUserInput } from '../models';
 import { ResetPwdUserInput } from '../models';
+import { SysUser } from '../models';
 import { UpdateUserInput } from '../models';
 import { UserInput } from '../models';
 import { UserRoleInput } from '../models';
@@ -77,13 +78,49 @@ export const SysUserApiAxiosParamCreator = function (configuration?: Configurati
         },
         /**
          * 
-         * @summary 修改用户密码
-         * @param {ChangePwdInput} [body] 
+         * @summary 查看用户基本信息
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        sysUserChangePwdPost: async (body?: ChangePwdInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            const localVarPath = `/sysUser/changePwd`;
+        sysUserBaseGet: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysUser/base`;
+            // 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
+
+            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 {SysUser} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysUserBasePost: async (body?: SysUser, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysUser/base`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
             let baseOptions;
@@ -118,13 +155,13 @@ export const SysUserApiAxiosParamCreator = function (configuration?: Configurati
         },
         /**
          * 
-         * @summary 删除用户
-         * @param {DeleteUserInput} [body] 
+         * @summary 修改用户密码
+         * @param {ChangePwdInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        sysUserDeletePost: async (body?: DeleteUserInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            const localVarPath = `/sysUser/delete`;
+        sysUserChangePwdPost: async (body?: ChangePwdInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysUser/changePwd`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
             let baseOptions;
@@ -159,28 +196,26 @@ export const SysUserApiAxiosParamCreator = function (configuration?: Configurati
         },
         /**
          * 
-         * @summary 查看用户
-         * @param {number} [id] 
+         * @summary 删除用户
+         * @param {DeleteUserInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        sysUserDetailGet: async (id?: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            const localVarPath = `/sysUser/detail`;
+        sysUserDeletePost: async (body?: DeleteUserInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysUser/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: 'GET', ...baseOptions, ...options};
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
             const localVarHeaderParameter = {} as any;
             const localVarQueryParameter = {} as any;
 
             // authentication Bearer required
 
-            if (id !== undefined) {
-                localVarQueryParameter['id'] = id;
-            }
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
 
             const query = new URLSearchParams(localVarUrlObj.search);
             for (const key in localVarQueryParameter) {
@@ -192,6 +227,8 @@ export const SysUserApiAxiosParamCreator = function (configuration?: Configurati
             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,
@@ -552,13 +589,12 @@ export const SysUserApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 修改用户密码
-         * @param {ChangePwdInput} [body] 
+         * @summary 查看用户基本信息
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async sysUserChangePwdPost(body?: ChangePwdInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultInt32>>> {
-            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserChangePwdPost(body, options);
+        async sysUserBaseGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSysUser>>> {
+            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserBaseGet(options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -566,13 +602,27 @@ export const SysUserApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 删除用户
-         * @param {DeleteUserInput} [body] 
+         * @summary 设置用户基本信息
+         * @param {SysUser} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async sysUserDeletePost(body?: DeleteUserInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
-            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserDeletePost(body, options);
+        async sysUserBasePost(body?: SysUser, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultInt32>>> {
+            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserBasePost(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 {ChangePwdInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysUserChangePwdPost(body?: ChangePwdInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultInt32>>> {
+            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserChangePwdPost(body, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -580,13 +630,13 @@ export const SysUserApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 查看用户
-         * @param {number} [id] 
+         * @summary 删除用户
+         * @param {DeleteUserInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async sysUserDetailGet(id?: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSysUser>>> {
-            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserDetailGet(id, options);
+        async sysUserDeletePost(body?: DeleteUserInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserDeletePost(body, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -717,6 +767,25 @@ export const SysUserApiFactory = function (configuration?: Configuration, basePa
         async sysUserAddPost(body?: AddUserInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
             return SysUserApiFp(configuration).sysUserAddPost(body, options).then((request) => request(axios, basePath));
         },
+        /**
+         * 
+         * @summary 查看用户基本信息
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysUserBaseGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysUser>> {
+            return SysUserApiFp(configuration).sysUserBaseGet(options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 设置用户基本信息
+         * @param {SysUser} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysUserBasePost(body?: SysUser, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultInt32>> {
+            return SysUserApiFp(configuration).sysUserBasePost(body, options).then((request) => request(axios, basePath));
+        },
         /**
          * 
          * @summary 修改用户密码
@@ -737,16 +806,6 @@ export const SysUserApiFactory = function (configuration?: Configuration, basePa
         async sysUserDeletePost(body?: DeleteUserInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
             return SysUserApiFp(configuration).sysUserDeletePost(body, options).then((request) => request(axios, basePath));
         },
-        /**
-         * 
-         * @summary 查看用户
-         * @param {number} [id] 
-         * @param {*} [options] Override http request option.
-         * @throws {RequiredError}
-         */
-        async sysUserDetailGet(id?: number, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysUser>> {
-            return SysUserApiFp(configuration).sysUserDetailGet(id, options).then((request) => request(axios, basePath));
-        },
         /**
          * 
          * @summary 授权用户角色
@@ -846,6 +905,27 @@ export class SysUserApi extends BaseAPI {
     public async sysUserAddPost(body?: AddUserInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
         return SysUserApiFp(this.configuration).sysUserAddPost(body, options).then((request) => request(this.axios, this.basePath));
     }
+    /**
+     * 
+     * @summary 查看用户基本信息
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysUserApi
+     */
+    public async sysUserBaseGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysUser>> {
+        return SysUserApiFp(this.configuration).sysUserBaseGet(options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 设置用户基本信息
+     * @param {SysUser} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysUserApi
+     */
+    public async sysUserBasePost(body?: SysUser, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultInt32>> {
+        return SysUserApiFp(this.configuration).sysUserBasePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
     /**
      * 
      * @summary 修改用户密码
@@ -868,17 +948,6 @@ export class SysUserApi extends BaseAPI {
     public async sysUserDeletePost(body?: DeleteUserInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
         return SysUserApiFp(this.configuration).sysUserDeletePost(body, options).then((request) => request(this.axios, this.basePath));
     }
-    /**
-     * 
-     * @summary 查看用户
-     * @param {number} [id] 
-     * @param {*} [options] Override http request option.
-     * @throws {RequiredError}
-     * @memberof SysUserApi
-     */
-    public async sysUserDetailGet(id?: number, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysUser>> {
-        return SysUserApiFp(this.configuration).sysUserDetailGet(id, options).then((request) => request(this.axios, this.basePath));
-    }
     /**
      * 
      * @summary 授权用户角色

+ 6 - 0
Web/src/api-services/models/add-user-input.ts

@@ -271,6 +271,12 @@ export interface AddUserInput {
      * @memberof AddUserInput
      */
     lastLoginDevice?: string | null;
+    /**
+     * 电子签名
+     * @type {string}
+     * @memberof AddUserInput
+     */
+    signature?: string | null;
     /**
      * 账号
      * @type {string}

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

@@ -151,7 +151,9 @@ export * from './sys-config';
 export * from './sys-dict-data';
 export * from './sys-dict-type';
 export * from './sys-file';
+export * from './sys-file-upload-avatar-body';
 export * from './sys-file-upload-body';
+export * from './sys-file-upload-signature-body';
 export * from './sys-file-uploads-body';
 export * from './sys-log-diff';
 export * from './sys-log-ex';

+ 18 - 6
Web/src/api-services/models/login-user-output.ts

@@ -17,12 +17,6 @@
  * @interface LoginUserOutput
  */
 export interface LoginUserOutput {
-    /**
-     * 用户Id
-     * @type {number}
-     * @memberof LoginUserOutput
-     */
-    userId?: number;
     /**
      * 账号名称
      * @type {string}
@@ -47,6 +41,18 @@ export interface LoginUserOutput {
      * @memberof LoginUserOutput
      */
     introduction?: string | null;
+    /**
+     * 地址
+     * @type {string}
+     * @memberof LoginUserOutput
+     */
+    address?: string | null;
+    /**
+     * 电子签名
+     * @type {string}
+     * @memberof LoginUserOutput
+     */
+    signature?: string | null;
     /**
      * 机构Id
      * @type {number}
@@ -59,6 +65,12 @@ export interface LoginUserOutput {
      * @memberof LoginUserOutput
      */
     orgName?: string | null;
+    /**
+     * 职位名称
+     * @type {string}
+     * @memberof LoginUserOutput
+     */
+    posName?: string | null;
     /**
      * 按钮权限集合
      * @type {Array<string>}

+ 26 - 0
Web/src/api-services/models/sys-file-upload-avatar-body.ts

@@ -0,0 +1,26 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * 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 SysFileUploadAvatarBody
+ */
+export interface SysFileUploadAvatarBody {
+    /**
+     * 
+     * @type {Blob}
+     * @memberof SysFileUploadAvatarBody
+     */
+    file: Blob;
+}

+ 26 - 0
Web/src/api-services/models/sys-file-upload-signature-body.ts

@@ -0,0 +1,26 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * 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 SysFileUploadSignatureBody
+ */
+export interface SysFileUploadSignatureBody {
+    /**
+     * 
+     * @type {Blob}
+     * @memberof SysFileUploadSignatureBody
+     */
+    file: Blob;
+}

+ 6 - 0
Web/src/api-services/models/sys-user.ts

@@ -282,4 +282,10 @@ export interface SysUser {
      * @memberof SysUser
      */
     lastLoginDevice?: string | null;
+    /**
+     * 电子签名
+     * @type {string}
+     * @memberof SysUser
+     */
+    signature?: string | null;
 }

+ 1 - 0
Web/src/api-services/models/update-db-table-input.ts

@@ -42,3 +42,4 @@ export interface UpdateDbTableInput {
      */
     description?: string | null;
 }
+

+ 6 - 0
Web/src/api-services/models/update-user-input.ts

@@ -271,6 +271,12 @@ export interface UpdateUserInput {
      * @memberof UpdateUserInput
      */
     lastLoginDevice?: string | null;
+    /**
+     * 电子签名
+     * @type {string}
+     * @memberof UpdateUserInput
+     */
+    signature?: string | null;
     /**
      * 账号
      * @type {string}

+ 99 - 0
Web/src/api/system/admin.ts

@@ -0,0 +1,99 @@
+import request from '/@/utils/request';
+
+enum Api {
+	// 代码生成接口
+	GetGeneratePage = '/codeGenerate/page',
+	AddGenerate = '/codeGenerate/add',
+	UpdateGenerate = '/codeGenerate/edit',
+	DeleGenerate = '/codeGenerate/delete',
+	GetTableList = '/codeGenerate/InformationList',
+	GetColumnList = '/codeGenerate/ColumnList',
+	GenerateRunLocal = '/codeGenerate/runLocal',
+	GenerateRunDown = '/codeGenerate/runDown',
+	GetGenerateConfigList = '/sysCodeGenerateConfig/list',
+	UpdateGenerateConfig = '/sysCodeGenerateConfig/edit',
+
+	// 数据库接口
+	GetColumnInfoList = '/sysDatabase/columnList',
+	GetTableInfoList = '/sysDatabase/tableList',
+	AddTable = '/sysDatabase/table/add',
+	UpdateTable = '/sysDatabase/table/update',
+	DeleTetable = '/sysDatabase/table/delete',
+	AddColumn = '/sysDatabase/column/add',
+	UpdateColumn = '/sysDatabase/column/update',
+	DeleteColumn = '/sysDatabase/column/delete',
+	CreateEntity = '/sysDatabase/entity/createEntity',
+}
+
+//////////数据库管理接口 //////////
+// 获取表字段
+export const getColumnInfoList = (params: any) =>
+	request({
+		url: Api.GetColumnInfoList,
+		method: 'get',
+		data: params,
+	});
+
+// 获取所有表
+export const getTableInfoList = (params?: any) =>
+	request({
+		url: Api.GetTableInfoList,
+		method: 'get',
+		data: params,
+	});
+
+// 添加表
+export const addTable = (params: any) =>
+request({
+    url: Api.AddTable,
+    method: 'post',
+    data: params,
+});
+
+// 修改表
+export const updateTable = (params: any) =>
+request({
+    url: Api.UpdateTable,
+    method: 'post',
+    data: params,
+});
+
+// 删除表
+export const deleteTable = (params: any) =>
+request({
+    url: Api.DeleTetable,
+    method: 'post',
+    data: params,
+});
+
+// 添加字段
+export const addColumn = (params: any) =>
+request({
+    url: Api.AddColumn,
+    method: 'post',
+    data: params,
+});
+
+// 修改字段
+export const updateColumn = (params: any) =>
+request({
+    url: Api.UpdateColumn,
+    method: 'post',
+    data: params,
+});
+
+// 删除字段
+export const deleteColumn = (params: any) =>
+request({
+    url: Api.DeleteColumn,
+    method: 'post',
+    data: params,
+});
+
+// 生成实体
+export const createEntity = (params: any) =>
+request({
+    url: Api.CreateEntity,
+    method: 'post',
+    data: params,
+});

+ 2 - 5
Web/src/layout/navBars/breadcrumb/user.vue

@@ -64,11 +64,8 @@
 			</span>
 			<template #dropdown>
 				<el-dropdown-menu>
-					<!-- <el-dropdown-item command="/dashboard/workbench">{{ $t('message.user.dropdown1') }}</el-dropdown-item> -->
-					<!-- <el-dropdown-item command="wareHouse">{{ $t('message.user.dropdown6') }}</el-dropdown-item> -->
-					<el-dropdown-item command="/personal">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
-					<!-- <el-dropdown-item command="/404">{{ $t('message.user.dropdown3') }}</el-dropdown-item>
-					<el-dropdown-item command="/401">{{ $t('message.user.dropdown4') }}</el-dropdown-item> -->
+					<!-- <el-dropdown-item command="/dashboard/home">{{ $t('message.user.dropdown1') }}</el-dropdown-item> -->
+					<el-dropdown-item command="/system/userCenter">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
 					<el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
 				</el-dropdown-menu>
 			</template>

+ 1 - 1
Web/src/layout/navBars/tagsView/tagsView.vue

@@ -530,7 +530,7 @@ export default defineComponent({
 			// 监听布局配置开启 TagsView 共用,为了演示还原默认值
 			proxy.mittBus.on('openShareTagsView', () => {
 				if (getThemeConfig.value.isShareTagsView) {
-					router.push('/dashboard/workbench');
+					router.push('/dashboard/home');
 					state.tagsViewList = [];
 					state.tagsViewRoutesList.map((v: any) => {
 						if (v.meta.isAffix && !v.meta.isHide) {

+ 6 - 3
Web/src/main.ts

@@ -7,19 +7,22 @@ import { i18n } from '/@/i18n/index';
 import other from '/@/utils/other';
 
 import ElementPlus from 'element-plus';
-import VForm3 from 'vform3-builds'; // 引入VForm 3库
 import 'element-plus/dist/index.css';
-import 'vform3-builds/dist/designer.style.css'; // 引入VForm3样式
+import VForm3 from 'vform3-builds'; // VForm3表单设计
+import 'vform3-builds/dist/designer.style.css';
 import '/@/theme/index.scss';
 import mitt from 'mitt';
 import VueGridLayout from 'vue-grid-layout';
+import VueSignaturePad from 'vue-signature-pad'; // 电子签名
+import vue3TreeOrg from 'vue3-tree-org'; // 组织架构图
+import 'vue3-tree-org/lib/vue3-tree-org.css';
 
 const app = createApp(App);
 
 directive(app);
 other.elSvg(app);
 
-app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(VForm3).use(i18n).use(VueGridLayout).mount('#app');
+app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(VForm3).use(i18n).use(VueGridLayout).use(VueSignaturePad).use(vue3TreeOrg).mount('#app');
 
 const globalProperties = {
 	mittBus: mitt(),

+ 1 - 1
Web/src/router/index.ts

@@ -98,7 +98,7 @@ router.beforeEach(async (to, from, next) => {
 			Session.clear();
 			NProgress.done();
 		} else if (token && to.path === '/login') {
-			next('/dashboard/workbench');
+			next('/dashboard/home');
 			NProgress.done();
 		} else {
 			const storesRoutesList = useRoutesList(pinia);

+ 1 - 1
Web/src/router/route.ts

@@ -26,7 +26,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
 		path: '/',
 		name: '/',
 		component: () => import('/@/layout/index.vue'),
-		redirect: '/dashboard/workbench',
+		redirect: '/dashboard/home',
 		meta: {
 			isKeepAlive: true,
 		},

+ 5 - 0
Web/src/stores/interface/index.ts

@@ -8,6 +8,11 @@ export interface UserInfosState {
 	account: string;
 	realName: string;
 	avatar: string;
+	address: string;
+	signature: string;
+	orgId: string;
+	orgName: string;
+	posName: string;
 	roles: string[];
 	authBtnList: string[];
 	time: number;

+ 7 - 9
Web/src/stores/userInfo.ts

@@ -1,5 +1,5 @@
 import { defineStore } from 'pinia';
-import { UserInfosStates } from './interface';
+import { UserInfosState, UserInfosStates } from './interface';
 import { Session } from '/@/utils/storage';
 
 import { getAPI } from '/@/utils/axios-utils';
@@ -11,14 +11,7 @@ import { SysAuthApi } from '/@/api-services/api';
  */
 export const useUserInfo = defineStore('userInfo', {
 	state: (): UserInfosStates => ({
-		userInfos: {
-			account: '',
-			realName: '',
-			avatar: '',
-			roles: [],
-			authBtnList: [],
-			time: 0,
-		},
+		userInfos: {} as UserInfosState,
 	}),
 	actions: {
 		async setUserInfos() {
@@ -42,6 +35,11 @@ export const useUserInfo = defineStore('userInfo', {
 							account: d.account,
 							realName: d.realName,
 							avatar: d.avatar ? d.avatar : '/favicon.ico',
+							address: d.address,
+							signature: d.signature,
+							orgId: d.orgId,
+							orgName: d.orgName,
+							posName: d.posName,
 							roles: [],
 							authBtnList: d.buttons,
 							time: new Date().getTime(),

+ 71 - 28
Web/src/utils/base64Conver.ts

@@ -2,16 +2,16 @@
  * @description: base64 to blob
  */
 export function dataURLtoBlob(base64Buf: string): Blob {
-  const arr = base64Buf.split(',');
-  const typeItem = arr[0];
-  const mime = typeItem.match(/:(.*?);/)![1];
-  const bstr = window.atob(arr[1]);
-  let n = bstr.length;
-  const u8arr = new Uint8Array(n);
-  while (n--) {
-    u8arr[n] = bstr.charCodeAt(n);
-  }
-  return new Blob([u8arr], { type: mime });
+	const arr = base64Buf.split(',');
+	const typeItem = arr[0];
+	const mime = typeItem.match(/:(.*?);/)![1];
+	const bstr = window.atob(arr[1]);
+	let n = bstr.length;
+	const u8arr = new Uint8Array(n);
+	while (n--) {
+		u8arr[n] = bstr.charCodeAt(n);
+	}
+	return new Blob([u8arr], { type: mime });
 }
 
 /**
@@ -19,23 +19,66 @@ export function dataURLtoBlob(base64Buf: string): Blob {
  * @param url
  */
 export function urlToBase64(url: string, mineType?: string): Promise<string> {
-  return new Promise((resolve, reject) => {
-    let canvas = document.createElement('CANVAS') as Nullable<HTMLCanvasElement>;
-    const ctx = canvas!.getContext('2d');
+	return new Promise((resolve, reject) => {
+		let canvas = document.createElement('CANVAS') as Nullable<HTMLCanvasElement>;
+		const ctx = canvas!.getContext('2d');
 
-    const img = new Image();
-    img.crossOrigin = '';
-    img.onload = function () {
-      if (!canvas || !ctx) {
-        return reject();
-      }
-      canvas.height = img.height;
-      canvas.width = img.width;
-      ctx.drawImage(img, 0, 0);
-      const dataURL = canvas.toDataURL(mineType || 'image/png');
-      canvas = null;
-      resolve(dataURL);
-    };
-    img.src = url;
-  });
+		const img = new Image();
+		img.crossOrigin = '';
+		img.onload = function () {
+			if (!canvas || !ctx) {
+				return reject();
+			}
+			canvas.height = img.height;
+			canvas.width = img.width;
+			ctx.drawImage(img, 0, 0);
+			const dataURL = canvas.toDataURL(mineType || 'image/png');
+			canvas = null;
+			resolve(dataURL);
+		};
+		img.src = url;
+	});
+}
+
+/**
+ * File转Base64
+ * @param file
+ */
+export function fileToBase64(file: Blob) {
+	return new Promise((resolve, reject) => {
+		const reader = new FileReader();
+		reader.readAsDataURL(file);
+		reader.onload = () => resolve(reader.result);
+		reader.onerror = (error) => reject(error);
+	});
+}
+
+/**
+ * Base64转File
+ * @param dataURL   {String}  base64
+ * @param fileName	{String}  文件名
+ * @param mimeType	{String}  [可选]文件类型,默认为base64中的类型
+ * @returns {File}
+ */
+export function base64ToFile(dataURL: string, fileName: string, mimeType = null) {
+	var arr = dataURL.split(',');
+	var defaultMimeType = arr[0].match(/:(.*?);/)[1];
+	var bStr = atob(arr[1]);
+	let n = bStr.length;
+	var u8arr = new Uint8Array(n);
+	while (n--) {
+		u8arr[n] = bStr.charCodeAt(n);
+	}
+	return new File([u8arr], fileName, { type: mimeType || defaultMimeType });
+}
+
+/**
+ * Blob转File
+ * @param blob     {Blob}   blob
+ * @param fileName {String} 文件名
+ * @param mimeType {String} 文件类型
+ * @return {File}
+ */
+export function blobToFile(blob: Blob, fileName: string, mimeType: any) {
+	return new File([blob], fileName, { type: mimeType });
 }

+ 0 - 0
Web/src/utils/http/requset.js


+ 175 - 21
Web/src/utils/request.ts

@@ -1,20 +1,74 @@
-import axios from 'axios';
+import axios, { AxiosInstance } from 'axios';
 import { ElMessage, ElMessageBox } from 'element-plus';
-import { Session } from '/@/utils/storage';
+import { Local, Session } from '/@/utils/storage';
 
 // 配置新建一个 axios 实例
-const service = axios.create({
+export const service = axios.create({
 	baseURL: import.meta.env.VITE_API_URL as any,
 	timeout: 50000,
 	headers: { 'Content-Type': 'application/json' },
 });
 
+// token 键定义
+export const accessTokenKey = 'access-token';
+export const refreshAccessTokenKey = `x-${accessTokenKey}`;
+
+// 获取 token
+export const getToken = () => {
+	return Local.get(accessTokenKey);
+};
+
+// 清除 token
+export const clearAccessTokens = () => {
+	Local.remove(accessTokenKey);
+	Local.remove(refreshAccessTokenKey);
+
+	// 清除其他
+	Session.clear();
+
+	// 刷新浏览器
+	window.location.reload();
+};
+
+// axios 默认实例
+export const axiosInstance: AxiosInstance = axios;
+
 // 添加请求拦截器
 service.interceptors.request.use(
 	(config) => {
-		// 在发送请求之前做些什么 token
-		if (Session.get('token')) {
-			(<any>config.headers).common['Authorization'] = `${Session.get('token')}`;
+		// // 在发送请求之前做些什么 token
+		// if (Session.get('token')) {
+		// 	(<any>config.headers).common['Authorization'] = `${Session.get('token')}`;
+		// }
+
+		// 获取本地的 token
+		const accessToken = Local.get(accessTokenKey);
+		if (accessToken) {
+			// 将 token 添加到请求报文头中
+			config.headers!['Authorization'] = `Bearer ${accessToken}`;
+
+			// 判断 accessToken 是否过期
+			const jwt: any = decryptJWT(accessToken);
+			const exp = getJWTDate(jwt.exp as number);
+
+			// token 已经过期
+			if (new Date() >= exp) {
+				// 获取刷新 token
+				const refreshAccessToken = Local.get(refreshAccessTokenKey);
+
+				// 携带刷新 token
+				if (refreshAccessToken) {
+					config.headers!['X-Authorization'] = `Bearer ${refreshAccessToken}`;
+				}
+			}
+			// debugger
+			// get请求映射params参数
+			if (config.method?.toLowerCase() === 'get' && config.data) {
+				let url = config.url + '?' + tansParams(config.data);
+				url = url.slice(0, -1);
+				config.data = {};
+				config.url = url;
+			}
 		}
 		return config;
 	},
@@ -26,24 +80,76 @@ service.interceptors.request.use(
 
 // 添加响应拦截器
 service.interceptors.response.use(
-	(response) => {
-		// 对响应数据做点什么
-		const res = response.data;
-		if (res.code && res.code !== 0) {
-			// `token` 过期或者账号已在别处登录
-			if (res.code === 401 || res.code === 4001) {
-				Session.clear(); // 清除浏览器全部临时缓存
-				window.location.href = '/'; // 去登录页
-				ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
-					.then(() => {})
-					.catch(() => {});
-			}
-			return Promise.reject(service.interceptors.response);
-		} else {
-			return response.data;
+	(res) => {
+		// // 对响应数据做点什么
+		// const res = response.data;
+		// if (res.code && res.code !== 0) {
+		// 	// `token` 过期或者账号已在别处登录
+		// 	if (res.code === 401 || res.code === 4001) {
+		// 		Session.clear(); // 清除浏览器全部临时缓存
+		// 		window.location.href = '/'; // 去登录页
+		// 		ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
+		// 			.then(() => {})
+		// 			.catch(() => {});
+		// 	}
+		// 	return Promise.reject(service.interceptors.response);
+		// } else {
+		// 	return response.data;
+		// }
+		// 获取状态码和返回数据
+		var status = res.status;
+		var serve = res.data;
+
+		// 处理 401
+		if (status === 401) {
+			clearAccessTokens();
+		}
+
+		// 处理未进行规范化处理的
+		if (status >= 400) {
+			throw new Error(res.statusText || 'Request Error.');
+		}
+
+		// 处理规范化结果错误
+		if (serve && serve.hasOwnProperty('errors') && serve.errors) {
+			throw new Error(JSON.stringify(serve.errors || 'Request Error.'));
+		}
+
+		// 读取响应报文头 token 信息
+		var accessToken = res.headers[accessTokenKey];
+		var refreshAccessToken = res.headers[refreshAccessTokenKey];
+
+		// 判断是否是无效 token
+		if (accessToken === 'invalid_token') {
+			clearAccessTokens();
+		}
+		// 判断是否存在刷新 token,如果存在则存储在本地
+		else if (refreshAccessToken && accessToken && accessToken !== 'invalid_token') {
+			Local.set(accessTokenKey, accessToken);
+			Local.set(refreshAccessTokenKey, refreshAccessToken);
 		}
+
+		// 响应拦截及自定义处理
+		if (serve.code === 401) {
+			clearAccessTokens();
+		} else if (serve.code === undefined) {
+			return Promise.resolve(res);
+		} else if (serve.code !== 200) {
+			var message = JSON.stringify(serve.message);
+			ElMessage.error(message);
+			throw new Error(message);
+		}
+
+		return res;
 	},
 	(error) => {
+		// 处理响应错误
+		if (error.response) {
+			if (error.response.status === 401) {
+				clearAccessTokens();
+			}
+		}
+
 		// 对响应错误做点什么
 		if (error.message.indexOf('timeout') != -1) {
 			ElMessage.error('网络超时');
@@ -53,9 +159,57 @@ service.interceptors.response.use(
 			if (error.response.data) ElMessage.error(error.response.statusText);
 			else ElMessage.error('接口路径找不到');
 		}
+
 		return Promise.reject(error);
 	}
 );
 
+/**
+ *  参数处理
+ * @param {*} params  参数
+ */
+export function tansParams(params: any) {
+	let result = '';
+	for (const propName of Object.keys(params)) {
+		const value = params[propName];
+		var part = encodeURIComponent(propName) + '=';
+		if (value !== null && value !== '' && typeof value !== 'undefined') {
+			if (typeof value === 'object') {
+				for (const key of Object.keys(value)) {
+					if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
+						let params = propName + '[' + key + ']';
+						var subPart = encodeURIComponent(params) + '=';
+						result += subPart + encodeURIComponent(value[key]) + '&';
+					}
+				}
+			} else {
+				result += part + encodeURIComponent(value) + '&';
+			}
+		}
+	}
+	return result;
+}
+
+/**
+ * 解密 JWT token 的信息
+ * @param token jwt token 字符串
+ * @returns <any>object
+ */
+export function decryptJWT(token: string): any {
+	token = token.replace(/_/g, '/').replace(/-/g, '+');
+	var json = decodeURIComponent(escape(window.atob(token.split('.')[1])));
+	return JSON.parse(json);
+}
+
+/**
+ * 将 JWT 时间戳转换成 Date
+ * @description 主要针对 `exp`,`iat`,`nbf`
+ * @param timestamp 时间戳
+ * @returns Date 对象
+ */
+export function getJWTDate(timestamp: number): Date {
+	return new Date(timestamp * 1000);
+}
+
 // 导出 axios 实例
 export default service;

+ 236 - 0
Web/src/utils/ruoyi.js

@@ -0,0 +1,236 @@
+
+
+/**
+ * 通用js方法封装处理
+ * Copyright (c) 2019 ruoyi
+ */
+
+// 日期格式化
+export function parseTime(time, pattern) {
+  if (arguments.length === 0 || !time) {
+    return null
+  }
+  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
+  let date
+  if (typeof time === 'object') {
+    date = time
+  } else {
+    if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
+      time = parseInt(time)
+    } else if (typeof time === 'string') {
+      time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
+    }
+    if ((typeof time === 'number') && (time.toString().length === 10)) {
+      time = time * 1000
+    }
+    date = new Date(time)
+  }
+  const formatObj = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds(),
+    a: date.getDay()
+  }
+  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+    let value = formatObj[key]
+    // Note: getDay() returns 0 on Sunday
+    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
+    if (result.length > 0 && value < 10) {
+      value = '0' + value
+    }
+    return value || 0
+  })
+  return time_str
+}
+
+// 表单重置
+export function resetForm(refName) {
+  if (this.$refs[refName]) {
+    this.$refs[refName].resetFields();
+  }
+}
+
+// 添加日期范围
+export function addDateRange(params, dateRange, propName) {
+  let search = params;
+  search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
+  dateRange = Array.isArray(dateRange) ? dateRange : [];
+  if (typeof (propName) === 'undefined') {
+    search.params['beginTime'] = dateRange[0];
+    search.params['endTime'] = dateRange[1];
+  } else {
+    search.params['begin' + propName] = dateRange[0];
+    search.params['end' + propName] = dateRange[1];
+  }
+  return search;
+}
+
+// 回显数据字典
+export function selectDictLabel(datas, value) {
+  if (value === undefined) {
+    return "";
+  }
+  var actions = [];
+  Object.keys(datas).some((key) => {
+    if (datas[key].value == ('' + value)) {
+      actions.push(datas[key].label);
+      return true;
+    }
+  })
+  if (actions.length === 0) {
+    actions.push(value);
+  }
+  return actions.join('');
+}
+
+// 回显数据字典(字符串数组)
+export function selectDictLabels(datas, value, separator) {
+  if (value === undefined) {
+    return "";
+  }
+  var actions = [];
+  var currentSeparator = undefined === separator ? "," : separator;
+  var temp = value.split(currentSeparator);
+  Object.keys(value.split(currentSeparator)).some((val) => {
+    var match = false;
+    Object.keys(datas).some((key) => {
+      if (datas[key].value == ('' + temp[val])) {
+        actions.push(datas[key].label + currentSeparator);
+        match = true;
+      }
+    })
+    if (!match) {
+      actions.push(temp[val] + currentSeparator);
+    }
+  })
+  return actions.join('').substring(0, actions.join('').length - 1);
+}
+
+// 字符串格式化(%s )
+export function sprintf(str) {
+  var args = arguments, flag = true, i = 1;
+  str = str.replace(/%s/g, function () {
+    var arg = args[i++];
+    if (typeof arg === 'undefined') {
+      flag = false;
+      return '';
+    }
+    return arg;
+  });
+  return flag ? str : '';
+}
+
+// 转换字符串,undefined,null等转化为""
+export function parseStrEmpty(str) {
+  if (!str || str == "undefined" || str == "null") {
+    return "";
+  }
+  return str;
+}
+
+// 数据合并
+export function mergeRecursive(source, target) {
+  for (var p in target) {
+    try {
+      if (target[p].constructor == Object) {
+        source[p] = mergeRecursive(source[p], target[p]);
+      } else {
+        source[p] = target[p];
+      }
+    } catch (e) {
+      source[p] = target[p];
+    }
+  }
+  return source;
+};
+
+/**
+ * 构造树型结构数据
+ * @param {*} data 数据源
+ * @param {*} id id字段 默认 'id'
+ * @param {*} parentId 父节点字段 默认 'parentId'
+ * @param {*} children 孩子节点字段 默认 'children'
+ */
+export function handleTree(data, id, parentId, children) {
+  let config = {
+    id: id || 'id',
+    parentId: parentId || 'parentId',
+    childrenList: children || 'children'
+  };
+
+  var childrenListMap = {};
+  var nodeIds = {};
+  var tree = [];
+
+  for (let d of data) {
+    let parentId = d[config.parentId];
+    if (childrenListMap[parentId] == null) {
+      childrenListMap[parentId] = [];
+    }
+    nodeIds[d[config.id]] = d;
+    childrenListMap[parentId].push(d);
+  }
+
+  for (let d of data) {
+    let parentId = d[config.parentId];
+    if (nodeIds[parentId] == null) {
+      tree.push(d);
+    }
+  }
+
+  for (let t of tree) {
+    adaptToChildrenList(t);
+  }
+
+  function adaptToChildrenList(o) {
+    if (childrenListMap[o[config.id]] !== null) {
+      o[config.childrenList] = childrenListMap[o[config.id]];
+    }
+    if (o[config.childrenList]) {
+      for (let c of o[config.childrenList]) {
+        adaptToChildrenList(c);
+      }
+    }
+  }
+  return tree;
+}
+
+/**
+* 参数处理
+* @param {*} params  参数
+*/
+export function tansParams(params) {
+  let result = ''
+  for (const propName of Object.keys(params)) {
+    const value = params[propName];
+    var part = encodeURIComponent(propName) + "=";
+    if (value !== null && value !== "" && typeof (value) !== "undefined") {
+      if (typeof value === 'object') {
+        for (const key of Object.keys(value)) {
+          if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
+            let params = propName + '[' + key + ']';
+            var subPart = encodeURIComponent(params) + "=";
+            result += subPart + encodeURIComponent(value[key]) + "&";
+          }
+        }
+      } else {
+        result += part + encodeURIComponent(value) + "&";
+      }
+    }
+  }
+  return result
+}
+
+// 验证是否为blob格式
+export async function blobValidate(data) {
+  try {
+    const text = await data.text();
+    JSON.parse(text);
+    return false;
+  } catch (error) {
+    return true;
+  }
+}

File diff suppressed because it is too large
+ 0 - 302
Web/src/views/dashboard/analysis/index.vue


File diff suppressed because it is too large
+ 0 - 302
Web/src/views/dashboard/workbench/index.vue


+ 1 - 10
Web/src/views/home/index.vue

@@ -1,16 +1,7 @@
 <template>
 	<div class="home-container">
 		<el-row :gutter="15" class="home-card-one mb15">
-			<el-col
-				:xs="24"
-				:sm="12"
-				:md="12"
-				:lg="6"
-				:xl="6"
-				v-for="(v, k) in homeOne"
-				:key="k"
-				:class="{ 'home-media home-media-lg': k > 1, 'home-media-sm': k === 1 }"
-			>
+			<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-for="(v, k) in homeOne" :key="k" :class="{ 'home-media home-media-lg': k > 1, 'home-media-sm': k === 1 }">
 				<div class="home-card-item flex">
 					<div class="flex-margin flex w100" :class="` home-one-animation${k}`">
 						<div class="flex-auto">

+ 0 - 387
Web/src/views/personal/index.vue

@@ -1,387 +0,0 @@
-<template>
-	<div class="personal">
-		<el-row>
-			<!-- 个人信息 -->
-			<el-col :xs="24" :sm="16">
-				<el-card shadow="hover" header="个人信息">
-					<div class="personal-user">
-						<div class="personal-user-left">
-							<el-upload class="h100 personal-user-left-upload" action="https://jsonplaceholder.typicode.com/posts/" multiple :limit="1">
-								<img src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" />
-							</el-upload>
-						</div>
-						<div class="personal-user-right">
-							<el-row>
-								<el-col :span="24" class="personal-title mb18">{{ currentTime }},admin,生活变的再糟糕,也不妨碍我变得更好! </el-col>
-								<el-col :span="24">
-									<el-row>
-										<el-col :xs="24" :sm="8" class="personal-item mb6">
-											<div class="personal-item-label">昵称:</div>
-											<div class="personal-item-value">小柒</div>
-										</el-col>
-										<el-col :xs="24" :sm="16" class="personal-item mb6">
-											<div class="personal-item-label">身份:</div>
-											<div class="personal-item-value">超级管理</div>
-										</el-col>
-									</el-row>
-								</el-col>
-								<el-col :span="24">
-									<el-row>
-										<el-col :xs="24" :sm="8" class="personal-item mb6">
-											<div class="personal-item-label">登录IP:</div>
-											<div class="personal-item-value">192.168.1.1</div>
-										</el-col>
-										<el-col :xs="24" :sm="16" class="personal-item mb6">
-											<div class="personal-item-label">登录时间:</div>
-											<div class="personal-item-value">2021-02-05 18:47:26</div>
-										</el-col>
-									</el-row>
-								</el-col>
-							</el-row>
-						</div>
-					</div>
-				</el-card>
-			</el-col>
-
-			<!-- 消息通知 -->
-			<el-col :xs="24" :sm="8" class="pl15 personal-info">
-				<el-card shadow="hover">
-					<template #header>
-						<span>消息通知</span>
-						<span class="personal-info-more">更多</span>
-					</template>
-					<div class="personal-info-box">
-						<ul class="personal-info-ul">
-							<li v-for="(v, k) in newsInfoList" :key="k" class="personal-info-li">
-								<a :href="v.link" target="_block" class="personal-info-li-title">{{ v.title }}</a>
-							</li>
-						</ul>
-					</div>
-				</el-card>
-			</el-col>
-
-			<!-- 营销推荐 -->
-			<el-col :span="24">
-				<el-card shadow="hover" class="mt15" header="营销推荐">
-					<el-row :gutter="15" class="personal-recommend-row">
-						<el-col :sm="6" v-for="(v, k) in recommendList" :key="k" class="personal-recommend-col">
-							<div class="personal-recommend" :style="{ 'background-color': v.bg }">
-								<SvgIcon :name="v.icon" :size="70" :style="{ color: v.iconColor }" />
-								<div class="personal-recommend-auto">
-									<div>{{ v.title }}</div>
-									<div class="personal-recommend-msg">{{ v.msg }}</div>
-								</div>
-							</div>
-						</el-col>
-					</el-row>
-				</el-card>
-			</el-col>
-
-			<!-- 更新信息 -->
-			<el-col :span="24">
-				<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
-					<div class="personal-edit-title">基本信息</div>
-					<el-form :model="personalForm" size="default" label-width="40px" class="mt35 mb35">
-						<el-row :gutter="35">
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="昵称">
-									<el-input v-model="personalForm.name" placeholder="请输入昵称" clearable></el-input>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="邮箱">
-									<el-input v-model="personalForm.email" placeholder="请输入邮箱" clearable></el-input>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="签名">
-									<el-input v-model="personalForm.autograph" placeholder="请输入签名" clearable></el-input>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="职业">
-									<el-select v-model="personalForm.occupation" placeholder="请选择职业" clearable class="w100">
-										<el-option label="计算机 / 互联网 / 通信" value="1"></el-option>
-										<el-option label="生产 / 工艺 / 制造" value="2"></el-option>
-										<el-option label="医疗 / 护理 / 制药" value="3"></el-option>
-									</el-select>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="手机">
-									<el-input v-model="personalForm.phone" placeholder="请输入手机" clearable></el-input>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="性别">
-									<el-select v-model="personalForm.sex" placeholder="请选择性别" clearable class="w100">
-										<el-option label="男" value="1"></el-option>
-										<el-option label="女" value="2"></el-option>
-									</el-select>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-								<el-form-item>
-									<el-button type="primary">
-										<el-icon>
-											<ele-Position />
-										</el-icon>
-										更新个人信息
-									</el-button>
-								</el-form-item>
-							</el-col>
-						</el-row>
-					</el-form>
-					<div class="personal-edit-title mb15">账号安全</div>
-					<div class="personal-edit-safe-box">
-						<div class="personal-edit-safe-item">
-							<div class="personal-edit-safe-item-left">
-								<div class="personal-edit-safe-item-left-label">账户密码</div>
-								<div class="personal-edit-safe-item-left-value">当前密码强度:强</div>
-							</div>
-							<div class="personal-edit-safe-item-right">
-								<el-button text type="primary">立即修改</el-button>
-							</div>
-						</div>
-					</div>
-					<div class="personal-edit-safe-box">
-						<div class="personal-edit-safe-item">
-							<div class="personal-edit-safe-item-left">
-								<div class="personal-edit-safe-item-left-label">密保手机</div>
-								<div class="personal-edit-safe-item-left-value">已绑定手机:132****4108</div>
-							</div>
-							<div class="personal-edit-safe-item-right">
-								<el-button text type="primary">立即修改</el-button>
-							</div>
-						</div>
-					</div>
-					<div class="personal-edit-safe-box">
-						<div class="personal-edit-safe-item">
-							<div class="personal-edit-safe-item-left">
-								<div class="personal-edit-safe-item-left-label">密保问题</div>
-								<div class="personal-edit-safe-item-left-value">已设置密保问题,账号安全大幅度提升</div>
-							</div>
-							<div class="personal-edit-safe-item-right">
-								<el-button text type="primary">立即设置</el-button>
-							</div>
-						</div>
-					</div>
-					<div class="personal-edit-safe-box">
-						<div class="personal-edit-safe-item">
-							<div class="personal-edit-safe-item-left">
-								<div class="personal-edit-safe-item-left-label">绑定QQ</div>
-								<div class="personal-edit-safe-item-left-value">已绑定QQ:110****566</div>
-							</div>
-							<div class="personal-edit-safe-item-right">
-								<el-button text type="primary">立即设置</el-button>
-							</div>
-						</div>
-					</div>
-				</el-card>
-			</el-col>
-		</el-row>
-	</div>
-</template>
-
-<script lang="ts">
-import { toRefs, reactive, computed, defineComponent } from 'vue';
-import { formatAxis } from '/@/utils/formatTime';
-import { newsInfoList, recommendList } from './mock';
-
-// 定义接口来定义对象的类型
-interface PersonalState {
-	newsInfoList: any;
-	recommendList: any;
-	personalForm: any;
-}
-
-export default defineComponent({
-	name: 'personal',
-	setup() {
-		const state = reactive<PersonalState>({
-			newsInfoList,
-			recommendList,
-			personalForm: {
-				name: '',
-				email: '',
-				autograph: '',
-				occupation: '',
-				phone: '',
-				sex: '',
-			},
-		});
-		// 当前时间提示语
-		const currentTime = computed(() => {
-			return formatAxis(new Date());
-		});
-		return {
-			currentTime,
-			...toRefs(state),
-		};
-	},
-});
-</script>
-
-<style scoped lang="scss">
-@import '../../theme/mixins/index.scss';
-.personal {
-	.personal-user {
-		height: 130px;
-		display: flex;
-		align-items: center;
-		.personal-user-left {
-			width: 100px;
-			height: 130px;
-			border-radius: 3px;
-			:deep(.el-upload) {
-				height: 100%;
-			}
-			.personal-user-left-upload {
-				img {
-					width: 100%;
-					height: 100%;
-					border-radius: 3px;
-				}
-				&:hover {
-					img {
-						animation: logoAnimation 0.3s ease-in-out;
-					}
-				}
-			}
-		}
-		.personal-user-right {
-			flex: 1;
-			padding: 0 15px;
-			.personal-title {
-				font-size: 18px;
-				@include text-ellipsis(1);
-			}
-			.personal-item {
-				display: flex;
-				align-items: center;
-				font-size: 13px;
-				.personal-item-label {
-					color: var(--el-text-color-secondary);
-					@include text-ellipsis(1);
-				}
-				.personal-item-value {
-					@include text-ellipsis(1);
-				}
-			}
-		}
-	}
-	.personal-info {
-		.personal-info-more {
-			float: right;
-			color: var(--el-text-color-secondary);
-			font-size: 13px;
-			&:hover {
-				color: var(--el-color-primary);
-				cursor: pointer;
-			}
-		}
-		.personal-info-box {
-			height: 130px;
-			overflow: hidden;
-			.personal-info-ul {
-				list-style: none;
-				.personal-info-li {
-					font-size: 13px;
-					padding-bottom: 10px;
-					.personal-info-li-title {
-						display: inline-block;
-						@include text-ellipsis(1);
-						color: var(--el-text-color-secondary);
-						text-decoration: none;
-					}
-					& a:hover {
-						color: var(--el-color-primary);
-						cursor: pointer;
-					}
-				}
-			}
-		}
-	}
-	.personal-recommend-row {
-		.personal-recommend-col {
-			.personal-recommend {
-				position: relative;
-				height: 100px;
-				border-radius: 3px;
-				overflow: hidden;
-				cursor: pointer;
-				&:hover {
-					i {
-						right: 0px !important;
-						bottom: 0px !important;
-						transition: all ease 0.3s;
-					}
-				}
-				i {
-					position: absolute;
-					right: -10px;
-					bottom: -10px;
-					font-size: 70px;
-					transform: rotate(-30deg);
-					transition: all ease 0.3s;
-				}
-				.personal-recommend-auto {
-					padding: 15px;
-					position: absolute;
-					left: 0;
-					top: 5%;
-					color: var(--next-color-white);
-					.personal-recommend-msg {
-						font-size: 12px;
-						margin-top: 10px;
-					}
-				}
-			}
-		}
-	}
-	.personal-edit {
-		.personal-edit-title {
-			position: relative;
-			padding-left: 10px;
-			color: var(--el-text-color-regular);
-			&::after {
-				content: '';
-				width: 2px;
-				height: 10px;
-				position: absolute;
-				left: 0;
-				top: 50%;
-				transform: translateY(-50%);
-				background: var(--el-color-primary);
-			}
-		}
-		.personal-edit-safe-box {
-			border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
-			padding: 15px 0;
-			.personal-edit-safe-item {
-				width: 100%;
-				display: flex;
-				align-items: center;
-				justify-content: space-between;
-				.personal-edit-safe-item-left {
-					flex: 1;
-					overflow: hidden;
-					.personal-edit-safe-item-left-label {
-						color: var(--el-text-color-regular);
-						margin-bottom: 5px;
-					}
-					.personal-edit-safe-item-left-value {
-						color: var(--el-text-color-secondary);
-						@include text-ellipsis(1);
-						margin-right: 15px;
-					}
-				}
-			}
-			&:last-of-type {
-				padding-bottom: 0;
-				border-bottom: none;
-			}
-		}
-	}
-}
-</style>

+ 0 - 66
Web/src/views/personal/mock.ts

@@ -1,66 +0,0 @@
-/**
- * 消息通知
- * @returns 返回模拟数据
- */
-export const newsInfoList: Array<object> = [
-	{
-		title: '[发布] 2021年02月28日发布基于 vue3.x + vite v1.0.0 版本',
-		date: '02/28',
-		link: 'https://gitee.com/lyt-top/vue-next-admin',
-	},
-	{
-		title: '[发布] 2021年04月15日发布 vue2.x + webpack 重构版本',
-		date: '04/15',
-		link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/',
-	},
-	{
-		title: '[重构] 2021年04月10日 重构 vue2.x + webpack v1.0.0 版本',
-		date: '04/10',
-		link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/',
-	},
-	{
-		title: '[预览] 2020年12月08日,基于 vue3.x 版本后台模板的预览',
-		date: '12/08',
-		link: 'http://lyt-top.gitee.io/vue-next-admin-preview/#/login',
-	},
-	{
-		title: '[预览] 2020年11月15日,基于 vue2.x 版本后台模板的预览',
-		date: '11/15',
-		link: 'https://lyt-top.gitee.io/vue-prev-admin-preview/#/login',
-	},
-];
-
-/**
- * 营销推荐
- * @returns 返回模拟数据
- */
-export const recommendList: Array<object> = [
-	{
-		title: '优惠券',
-		msg: '现金券、折扣券、营销必备',
-		icon: 'ele-Food',
-		bg: '#48D18D',
-		iconColor: '#64d89d',
-	},
-	{
-		title: '多人拼团',
-		msg: '社交电商、开辟流量',
-		icon: 'ele-ShoppingCart',
-		bg: '#F95959',
-		iconColor: '#F86C6B',
-	},
-	{
-		title: '分销中心',
-		msg: '轻松招募分销员,成功推广奖励',
-		icon: 'ele-School',
-		bg: '#8595F4',
-		iconColor: '#92A1F4',
-	},
-	{
-		title: '秒杀',
-		msg: '超低价抢购引导更多销量',
-		icon: 'ele-AlarmClock',
-		bg: '#FEBB50',
-		iconColor: '#FDC566',
-	},
-];

+ 361 - 0
Web/src/views/system/database/component/addTable.vue

@@ -0,0 +1,361 @@
+<template>
+	<div class="sys-dbTable-container">
+		<el-dialog v-model="isShowDialog" title="表编辑" draggable width="1400px">
+			<el-divider content-position="left">数据表信息</el-divider>
+			<el-form :model="ruleForm" ref="ruleFormRef" size="default" label-width="80px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="表名称" prop="tableName" :rules="[{ required: true, message: '名称不能为空', trigger: 'blur' }]">
+							<el-input v-model="ruleForm.tableName" placeholder="表名称" 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="description" :rules="[{ required: true, message: '描述不能为空', trigger: 'blur' }]">
+							<el-input v-model="ruleForm.description" placeholder="描述" clearable type="textarea" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<el-divider content-position="left">数据列信息</el-divider>
+			<el-table :data="tableData" style="width: 100%" max-height="400">
+				<el-table-column fixed prop="dbColumnName" label="字段名" width="150">
+					<template #default="scope">
+						<el-input v-model="scope.row.dbColumnName" autocomplete="off" />
+					</template>
+				</el-table-column>
+				<el-table-column prop="columnDescription" label="描述" width="200">
+					<template #default="scope">
+						<el-input v-model="scope.row.columnDescription" autocomplete="off" />
+					</template>
+				</el-table-column>
+				<el-table-column prop="isPrimarykey" label="主键" >
+					<template #default="scope">
+						<el-select v-model="scope.row.isPrimarykey" class="m-2" placeholder="Select">
+							<el-option v-for="item in isOrNotSelect()" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<!-- <el-switch v-model="scope.row.isPrimarykey" active-text="是" inactive-text="否" /> -->
+					</template>
+				</el-table-column>
+				<el-table-column prop="isIdentity" label="自增" >
+					<template #default="scope">
+						<el-select v-model="scope.row.isIdentity" class="m-2" placeholder="Select">
+							<el-option v-for="item in isOrNotSelect()" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<!-- <el-switch v-model="scope.row.isIdentity" active-text="是" inactive-text="否" /> -->
+					</template>
+				</el-table-column>
+				<el-table-column prop="dataType" label="类型" >
+					<template #default="scope">
+						<el-select v-model="scope.row.dataType" class="m-2" placeholder="Select">
+							<el-option v-for="item in apiTypeSelect()" :key="item.value" :label="item.value" :value="item.value" />
+						</el-select>
+					</template>
+				</el-table-column>
+				<el-table-column prop="isNullable" label="可空" >
+					<template #default="scope">
+						<el-select v-model="scope.row.isNullable" class="m-2" placeholder="Select">
+							<el-option v-for="item in isOrNotSelect()" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<!-- <el-switch v-model="scope.row.isNullable" active-text="是" inactive-text="否" /> -->
+					</template>
+				</el-table-column>
+				<el-table-column prop="length" label="长度" >
+					<template #default="scope">
+						<el-input-number v-model="scope.row.length" size="small" />
+					</template>
+				</el-table-column>
+				<el-table-column prop="decimalDigits" label="保留几位小数">
+					<template #default="scope">
+						<el-input-number v-model="scope.row.decimalDigits" size="small" />
+					</template>
+				</el-table-column>
+				<el-table-column fixed="right" label="操作" width="120">
+					<template #default="scope">
+						<el-button link type="primary" size="small" @click.prevent="deleteRow(scope.$index)"> Remove </el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<div style="text-align: center">
+				<el-button type="primary" text bg @click="addPrimaryColumn">新增主键字段</el-button>
+				<el-button type="primary" text bg @click="addColumn">新增普通字段</el-button>
+				<el-button type="primary" text bg @click="addTenantColumn">新增租户字段</el-button>
+				<el-button type="primary" text bg @click="addBaseColumn">新增基础字段</el-button>
+			</div>
+
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="cancel" size="default">取 消</el-button>
+					<el-button type="primary" @click="submit" size="default">确 定</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, defineComponent, getCurrentInstance, ref } from 'vue';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysDatabaseApi } from '/@/api-services/api';
+import { UpdateDbTableInput } from '/@/api-services/models';
+
+export default defineComponent({
+	name: 'sysEditTable',
+	components: {},
+	setup() {
+		var colIndex = 0;
+		/**
+		 *
+		 * @export
+		 * @interface EditRecordRow
+		 */
+		interface EditRecordRow {
+			columnDescription?: string | null;
+			dataType?: string | null;
+			dbColumnName?: string | null;
+			decimalDigits: number;
+			isIdentity: number;
+			isNullable: number;
+			isPrimarykey: number;
+			length: number;
+			key?: number;
+			editable?: boolean;
+			isNew: boolean;
+		}
+		const { proxy } = getCurrentInstance() as any;
+		const ruleFormRef = ref();
+		const tableData = reactive([]) as Array<EditRecordRow>;
+		const state = reactive({
+			isShowDialog: false,
+			ruleForm: {} as UpdateDbTableInput,
+		});
+		// 打开弹窗
+		const openDialog = (row: any) => {
+			state.ruleForm = row;
+			state.isShowDialog = true;
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			proxy.mittBus.emit('submitRefreshTable');
+			state.isShowDialog = false;
+		};
+		// 取消
+		const cancel = () => {
+			state.isShowDialog = false;
+		};
+		// 提交
+		const submit = () => {
+			ruleFormRef.value.validate(async (valid: boolean) => {
+				if (!valid) return;
+				await getAPI(SysDatabaseApi).sysDatabaseTableUpdatePost(state.ruleForm);
+				closeDialog();
+			});
+		};
+
+		const apiTypeSelect = () => {
+			return [
+				{
+					value: 'text',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'varchar',
+					hasLength: true,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'nvarchar',
+					hasLength: true,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'char',
+					hasLength: true,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'nchar',
+					hasLength: true,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'timestamp',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'int',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'smallint',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'tinyint',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'bigint',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'bit',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'decimal',
+					hasLength: true,
+					hasDecimalDigits: true,
+				},
+				{
+					value: 'datetime',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+			];
+		};
+
+		const isOrNotSelect = () => {
+			return [
+				{
+					label: '是',
+					value: 1
+				},
+				{
+					label: '否',
+					value: 0
+				}
+			];
+		};
+
+		const deleteRow = (index: number) => {
+			tableData.splice(index, 1);
+		};
+
+		function addPrimaryColumn() {
+			const addRow: EditRecordRow = {
+				columnDescription: '主键Id',
+				dataType: 'bigint',
+				dbColumnName: 'Id',
+				decimalDigits: 0,
+				isIdentity: 0,
+				isNullable: 0,
+				isPrimarykey: 1,
+				length: 0,
+				key: colIndex,
+				editable: true,
+				isNew: true,
+			};
+			tableData.push(addRow);
+			colIndex++;
+		}
+
+		function addColumn() {
+			const addRow: EditRecordRow = {
+				columnDescription: '',
+				dataType: '',
+				dbColumnName: '',
+				decimalDigits: 0,
+				isIdentity: 0,
+				isNullable: 1,
+				isPrimarykey: 0,
+				length: 0,
+				key: colIndex,
+				editable: true,
+				isNew: true,
+			};
+			tableData.push(addRow);
+			colIndex++;
+		}
+
+		function addTenantColumn() {
+			const addRow: EditRecordRow = {
+				columnDescription: '租户Id',
+				dataType: 'bigint',
+				dbColumnName: 'TenantId',
+				decimalDigits: 0,
+				isIdentity: 0,
+				isNullable: 1,
+				isPrimarykey: 0,
+				length: 0,
+				key: colIndex,
+				editable: true,
+				isNew: true,
+			};
+			tableData.push(addRow);
+			colIndex++;
+		}
+
+		function addBaseColumn() {
+			const fileds = [
+				{
+					dataType: 'datetime',
+					name: 'CreateTime',
+					desc: '创建时间',
+				},
+				{
+					dataType: 'datetime',
+					name: 'UpdateTime',
+					desc: '更新时间',
+				},
+				{
+					dataType: 'bigint',
+					name: 'CreateUserId',
+					desc: '创建者Id',
+				},
+				{
+					dataType: 'bigint',
+					name: 'UpdateUserId',
+					desc: '修改者Id',
+				},
+				{
+					dataType: 'bit',
+					name: 'IsDelete',
+					desc: '软删除',
+					isNullable: 0,
+				},
+			];
+
+			fileds.forEach((m: any) => {
+				tableData.push({
+					columnDescription: m.desc,
+					dataType: m.dataType,
+					dbColumnName: m.name,
+					decimalDigits: 0,
+					isIdentity: 0,
+					isNullable: m.isNullable === 0 ? 0 : 1,
+					isPrimarykey: 0,
+					length: m.length || 0,
+					key: colIndex,
+					editable: true,
+					isNew: true,
+				});
+				colIndex++;
+			});
+		}
+		return {
+			ruleFormRef,
+			openDialog,
+			closeDialog,
+			cancel,
+			submit,
+			...toRefs(state),
+			tableData,
+			deleteRow,
+			apiTypeSelect,
+			addPrimaryColumn,
+			addColumn,
+			addTenantColumn,
+			addBaseColumn,
+			isOrNotSelect
+		};
+	},
+});
+</script>

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

@@ -1,6 +1,7 @@
 <template>
 	<div class="sys-dbTable-container">
-		<el-dialog v-model="isShowDialog" title="表编辑" draggable width="600px">
+		<el-dialog v-model="isShowDialog" title="表编辑" draggable width="1400px">
+			<el-divider content-position="left">数据表信息</el-divider>
 			<el-form :model="ruleForm" ref="ruleFormRef" size="default" label-width="80px">
 				<el-row :gutter="35">
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
@@ -15,6 +16,72 @@
 					</el-col>
 				</el-row>
 			</el-form>
+			<el-divider content-position="left">数据列信息</el-divider>
+			<el-table :data="tableData" style="width: 100%" max-height="400">
+				<el-table-column fixed prop="dbColumnName" label="字段名" width="150">
+					<template #default="scope">
+						<el-input v-model="scope.row.dbColumnName" autocomplete="off" />
+					</template>
+				</el-table-column>
+				<el-table-column prop="columnDescription" label="描述" width="200">
+					<template #default="scope">
+						<el-input v-model="scope.row.columnDescription" autocomplete="off" />
+					</template>
+				</el-table-column>
+				<el-table-column prop="isPrimarykey" label="主键" >
+					<template #default="scope">
+						<el-select v-model="scope.row.isPrimarykey" class="m-2" placeholder="Select">
+							<el-option v-for="item in isOrNotSelect()" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<!-- <el-switch v-model="scope.row.isPrimarykey" active-text="是" inactive-text="否" /> -->
+					</template>
+				</el-table-column>
+				<el-table-column prop="isIdentity" label="自增" >
+					<template #default="scope">
+						<el-select v-model="scope.row.isIdentity" class="m-2" placeholder="Select">
+							<el-option v-for="item in isOrNotSelect()" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<!-- <el-switch v-model="scope.row.isIdentity" active-text="是" inactive-text="否" /> -->
+					</template>
+				</el-table-column>
+				<el-table-column prop="dataType" label="类型" >
+					<template #default="scope">
+						<el-select v-model="scope.row.dataType" class="m-2" placeholder="Select">
+							<el-option v-for="item in apiTypeSelect()" :key="item.value" :label="item.value" :value="item.value" />
+						</el-select>
+					</template>
+				</el-table-column>
+				<el-table-column prop="isNullable" label="可空" >
+					<template #default="scope">
+						<el-select v-model="scope.row.isNullable" class="m-2" placeholder="Select">
+							<el-option v-for="item in isOrNotSelect()" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<!-- <el-switch v-model="scope.row.isNullable" active-text="是" inactive-text="否" /> -->
+					</template>
+				</el-table-column>
+				<el-table-column prop="length" label="长度" >
+					<template #default="scope">
+						<el-input-number v-model="scope.row.length" size="small" />
+					</template>
+				</el-table-column>
+				<el-table-column prop="decimalDigits" label="保留几位小数">
+					<template #default="scope">
+						<el-input-number v-model="scope.row.decimalDigits" size="small" />
+					</template>
+				</el-table-column>
+				<el-table-column fixed="right" label="操作" width="120">
+					<template #default="scope">
+						<el-button link type="primary" size="small" @click.prevent="deleteRow(scope.$index)"> Remove </el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<div style="text-align: center">
+				<el-button type="primary" text bg @click="addPrimaryColumn">新增主键字段</el-button>
+				<el-button type="primary" text bg @click="addColumn">新增普通字段</el-button>
+				<el-button type="primary" text bg @click="addTenantColumn">新增租户字段</el-button>
+				<el-button type="primary" text bg @click="addBaseColumn">新增基础字段</el-button>
+			</div>
+
 			<template #footer>
 				<span class="dialog-footer">
 					<el-button @click="cancel" size="default">取 消</el-button>
@@ -36,8 +103,28 @@ export default defineComponent({
 	name: 'sysEditTable',
 	components: {},
 	setup() {
+		var colIndex = 0;
+		/**
+		 *
+		 * @export
+		 * @interface EditRecordRow
+		 */
+		interface EditRecordRow {
+			columnDescription?: string | null;
+			dataType?: string | null;
+			dbColumnName?: string | null;
+			decimalDigits: number;
+			isIdentity: number;
+			isNullable: number;
+			isPrimarykey: number;
+			length: number;
+			key?: number;
+			editable?: boolean;
+			isNew: boolean;
+		}
 		const { proxy } = getCurrentInstance() as any;
 		const ruleFormRef = ref();
+		const tableData = reactive([]) as Array<EditRecordRow>;
 		const state = reactive({
 			isShowDialog: false,
 			ruleForm: {} as UpdateDbTableInput,
@@ -64,6 +151,195 @@ export default defineComponent({
 				closeDialog();
 			});
 		};
+
+		const apiTypeSelect = () => {
+			return [
+				{
+					value: 'text',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'varchar',
+					hasLength: true,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'nvarchar',
+					hasLength: true,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'char',
+					hasLength: true,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'nchar',
+					hasLength: true,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'timestamp',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'int',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'smallint',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'tinyint',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'bigint',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'bit',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+				{
+					value: 'decimal',
+					hasLength: true,
+					hasDecimalDigits: true,
+				},
+				{
+					value: 'datetime',
+					hasLength: false,
+					hasDecimalDigits: false,
+				},
+			];
+		};
+
+		const isOrNotSelect = () => {
+			return [
+				{
+					label: '是',
+					value: 1
+				},
+				{
+					label: '否',
+					value: 0
+				}
+			];
+		};
+
+		const deleteRow = (index: number) => {
+			tableData.splice(index, 1);
+		};
+
+		function addPrimaryColumn() {
+			const addRow: EditRecordRow = {
+				columnDescription: '主键Id',
+				dataType: 'bigint',
+				dbColumnName: 'Id',
+				decimalDigits: 0,
+				isIdentity: 0,
+				isNullable: 0,
+				isPrimarykey: 1,
+				length: 0,
+				key: colIndex,
+				editable: true,
+				isNew: true,
+			};
+			tableData.push(addRow);
+			colIndex++;
+		}
+
+		function addColumn() {
+			const addRow: EditRecordRow = {
+				columnDescription: '',
+				dataType: '',
+				dbColumnName: '',
+				decimalDigits: 0,
+				isIdentity: 0,
+				isNullable: 1,
+				isPrimarykey: 0,
+				length: 0,
+				key: colIndex,
+				editable: true,
+				isNew: true,
+			};
+			tableData.push(addRow);
+			colIndex++;
+		}
+
+		function addTenantColumn() {
+			const addRow: EditRecordRow = {
+				columnDescription: '租户Id',
+				dataType: 'bigint',
+				dbColumnName: 'TenantId',
+				decimalDigits: 0,
+				isIdentity: 0,
+				isNullable: 1,
+				isPrimarykey: 0,
+				length: 0,
+				key: colIndex,
+				editable: true,
+				isNew: true,
+			};
+			tableData.push(addRow);
+			colIndex++;
+		}
+
+		function addBaseColumn() {
+			const fileds = [
+				{
+					dataType: 'datetime',
+					name: 'CreateTime',
+					desc: '创建时间',
+				},
+				{
+					dataType: 'datetime',
+					name: 'UpdateTime',
+					desc: '更新时间',
+				},
+				{
+					dataType: 'bigint',
+					name: 'CreateUserId',
+					desc: '创建者Id',
+				},
+				{
+					dataType: 'bigint',
+					name: 'UpdateUserId',
+					desc: '修改者Id',
+				},
+				{
+					dataType: 'bit',
+					name: 'IsDelete',
+					desc: '软删除',
+					isNullable: 0,
+				},
+			];
+
+			fileds.forEach((m: any) => {
+				tableData.push({
+					columnDescription: m.desc,
+					dataType: m.dataType,
+					dbColumnName: m.name,
+					decimalDigits: 0,
+					isIdentity: 0,
+					isNullable: m.isNullable === 0 ? 0 : 1,
+					isPrimarykey: 0,
+					length: m.length || 0,
+					key: colIndex,
+					editable: true,
+					isNew: true,
+				});
+				colIndex++;
+			});
+		}
 		return {
 			ruleFormRef,
 			openDialog,
@@ -71,6 +347,14 @@ export default defineComponent({
 			cancel,
 			submit,
 			...toRefs(state),
+			tableData,
+			deleteRow,
+			apiTypeSelect,
+			addPrimaryColumn,
+			addColumn,
+			addTenantColumn,
+			addBaseColumn,
+			isOrNotSelect
 		};
 	},
 });

+ 17 - 7
Web/src/views/system/database/index.vue

@@ -63,13 +63,14 @@
 </template>
 
 <script lang="ts">
-import { toRefs, reactive, onMounted, ref, defineComponent, onUnmounted, getCurrentInstance } from 'vue';
+import { toRefs, reactive, onMounted, ref, defineComponent, onUnmounted, getCurrentInstance, toRaw } from 'vue';
 import { ElMessageBox, ElMessage } from 'element-plus';
 import EditTable from '/@/views/system/database/component/editTable.vue';
 import EditColumn from '/@/views/system/database/component/editColumn.vue';
 
 import { getAPI } from '/@/utils/axios-utils';
 import { SysDatabaseApi } from '/@/api-services/api';
+import { getTableInfoList, getColumnInfoList } from '/@/api/system/admin';
 import { DbColumnOutput, DbTableInfo } from '/@/api-services/models';
 
 export default defineComponent({
@@ -96,6 +97,7 @@ export default defineComponent({
 		onMounted(async () => {
 			state.loading = true;
 			var res = await getAPI(SysDatabaseApi).sysDatabaseListGet();
+			// var res = await getTableInfoList({});
 			state.dbData = res.data.result;
 			state.loading = false;
 
@@ -116,16 +118,24 @@ export default defineComponent({
 			state.columnData = [];
 
 			state.loading = true;
-			var res = await getAPI(SysDatabaseApi).sysDatabaseTableListGet(state.configId);
-			state.tableData = res.data.result ?? [];
-			state.loading = false;
+			getTableInfoList({ configId: state.configId }).then((res) => {
+				state.tableData = res.data.result ?? [];
+				state.loading = false;
+			});
+			// var res = await getAPI(SysDatabaseApi).sysDatabaseTableListGet(state.configId);
+			// state.tableData = res.data.result ?? [];
+			// state.loading = false;
 		};
 		// 列查询操作
 		const handleQueryColunm = async () => {
 			state.loading1 = true;
-			var res = await getAPI(SysDatabaseApi).sysDatabaseColumnListGet(state.tableName, state.configId);
-			state.columnData = res.data.result ?? [];
-			state.loading1 = false;
+			// var res = await getAPI(SysDatabaseApi).sysDatabaseColumnListGet(state.tableName, state.configId);
+			// state.columnData = res.data.result ?? [];
+			// state.loading1 = false;
+			getColumnInfoList({ tableName: state.tableName, configId: state.configId }).then((res) => {
+				state.columnData = res.data.result ?? [];
+				state.loading1 = false;
+			});
 		};
 		// 打开表编辑页面
 		const openEditTable = () => {

+ 1 - 1
Web/src/views/system/menu/index.vue

@@ -14,7 +14,7 @@
 				</el-form-item>
 				<el-form-item>
 					<el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
-					<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysMenu:page'"> 查询 </el-button>
+					<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysMenu:list'"> 查询 </el-button>
 					<el-button icon="ele-Plus" @click="openAddMenu" v-auth="'sysMenu:add'"> 新增 </el-button>
 				</el-form-item>
 			</el-form>

+ 1 - 1
Web/src/views/system/onlineUser/index.vue

@@ -73,7 +73,7 @@ const state = reactive({
 
 // 初始化SignalR对象
 const connection = new SignalR.HubConnectionBuilder()
-	.configureLogging(SignalR.LogLevel.Information)
+	//.configureLogging(SignalR.LogLevel.Information)
 	.withUrl(`${import.meta.env.VITE_API_URL}/hubs/onlineUser?access_token=${getToken()}`)
 	.withAutomaticReconnect({
 		nextRetryDelayInMilliseconds: (a) => {

+ 1 - 1
Web/src/views/system/org/index.vue

@@ -16,7 +16,7 @@
 						</el-form-item>
 						<el-form-item>
 							<el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
-							<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysOrg:page'"> 查询 </el-button>
+							<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysOrg:list'"> 查询 </el-button>
 							<el-button icon="ele-Plus" @click="openAddOrg" v-auth="'sysOrg:add'"> 新增 </el-button>
 						</el-form-item>
 					</el-form>

+ 0 - 81
Web/src/views/system/password/index.vue

@@ -1,81 +0,0 @@
-<template>
-	<div class="sys-password-container">
-		<NoticeBar text="账号密码修改,请慎重操作!" leftIcon="iconfont icon-tongzhi2" background="var(--el-color-primary-light-9)" color="var(--el-color-primary)" />
-		<el-card shadow="hover" header="修改当前账号密码" class="mt8">
-			<el-form ref="ruleFormRef" :model="ruleForm" status-icon label-width="80px">
-				<el-form-item label="当前密码" prop="passwordOld" :rules="[{ required: true, message: '当前密码不能为空', trigger: 'blur' }]">
-					<el-input v-model="ruleForm.passwordOld" type="password" autocomplete="off" />
-				</el-form-item>
-				<el-form-item label="新密码" prop="passwordNew" :rules="[{ required: true, message: '新密码不能为空', trigger: 'blur' }]">
-					<el-input v-model="ruleForm.passwordNew" type="password" autocomplete="off" />
-				</el-form-item>
-				<el-form-item label="确认密码" prop="passwordNew2" :rules="[{ validator: validatePassword, required: true, trigger: 'blur' }]">
-					<el-input type="password" />
-				</el-form-item>
-				<el-form-item>
-					<el-button icon="ele-Refresh" @click="reset" size="default">重 置</el-button>
-					<el-button icon="ele-Select" type="primary" @click="submit" size="default" v-auth="'sysUser:changePwd'">确 定</el-button>
-				</el-form-item>
-			</el-form>
-		</el-card>
-	</div>
-</template>
-
-<script lang="ts">
-import { reactive, toRefs, defineComponent, ref } from 'vue';
-import NoticeBar from '/@/components/noticeBar/index.vue';
-import { ElMessageBox } from 'element-plus';
-import { Session } from '/@/utils/storage';
-
-import { getAPI } from '/@/utils/axios-utils';
-import { SysUserApi } from '/@/api-services/api';
-import { ChangePwdInput } from '/@/api-services/models';
-
-export default defineComponent({
-	name: 'sysPassword',
-	components: { NoticeBar },
-	setup() {
-		const ruleFormRef = ref();
-
-		const validatePassword = (rule: any, value: any, callback: any) => {
-			if (value != state.ruleForm.passwordNew) {
-				callback(new Error('两次密码不一致!'));
-			} else {
-				callback();
-			}
-		};
-		const state = reactive({
-			ruleForm: {} as ChangePwdInput,
-		});
-		// 重置
-		const reset = () => {
-			state.ruleForm.passwordOld = '';
-			state.ruleForm.passwordNew = '';
-		};
-		// 提交
-		const submit = () => {
-			ruleFormRef.value.validate(async (valid: boolean) => {
-				if (!valid) return;
-				await getAPI(SysUserApi).sysUserChangePwdPost(state.ruleForm);
-				// 退出系统
-				ElMessageBox.confirm('密码已修改,是否重新登录系统?', '提示', {
-					confirmButtonText: '确定',
-					cancelButtonText: '取消',
-					type: 'warning',
-				}).then(async () => {
-					// 清除缓存
-					Session.clear();
-					window.location.reload();
-				});
-			});
-		};
-		return {
-			ruleFormRef,
-			reset,
-			submit,
-			validatePassword,
-			...toRefs(state),
-		};
-	},
-});
-</script>

+ 96 - 0
Web/src/views/system/user/component/orgTree.vue

@@ -0,0 +1,96 @@
+<template>
+	<div v-loading="state.loading">
+		<div style="text-align: right">
+			<div style="margin-right: 10px"><el-switch v-model="state.horizontal"></el-switch> 横向/纵向</div>
+		</div>
+		<div style="height: 500px">
+			<vue3-tree-org
+				:data="state.orgData"
+				:props="{ id: 'id', pid: 'pid', label: 'name', expand: 'expand', children: 'children' }"
+				:label-style="state.style"
+				:default-expand-level="100"
+				:horizontal="state.horizontal"
+				:collapsable="state.collapsable"
+				:only-one-node="state.onlyOneNode"
+				:clone-node-drag="state.cloneNodeDrag"
+				:node-draggable="state.nodeDraggable"
+			>
+				<template v-slot="{ node }">
+					<div class="tree-org-node__text node-label">
+						<div class="node-title">{{ node.label }}</div>
+						<div class="node-id">编码:{{ node.id }}</div>
+					</div>
+				</template>
+				<template v-slot:expand="{ node }">
+					<div>{{ node.children.length }}</div>
+				</template>
+			</vue3-tree-org>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup name="orgTree">
+import { onMounted, reactive } from 'vue';
+
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '/@/stores/userInfo';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysOrgApi } from '/@/api-services/api';
+
+const stores = useUserInfo();
+const { userInfos } = storeToRefs(stores);
+
+const currentNodeStyle = { color: '#FFFFFF', background: '#3B3B3B' };
+
+const state = reactive({
+	loading: false,
+	orgData: [] as any,
+	horizontal: false,
+	collapsable: true,
+	onlyOneNode: false,
+	cloneNodeDrag: false,
+	nodeDraggable: false,
+	style: {
+		background: '#FF5C00',
+		color: '#FFFFFF',
+	},
+});
+
+onMounted(async () => {
+	state.loading = true;
+	var res = await getAPI(SysOrgApi).sysOrgListGet(0);
+	var d = res.data.result ?? [];
+	state.orgData = d[0] ?? []; // 默认第一个树分支
+	if (state.orgData.id == userInfos.value.orgId) state.orgData.style = currentNodeStyle;
+	else InitOrg(state.orgData.children, userInfos.value.orgId);
+	state.loading = false;
+});
+
+// 递归遍历
+const InitOrg = (orgData: any, id: any) => {
+	orgData.forEach(function (u: any) {
+		if (u.id == id) {
+			u.style = currentNodeStyle;
+			return;
+		} else if (u.children && u.children.length > 0) {
+			InitOrg(u.children, id);
+		}
+	});
+};
+</script>
+
+<style lang="scss" scoped>
+.tree-org-node__text {
+	// text-align: left;
+	font-size: 14px;
+	.node-title {
+		padding-bottom: 8px;
+		margin-bottom: 8px;
+		border-bottom: 1px solid currentColor;
+	}
+	.node-id {
+		font-size: 10px;
+	}
+}
+</style>

+ 320 - 0
Web/src/views/system/user/component/userCenter.vue

@@ -0,0 +1,320 @@
+<template>
+	<div class="sys-userCenter-container">
+		<el-row :gutter="8" style="width: 100%">
+			<el-col :span="8" :xs="24">
+				<el-card shadow="hover">
+					<div class="account-center-avatarHolder">
+						<el-upload class="h100" ref="uploadAvatarRef" action="" :limit="1" :show-file-list="false" :auto-upload="false" :on-change="uploadAvatarFile" accept=".jpg,.png,.bmp,.gif">
+							<el-avatar :size="100" :src="userInfos.avatar" />
+						</el-upload>
+						<div class="username">{{ userInfos.realName }}</div>
+					</div>
+					<div class="account-center-org">
+						<p>
+							<el-icon><ele-School /></el-icon> <span>{{ userInfos.orgName ?? '超级管理员' }}</span>
+						</p>
+						<p>
+							<el-icon><ele-Mug /></el-icon> <span>{{ userInfos.posName ?? '超级管理员' }}</span>
+						</p>
+						<p>
+							<el-icon><ele-LocationInformation /></el-icon> <span>{{ userInfos.address ?? '家庭住址' }}</span>
+						</p>
+					</div>
+					<div class="image-signature">
+						<el-image :src="userInfos.signature" fit="contain" alt="电子签名" lazy style="width: 100%; height: 100%"> </el-image>
+					</div>
+					<el-button icon="ele-Edit" type="primary" @click="openSignDialog" v-auth="'sysUser:signature'"> 电子签名 </el-button>
+					<el-upload ref="uploadSignRef" action="" accept=".png" :limit="1" :show-file-list="false" :auto-upload="false" :on-change="uploadSignFile" style="display: inline-block; margin-left: 12px">
+						<el-button icon="ele-UploadFilled" style="display: inline-block">上传手写签名</el-button>
+					</el-upload>
+				</el-card>
+			</el-col>
+
+			<el-col :span="16" :xs="24" v-loading="loading">
+				<el-card shadow="hover">
+					<el-tabs>
+						<el-tab-pane label="基础信息">
+							<el-form :model="ruleFormBase" ref="ruleFormBaseRef" size="default" label-width="100px">
+								<el-row :gutter="35">
+									<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+										<el-form-item label="真实姓名" prop="realName" :rules="[{ required: true, message: '真实姓名不能为空', trigger: 'blur' }]">
+											<el-input v-model="ruleFormBase.realName" placeholder="真实姓名" 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="nickName">
+											<el-input v-model="ruleFormBase.nickName" placeholder="昵称" 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="phone" :rules="[{ required: true, message: '手机号码不能为空', trigger: 'blur' }]">
+											<el-input v-model="ruleFormBase.phone" placeholder="手机号码" 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="email">
+											<el-input v-model="ruleFormBase.email" placeholder="邮箱" 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="birthday" :rules="[{ required: true, message: '出生日期不能为空', trigger: 'blur' }]">
+											<el-date-picker v-model="ruleFormBase.birthday" type="date" placeholder="出生日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 100%" />
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+										<el-form-item label="性别" prop="sex">
+											<el-radio-group v-model="ruleFormBase.sex">
+												<el-radio :label="1">男</el-radio>
+												<el-radio :label="2">女</el-radio>
+											</el-radio-group>
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+										<el-form-item label="地址" prop="nation">
+											<el-input v-model="ruleFormBase.address" placeholder="地址" clearable type="textarea" />
+										</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="ruleFormBase.remark" placeholder="备注" clearable type="textarea" />
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+										<el-form-item>
+											<el-button icon="ele-SuccessFilled" type="primary" @click="submitUserBase" v-auth="'sysUser:updateBase'"> 保存基本信息 </el-button>
+										</el-form-item>
+									</el-col>
+								</el-row>
+							</el-form>
+						</el-tab-pane>
+						<el-tab-pane label="组织机构">
+							<OrgTree ref="orgTreeRef" />
+						</el-tab-pane>
+						<el-tab-pane label="修改密码">
+							<el-form ref="ruleFormPasswordRef" :model="ruleFormPassword" status-icon label-width="80px">
+								<el-form-item label="当前密码" prop="passwordOld" :rules="[{ required: true, message: '当前密码不能为空', trigger: 'blur' }]">
+									<el-input v-model="ruleFormPassword.passwordOld" type="password" autocomplete="off" />
+								</el-form-item>
+								<el-form-item label="新密码" prop="passwordNew" :rules="[{ required: true, message: '新密码不能为空', trigger: 'blur' }]">
+									<el-input v-model="ruleFormPassword.passwordNew" type="password" autocomplete="off" />
+								</el-form-item>
+								<el-form-item label="确认密码" prop="passwordNew2" :rules="[{ validator: validatePassword, required: true, trigger: 'blur' }]">
+									<el-input type="password" />
+								</el-form-item>
+								<el-form-item>
+									<el-button icon="ele-Refresh" @click="resetPassword" size="default">重 置</el-button>
+									<el-button icon="ele-SuccessFilled" type="primary" @click="submitPassword" size="default" v-auth="'sysUser:changePwd'">确 定</el-button>
+								</el-form-item>
+							</el-form>
+						</el-tab-pane>
+					</el-tabs>
+				</el-card>
+			</el-col>
+		</el-row>
+
+		<el-dialog title="电子签名" v-model="signDialogVisible" draggable width="600px">
+			<div style="border: 1px dashed gray; width: 100%; height: 250px">
+				<VueSignaturePad ref="signaturePadRef" :options="signOptions" />
+			</div>
+			<div style="margin-top: 10px">
+				<div style="display: inline">画笔粗细:<el-input-number v-model="signOptions.minWidth" :min="0.5" :max="2.5" :step="0.1" size="small" /></div>
+				<div style="display: inline; margin-left: 30px">画笔颜色:<el-color-picker v-model="signOptions.penColor" color-format="hex" size="default"> </el-color-picker></div>
+			</div>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="unDoSign">撤销</el-button>
+					<el-button @click="clearSign">清屏</el-button>
+					<el-button type="primary" @click="saveUploadSign">保存</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent, ref, onMounted, watch } from 'vue';
+import { storeToRefs } from 'pinia';
+import { ElMessageBox, UploadInstance } from 'element-plus';
+import { useUserInfo } from '/@/stores/userInfo';
+import { Session } from '/@/utils/storage';
+import { base64ToFile } from '/@/utils/base64Conver';
+import OrgTree from '/@/views/system/user/component/orgTree.vue';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysFileApi, SysUserApi } from '/@/api-services/api';
+import { ChangePwdInput, SysUser } from '/@/api-services/models';
+
+export default defineComponent({
+	name: 'sysUserCenter',
+	components: { OrgTree },
+	setup() {
+		const stores = useUserInfo();
+		const { userInfos } = storeToRefs(stores);
+		const uploadSignRef = ref<UploadInstance>();
+		const uploadAvatarRef = ref<UploadInstance>();
+		const signaturePadRef = ref();
+		const ruleFormBaseRef = ref();
+		const ruleFormPasswordRef = ref();
+		const state = reactive({
+			loading: false,
+			signDialogVisible: false,
+			ruleFormBase: {} as SysUser,
+			ruleFormPassword: {} as ChangePwdInput,
+			signOptions: {
+				penColor: '#000000',
+				minWidth: 1.0,
+				onBegin: () => {
+					signaturePadRef.value.resizeCanvas();
+				},
+			},
+			signFileList: [] as any,
+		});
+		onMounted(async () => {
+			state.loading = true;
+			var res = await getAPI(SysUserApi).sysUserBaseGet();
+			state.ruleFormBase = res.data.result ?? { account: '' };
+			state.loading = false;
+		});
+		watch(state.signOptions, () => {
+			signaturePadRef.value.signaturePad.penColor = state.signOptions.penColor;
+			signaturePadRef.value.signaturePad.minWidth = state.signOptions.minWidth;
+		});
+		// 打开电子签名页面
+		const openSignDialog = () => {
+			state.signDialogVisible = true;
+		};
+		// 保存并上传电子签名
+		const saveUploadSign = async () => {
+			const { isEmpty, data } = signaturePadRef.value.saveSignature();
+			if (isEmpty) return;
+
+			var res = await getAPI(SysFileApi).sysFileUploadSignaturePostForm(base64ToFile(data, userInfos.value.account + '.png'));
+			userInfos.value.signature = res.data.result?.url + '';
+
+			clearSign();
+			state.signDialogVisible = false;
+		};
+		// 撤销电子签名
+		const unDoSign = () => {
+			signaturePadRef.value.undoSignature();
+			console.log(signaturePadRef.value.options);
+		};
+		// 清空电子签名
+		const clearSign = () => {
+			signaturePadRef.value.clearSignature();
+		};
+		// 上传手写电子签名
+		const uploadSignFile = async (file: any) => {
+			var res = await getAPI(SysFileApi).sysFileUploadSignaturePostForm(file.raw);
+			userInfos.value.signature = res.data.result?.url + '';
+		};
+		// 获得电子签名文件列表
+		const handleChangeSignFile = (_file: any, fileList: []) => {
+			state.signFileList = fileList;
+		};
+		// 上传头像文件
+		const uploadAvatarFile = async (file: any) => {
+			var res = await getAPI(SysFileApi).sysFileUploadAvatarPostForm(file.raw);
+			userInfos.value.avatar = res.data.result?.url + '';
+			uploadAvatarRef.value?.clearFiles();
+		};
+		// 密码提交
+		const submitUserBase = () => {
+			ruleFormBaseRef.value.validate(async (valid: boolean) => {
+				if (!valid) return;
+				ElMessageBox.confirm('确定修改个人基础信息?', '提示', {
+					confirmButtonText: '确定',
+					cancelButtonText: '取消',
+					type: 'warning',
+				}).then(async () => {
+					await getAPI(SysUserApi).sysUserBasePost(state.ruleFormBase);
+				});
+			});
+		};
+		// 密码验证
+		const validatePassword = (_rule: any, value: any, callback: any) => {
+			if (value != state.ruleFormPassword.passwordNew) {
+				callback(new Error('两次密码不一致!'));
+			} else {
+				callback();
+			}
+		};
+		// 密码重置
+		const resetPassword = () => {
+			state.ruleFormPassword.passwordOld = '';
+			state.ruleFormPassword.passwordNew = '';
+		};
+		// 密码提交
+		const submitPassword = () => {
+			ruleFormPasswordRef.value.validate(async (valid: boolean) => {
+				if (!valid) return;
+				await getAPI(SysUserApi).sysUserChangePwdPost(state.ruleFormPassword);
+				// 退出系统
+				ElMessageBox.confirm('密码已修改,是否重新登录系统?', '提示', {
+					confirmButtonText: '确定',
+					cancelButtonText: '取消',
+					type: 'warning',
+				}).then(async () => {
+					// 清除缓存
+					Session.clear();
+					window.location.reload();
+				});
+			});
+		};
+		return {
+			userInfos,
+			uploadSignRef,
+			uploadAvatarRef,
+			signaturePadRef,
+			ruleFormBaseRef,
+			ruleFormPasswordRef,
+			uploadAvatarFile,
+			openSignDialog,
+			saveUploadSign,
+			unDoSign,
+			clearSign,
+			uploadSignFile,
+			handleChangeSignFile,
+			validatePassword,
+			resetPassword,
+			submitPassword,
+			submitUserBase,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.account-center-avatarHolder {
+	text-align: center;
+	margin-bottom: 24px;
+
+	.username {
+		font-size: 20px;
+		line-height: 28px;
+		font-weight: 500;
+		margin-bottom: 4px;
+	}
+}
+.account-center-org {
+	margin-bottom: 8px;
+	position: relative;
+	p {
+		margin-top: 10px;
+	}
+	span {
+		padding-left: 10px;
+	}
+}
+
+.image-signature {
+	margin-top: 20px;
+	margin-bottom: 10px;
+	width: 100%;
+	height: 150px;
+	text-align: center;
+	vertical-align: middle;
+	border: solid 1px var(--el-border-color);
+}
+</style>

+ 6 - 2
Web/src/views/system/user/index.vue

@@ -201,8 +201,12 @@ export default defineComponent({
 			handleQuery();
 		};
 		// 修改状态
-		const changeStatus = async (row: any) => {
-			await getAPI(SysUserApi).sysUserSetStatusPost({ id: row.id, status: row.status });
+		const changeStatus = (row: any) => {
+			getAPI(SysUserApi)
+				.sysUserSetStatusPost({ id: row.id, status: row.status })
+				.catch(() => {
+					row.status = row.status == 1 ? 2 : 1;
+				});
 		};
 		// 重置密码
 		const resetUserPwd = async (row: any) => {

+ 6 - 0
vue-next-admin/.vscode/settings.json

@@ -0,0 +1,6 @@
+{
+    "i18n-ally.localesPaths": [
+        "src/i18n",
+        "src/i18n/lang"
+    ]
+}

Some files were not shown because too many files changed in this diff