Просмотр исходного кода

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

ccjungle 1 год назад
Родитель
Сommit
91b68d9d44
89 измененных файлов с 1402 добавлено и 376 удалено
  1. 2 1
      Admin.NET/Admin.NET.Application/Configuration/App.json
  2. 2 1
      Admin.NET/Admin.NET.Application/Configuration/Captcha.json
  3. 1 1
      Admin.NET/Admin.NET.Application/Configuration/Upload.json
  4. 1 1
      Admin.NET/Admin.NET.Application/Configuration/Wechat.json
  5. 14 14
      Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
  6. 3 3
      Admin.NET/Admin.NET.Core/Entity/EntityBase.cs
  7. 8 1
      Admin.NET/Admin.NET.Core/Entity/SysFile.cs
  8. 350 0
      Admin.NET/Admin.NET.Core/Enum/NationEnum.cs
  9. 3 6
      Admin.NET/Admin.NET.Core/EventBus/EventHandlerMonitor.cs
  10. 20 0
      Admin.NET/Admin.NET.Core/EventBus/RedisQueue.cs
  11. 1 1
      Admin.NET/Admin.NET.Core/EventBus/RetryEventHandlerExecutor.cs
  12. 5 4
      Admin.NET/Admin.NET.Core/Extension/RepositoryExtension.cs
  13. 1 0
      Admin.NET/Admin.NET.Core/Hub/OnlineUserHub.cs
  14. 1 1
      Admin.NET/Admin.NET.Core/Logging/LoggingSetup.cs
  15. 2 2
      Admin.NET/Admin.NET.Core/SeedData/SysConfigSeedData.cs
  16. 2 1
      Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs
  17. 6 17
      Admin.NET/Admin.NET.Core/SeedData/SysOrgSeedData.cs
  18. 32 12
      Admin.NET/Admin.NET.Core/SeedData/SysRoleMenuSeedData.cs
  19. 1 1
      Admin.NET/Admin.NET.Core/SeedData/SysTenantSeedData.cs
  20. 5 5
      Admin.NET/Admin.NET.Core/SeedData/SysUserSeedData.cs
  21. 4 1
      Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs
  22. 33 0
      Admin.NET/Admin.NET.Core/Service/Cache/SysCacheService.cs
  23. 2 2
      Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenService.cs
  24. 1 1
      Admin.NET/Admin.NET.Core/Service/Common/SysProcService.cs
  25. 1 1
      Admin.NET/Admin.NET.Core/Service/Config/SysConfigService.cs
  26. 0 1
      Admin.NET/Admin.NET.Core/Service/Const/SysConstService.cs
  27. 2 3
      Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs
  28. 0 1
      Admin.NET/Admin.NET.Core/Service/Dict/SysDictDataService.cs
  29. 0 4
      Admin.NET/Admin.NET.Core/Service/Dict/SysDictTypeService.cs
  30. 0 1
      Admin.NET/Admin.NET.Core/Service/Enum/SysEnumService.cs
  31. 24 0
      Admin.NET/Admin.NET.Core/Service/File/Dto/FileInput.cs
  32. 32 40
      Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs
  33. 0 0
      Admin.NET/Admin.NET.Core/Service/Log/Dto/ExportLogDto.cs
  34. 0 0
      Admin.NET/Admin.NET.Core/Service/Log/Dto/LogInput.cs
  35. 0 0
      Admin.NET/Admin.NET.Core/Service/Log/Dto/LogVisOutput.cs
  36. 0 0
      Admin.NET/Admin.NET.Core/Service/Log/SysLogDiffService.cs
  37. 0 0
      Admin.NET/Admin.NET.Core/Service/Log/SysLogExService.cs
  38. 0 0
      Admin.NET/Admin.NET.Core/Service/Log/SysLogOpService.cs
  39. 0 0
      Admin.NET/Admin.NET.Core/Service/Log/SysLogVisService.cs
  40. 0 1
      Admin.NET/Admin.NET.Core/Service/Menu/SysMenuService.cs
  41. 5 4
      Admin.NET/Admin.NET.Core/Service/Tenant/SysTenantService.cs
  42. 5 0
      Admin.NET/Admin.NET.Core/Service/User/SysUserMenuService.cs
  43. 12 1
      Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarFilter.cs
  44. 1 1
      Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarRepository.cs
  45. 18 3
      Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs
  46. 2 2
      Admin.NET/Admin.NET.Core/Util/ComputerUtil.cs
  47. 3 3
      Admin.NET/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj
  48. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Dto.cs.vm
  49. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Entity.cs.vm
  50. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Input.cs.vm
  51. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Manage.js.vm
  52. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Output.cs.vm
  53. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/SeedData.cs.vm
  54. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Service.cs.vm
  55. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/data.data.ts.vm
  56. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/dataModal.vue.vm
  57. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/editDialog.vue.vm
  58. 6 3
      Admin.NET/Admin.NET.Web.Entry/wwwroot/template/index.vue.vm
  59. 0 0
      Admin.NET/Admin.NET.Web.Entry/wwwroot/upload/logo.png
  60. 7 1
      Web/.env
  61. 23 23
      Web/package.json
  62. 3 0
      Web/public/print-lock.css
  63. 187 0
      Web/src/api-services/apis/sys-cache-api.ts
  64. 138 11
      Web/src/api-services/apis/sys-file-api.ts
  65. 69 0
      Web/src/api-services/models/admin-result-idisposable.ts
  66. 8 0
      Web/src/api-services/models/file-input.ts
  67. 8 0
      Web/src/api-services/models/file-output.ts
  68. 22 0
      Web/src/api-services/models/idisposable.ts
  69. 2 0
      Web/src/api-services/models/index.ts
  70. 8 0
      Web/src/api-services/models/sys-file-upload-file-body.ts
  71. 32 0
      Web/src/api-services/models/sys-file.ts
  72. 8 0
      Web/src/api-services/models/upload-file-from-base64-input.ts
  73. 14 9
      Web/src/layout/navBars/topBar/user.vue
  74. 12 9
      Web/src/main.ts
  75. 1 1
      Web/src/stores/userInfo.ts
  76. 1 1
      Web/src/views/home/widgets/components/about.vue
  77. 5 1
      Web/src/views/home/widgets/components/commit.vue
  78. 19 23
      Web/src/views/home/widgets/components/myapp.vue
  79. 8 11
      Web/src/views/home/widgets/components/schedule.vue
  80. 1 1
      Web/src/views/home/widgets/components/version.vue
  81. 1 1
      Web/src/views/home/widgets/components/welcome.vue
  82. 124 122
      Web/src/views/home/widgets/index.vue
  83. 5 4
      Web/src/views/login/component/account.vue
  84. 25 4
      Web/src/views/system/cache/index.vue
  85. 13 2
      Web/src/views/system/dict/index.vue
  86. 9 1
      Web/src/views/system/file/component/editSysfile.vue
  87. 14 2
      Web/src/views/system/file/index.vue
  88. 12 2
      Web/src/views/system/menu/index.vue
  89. 12 2
      Web/src/views/system/org/index.vue

+ 2 - 1
Admin.NET/Admin.NET.Application/Configuration/App.json

@@ -1,4 +1,4 @@
-{
+{
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
   "Urls": "http://*:5005", // 配置默认端口
@@ -46,6 +46,7 @@
     "PasswordStrengthValidation": "(?=^.{6,16}$)(?=.*\\d)(?=.*\\W+)(?=.*[A-Z])(?=.*[a-z])(?!.*\\n).*$", // 密码强度验证正则表达式,必须须包含大小写字母、数字和特殊字符的组合,长度在6-16之间
     "PasswordStrengthValidationMsg": "密码必须包含大小写字母、数字和特殊字符的组合,长度在6-16之间", // 密码强度验证消息提示
     "CryptoType": "SM2", // 密码加密算法:MD5、SM2、SM4
+    // 新业务系统记得改密匙,通过接口(http://localhost:5005/api/sysCommon/smKeyPair)获取。记得同步修改前端公钥配置:VITE_SM_PUBLIC_KEY
     "PublicKey": "0484C7466D950E120E5ECE5DD85D0C90EAA85081A3A2BD7C57AE6DC822EFCCBD66620C67B0103FC8DD280E36C3B282977B722AAEC3C56518EDCEBAFB72C5A05312", // 公钥
     "PrivateKey": "8EDB615B1D48B8BE188FC0F18EC08A41DF50EA731FA28BF409E6552809E3A111" // 私钥
   }

+ 2 - 1
Admin.NET/Admin.NET.Application/Configuration/Captcha.json

@@ -1,6 +1,7 @@
-{
+{
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
+  // Lazy.Captcha.Core 组件详细文档(https://api.gitee.com/pojianbing/lazy-captcha/)
   "CaptchaOptions": {
     "CaptchaType": 10, // 验证码类型0、1、2、3、4、5、6、7、8、9、10、11
     "CodeLength": 1, // 验证码长度, 要放在CaptchaType设置后  当类型为算术表达式时,长度代表操作的个数, 例如2

+ 1 - 1
Admin.NET/Admin.NET.Application/Configuration/Upload.json

@@ -2,7 +2,7 @@
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
   "Upload": {
-    "Path": "Upload/{yyyy}/{MM}/{dd}", // 文件上传目录
+    "Path": "upload/{yyyy}/{MM}/{dd}", // 文件上传目录
     "MaxSize": 51200, // 文件最大限制KB:1024*50
     "ContentType": [ "image/jpg", "image/png", "image/jpeg", "image/gif", "image/bmp", "text/plain", "application/pdf", "application/msword", "application/vnd.ms-excel", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "video/mp4", "application/wps-office.docx", "application/wps-office.xlsx", "application/wps-office.pptx", "application/vnd.android.package-archive" ],
     "EnableMd5": false // 启用文件MDF5验证-防止重复上传

+ 1 - 1
Admin.NET/Admin.NET.Application/Configuration/Wechat.json

@@ -12,7 +12,7 @@
     "WxOpenAppSecret": "",
     "WxToken": "", // 小程序消息推送中的令牌(Token)
     "WxEncodingAESKey": "", // 小程序消息推送中的消息加解密密钥(EncodingAESKey)
-    "QRImagePath": "" //小程序生成带参数二维码保存位置(绝对路径 eg: D:\\Web\\wwwroot\\Upload\\QRImage)
+    "QRImagePath": "" //小程序生成带参数二维码保存位置(绝对路径 eg: D:\\Web\\wwwroot\\upload\\QRImage)
   },
   // 微信支付
   "WechatPay": {

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

@@ -17,10 +17,10 @@
     <PackageReference Include="AngleSharp" Version="1.1.2" />
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
     <PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
-    <PackageReference Include="Elastic.Clients.Elasticsearch" Version="8.14.8" />
-    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.4.11" />
-    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.4.11" />
-    <PackageReference Include="Furion.Pure" Version="4.9.4.11" />
+    <PackageReference Include="Elastic.Clients.Elasticsearch" Version="8.15.1" />
+    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.5.4" />
+    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.5.4" />
+    <PackageReference Include="Furion.Pure" Version="4.9.5.4" />
     <PackageReference Include="IPTools.China" Version="1.6.0" />
     <PackageReference Include="IPTools.International" Version="1.6.0" />
     <PackageReference Include="Magicodes.IE.Excel" Version="2.7.5.1" />
@@ -33,12 +33,12 @@
     <PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
     <PackageReference Include="SixLabors.ImageSharp.Web" Version="3.1.2" />
     <PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.8" />
-    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.4.0" />
-    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.6.0" />
-    <PackageReference Include="SqlSugarCore" Version="5.1.4.166" />
+    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.5.0" />
+    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.7.0" />
+    <PackageReference Include="SqlSugarCore" Version="5.1.4.167" />
     <PackageReference Include="SSH.NET" Version="2024.1.0" />
     <PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.4" />
-    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1060" />
+    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1071" />
     <PackageReference Include="UAParser" Version="3.1.47" />
     <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
   </ItemGroup>
@@ -47,9 +47,9 @@
     <PackageReference Include="AspNet.Security.OAuth.Gitee" Version="6.0.15" />
     <PackageReference Include="AspNet.Security.OAuth.Weixin" Version="6.0.15" />
     <PackageReference Include="Lazy.Captcha.Core" Version="2.0.8" />
-    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.31" />
-    <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="6.0.31" />
-    <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="6.0.31" />
+    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.33" />
+    <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="6.0.33" />
+    <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="6.0.33" />
     <PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.1.9" />
   </ItemGroup>
 
@@ -57,9 +57,9 @@
     <PackageReference Include="AspNet.Security.OAuth.Gitee" Version="8.1.0" />
     <PackageReference Include="AspNet.Security.OAuth.Weixin" Version="8.1.0" />
     <PackageReference Include="Lazy.Captcha.Core" Version="2.0.7" />
-    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.7" />
-    <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.7" />
-    <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="8.0.7" />
+    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.8" />
+    <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.8" />
+    <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="8.0.8" />
     <PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.2.0" />
   </ItemGroup>
 

+ 3 - 3
Admin.NET/Admin.NET.Core/Entity/EntityBase.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -27,13 +27,13 @@ public abstract class EntityBase : EntityBaseId, IDeletedFilter
     /// <summary>
     /// 创建时间
     /// </summary>
-    [SugarColumn(ColumnDescription = "创建时间", IsNullable = true, IsOnlyIgnoreUpdate = true, InsertServerTime = true)]
+    [SugarColumn(ColumnDescription = "创建时间", IsNullable = true, IsOnlyIgnoreUpdate = true)]
     public virtual DateTime CreateTime { get; set; }
 
     /// <summary>
     /// 更新时间
     /// </summary>
-    [SugarColumn(ColumnDescription = "更新时间", IsOnlyIgnoreInsert = true, UpdateServerTime = true)]
+    [SugarColumn(ColumnDescription = "更新时间")]
     public virtual DateTime? UpdateTime { get; set; }
 
     /// <summary>

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

@@ -12,7 +12,7 @@ namespace Admin.NET.Core;
 [SugarTable(null, "系统文件表")]
 [SysTable]
 [SugarIndex("index_{table}_F", nameof(FileName), OrderByType.Asc)]
-public partial class SysFile : EntityBase
+public partial class SysFile : EntityTenantBaseData
 {
     /// <summary>
     /// 提供者
@@ -101,4 +101,11 @@ public partial class SysFile : EntityBase
     [SugarColumn(ColumnDescription = "文件类别", Length = 128)]
     [MaxLength(128)]
     public string? FileType { get; set; }
+
+    /// <summary>
+    /// 是否公开
+    /// 若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否公开")]
+    public bool IsPublic { get; set; } = false;
 }

+ 350 - 0
Admin.NET/Admin.NET.Core/Enum/NationEnum.cs

@@ -0,0 +1,350 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 民族枚举
+/// </summary>
+[Description("民族枚举")]
+public enum NationEnum
+{
+    /// <summary>
+    /// 汉族
+    /// </summary>
+    [Description("汉族")]
+    HanZu = 01,
+
+    /// <summary>
+    /// 壮族
+    /// </summary>
+    [Description("壮族")]
+    ZhuangZu = 02,
+
+    /// <summary>
+    /// 满族
+    /// </summary>
+    [Description("满族")]
+    ManZu = 03,
+
+    /// <summary>
+    /// 回族
+    /// </summary>
+    [Description("回族")]
+    HuiZu = 04,
+
+    /// <summary>
+    /// 苗族
+    /// </summary>
+    [Description("苗族")]
+    MiaoZu = 05,
+
+    /// <summary>
+    /// 维吾尔族
+    /// </summary>
+    [Description("维吾尔族")]
+    WeiWuErZu = 06,
+
+    /// <summary>
+    /// 土家族
+    /// </summary>
+    [Description("土家族")]
+    TuJiaZu = 07,
+
+    /// <summary>
+    /// 彝族
+    /// </summary>
+    [Description("彝族")]
+    YiZu = 08,
+
+    /// <summary>
+    /// 蒙古族
+    /// </summary>
+    [Description("蒙古族")]
+    MengGuZu = 09,
+
+    /// <summary>
+    /// 藏族
+    /// </summary>
+    [Description("藏族")]
+    ZangZu = 10,
+
+    /// <summary>
+    /// 布依族
+    /// </summary>
+    [Description("布依族")]
+    BuYiZu = 11,
+
+    /// <summary>
+    /// 侗族
+    /// </summary>
+    [Description("侗族")]
+    DongZu = 12,
+
+    /// <summary>
+    /// 瑶族
+    /// </summary>
+    [Description("瑶族")]
+    YaoZu = 13,
+
+    /// <summary>
+    /// 朝鲜族
+    /// </summary>
+    [Description("朝鲜族")]
+    ChaoXianZu = 14,
+
+    /// <summary>
+    /// 白族
+    /// </summary>
+    [Description("白族")]
+    BaiZu = 15,
+
+    /// <summary>
+    /// 哈尼族
+    /// </summary>
+    [Description("哈尼族")]
+    HaNiZu = 16,
+
+    /// <summary>
+    /// 哈萨克族
+    /// </summary>
+    [Description("哈萨克族")]
+    HaSaKeZu = 17,
+
+    /// <summary>
+    /// 黎族
+    /// </summary>
+    [Description("黎族")]
+    LiZu = 18,
+
+    /// <summary>
+    /// 傣族
+    /// </summary>
+    [Description("傣族")]
+    DaiZu = 19,
+
+    /// <summary>
+    /// 畲族
+    /// </summary>
+    [Description("畲族")]
+    SheZu = 20,
+
+    /// <summary>
+    /// 傈僳族
+    /// </summary>
+    [Description("傈僳族")]
+    LiSuZu = 21,
+
+    /// <summary>
+    /// 仡佬族
+    /// </summary>
+    [Description("仡佬族")]
+    GeLaoZu = 22,
+
+    /// <summary>
+    /// 拉祜族
+    /// </summary>
+    [Description("拉祜族")]
+    LaHuZu = 23,
+
+    /// <summary>
+    /// 东乡族
+    /// </summary>
+    [Description("东乡族")]
+    DongXiangZu = 24,
+
+    /// <summary>
+    /// 纳西族
+    /// </summary>
+    [Description("纳西族")]
+    NaXiZu = 25,
+
+    /// <summary>
+    /// 景颇族
+    /// </summary>
+    [Description("景颇族")]
+    JingPoZu = 26,
+
+    /// <summary>
+    /// 柯尔克孜族
+    /// </summary>
+    [Description("柯尔克孜族")]
+    KeErKeZiZu = 27,
+
+    /// <summary>
+    /// 土族
+    /// </summary>
+    [Description("土族")]
+    TuZu = 28,
+
+    /// <summary>
+    /// 达斡尔族
+    /// </summary>
+    [Description("达斡尔族")]
+    DaWoErZu = 29,
+
+    /// <summary>
+    /// 仫佬族
+    /// </summary>
+    [Description("仫佬族")]
+    MuLaoZu = 30,
+
+    /// <summary>
+    /// 羌族
+    /// </summary>
+    [Description("羌族")]
+    QiangZu = 31,
+
+    /// <summary>
+    /// 布朗族
+    /// </summary>
+    [Description("布朗族")]
+    BuLangZu = 32,
+
+    /// <summary>
+    /// 撒拉族
+    /// </summary>
+    [Description("撒拉族")]
+    SaLaZu = 33,
+
+    /// <summary>
+    /// 毛南族
+    /// </summary>
+    [Description("毛南族")]
+    MaoNanZu = 34,
+
+    /// <summary>
+    /// 仡族
+    /// </summary>
+    [Description("仡族")]
+    GeZu = 35,
+
+    /// <summary>
+    /// 锡伯族
+    /// </summary>
+    [Description("锡伯族")]
+    XiBoZu = 36,
+
+    /// <summary>
+    /// 阿昌族
+    /// </summary>
+    [Description("阿昌族")]
+    AChangZu = 37,
+
+    /// <summary>
+    /// 普米族
+    /// </summary>
+    [Description("普米族")]
+    PuMiZu = 38,
+
+    /// <summary>
+    /// 塔吉克族
+    /// </summary>
+    [Description("塔吉克族")]
+    TaJiKeZu = 39,
+
+    /// <summary>
+    /// 怒族
+    /// </summary>
+    [Description("怒族")]
+    NuZu = 40,
+
+    /// <summary>
+    /// 乌孜别克族
+    /// </summary>
+    [Description("乌孜别克族")]
+    WuZiBieKeZu = 41,
+
+    /// <summary>
+    /// 俄罗斯族
+    /// </summary>
+    [Description("俄罗斯族")]
+    ELuoSiZu = 42,
+
+    /// <summary>
+    /// 鄂温克族
+    /// </summary>
+    [Description("鄂温克族")]
+    EwenKeZu = 43,
+
+    /// <summary>
+    /// 德昂族
+    /// </summary>
+    [Description("德昂族")]
+    DeAngZu = 44,
+
+    /// <summary>
+    /// 保安族
+    /// </summary>
+    [Description("保安族")]
+    BaoAnZu = 45,
+
+    /// <summary>
+    /// 裕固族
+    /// </summary>
+    [Description("裕固族")]
+    YuGuZu = 46,
+
+    /// <summary>
+    /// 京族
+    /// </summary>
+    [Description("京族")]
+    JingZu = 47,
+
+    /// <summary>
+    /// 塔塔尔族
+    /// </summary>
+    [Description("塔塔尔族")]
+    TaTaErZu = 48,
+
+    /// <summary>
+    /// 独龙族
+    /// </summary>
+    [Description("独龙族")]
+    DuLongZu = 49,
+
+    /// <summary>
+    /// 鄂伦春族
+    /// </summary>
+    [Description("鄂伦春族")]
+    ELunChunZu = 50,
+
+    /// <summary>
+    /// 赫哲族
+    /// </summary>
+    [Description("赫哲族")]
+    HeZheZu = 51,
+
+    /// <summary>
+    /// 门巴族
+    /// </summary>
+    [Description("门巴族")]
+    MenBaZu = 52,
+
+    /// <summary>
+    /// 珞巴族
+    /// </summary>
+    [Description("珞巴族")]
+    LuoBaZu = 53,
+
+    /// <summary>
+    /// 高山族
+    /// </summary>
+    [Description("高山族")]
+    GaoShanZu = 54,
+
+    /// <summary>
+    /// 佤族
+    /// </summary>
+    [Description("佤族")]
+    WaZu = 55,
+
+    /// <summary>
+    /// 基诺族
+    /// </summary>
+    [Description("基诺族")]
+    JiNuoZu = 56
+}

+ 3 - 6
Admin.NET/Admin.NET.Core/EventBus/EventHandlerMonitor.cs

@@ -1,11 +1,8 @@
-// 麻省理工学院许可证
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
-// 版权所有 (c) 2021-2023 zuohuaijun,大名科技(天津)有限公司  联系电话/微信:18020030720  QQ:515096995
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
-// 特此免费授予获得本软件的任何人以处理本软件的权利,但须遵守以下条件:在所有副本或重要部分的软件中必须包括上述版权声明和本许可声明。
-//
-// 软件按“原样”提供,不提供任何形式的明示或暗示的保证,包括但不限于对适销性、适用性和非侵权的保证。
-// 在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是因合同、侵权或其他方式引起的,与软件或其使用或其他交易有关。
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 namespace Admin.NET.Core;
 

+ 20 - 0
Admin.NET/Admin.NET.Core/EventBus/RedisQueue.cs

@@ -179,4 +179,24 @@ public static class RedisQueue
         var queue = GetRedisReliableQueue<T>(topic);
         return queue.Take(count).ToList();
     }
+
+    /// <summary>
+    /// 申请分布式锁
+    /// </summary>
+    /// <param name="key">要锁定的key</param>
+    /// <param name="msTimeout">申请锁等待的时间,单位毫秒</param>
+    /// <param name="msExpire">锁过期时间,超过该时间没有主动是放则自动是放,必须整数秒,单位毫秒</param>
+    /// <param name="throwOnFailure">失败时是否抛出异常,如不抛出异常,可通过判断返回null得知申请锁失败</param>
+    /// <returns></returns>
+    public static IDisposable? BeginCacheLock(string key, int msTimeout = 500, int msExpire = 10000, bool throwOnFailure = true)
+    {
+        try
+        {
+            return _cacheProvider.Cache.AcquireLock(key, msTimeout, msExpire, throwOnFailure);
+        }
+        catch
+        {
+            return null;
+        }
+    }
 }

+ 1 - 1
Admin.NET/Admin.NET.Core/EventBus/RetryEventHandlerExecutor.cs

@@ -17,7 +17,7 @@ public class RetryEventHandlerExecutor : IEventHandlerExecutor
         // 判断是否自定义了重试失败回调服务
         var fallbackPolicyService = eventSubscribeAttribute?.FallbackPolicy == null
             ? null
-            : App.GetService(eventSubscribeAttribute.FallbackPolicy) as IEventFallbackPolicy;
+            : App.GetRequiredService(eventSubscribeAttribute.FallbackPolicy) as IEventFallbackPolicy;
 
         await Retry.InvokeAsync(async () =>
         {

+ 5 - 4
Admin.NET/Admin.NET.Core/Extension/RepositoryExtension.cs

@@ -133,8 +133,10 @@ public static class RepositoryExtension
     /// <returns> </returns>
     public static ISugarQueryable<T> OrderBuilder<T>(this ISugarQueryable<T> queryable, BasePageInput pageInput, string prefix = "", string defaultSortField = "Id", bool descSort = true)
     {
+        var iSqlBuilder = InstanceFactory.GetSqlBuilderWithContext(queryable.Context);
+
         // 约定默认每张表都有Id排序
-        var orderStr = string.IsNullOrWhiteSpace(defaultSortField) ? "" : $"{prefix}{defaultSortField}" + (descSort ? " Desc" : " Asc");
+        var orderStr = string.IsNullOrWhiteSpace(defaultSortField) ? "" : $"{prefix}{iSqlBuilder.GetTranslationColumnName(defaultSortField)}" + (descSort ? " Desc" : " Asc");
 
         TypeAdapterConfig typeAdapterConfig = new();
         typeAdapterConfig.ForType<T, BasePageInput>().IgnoreNullValues(true);
@@ -144,9 +146,8 @@ public static class RepositoryExtension
         if (!string.IsNullOrEmpty(nowPagerInput.Field) && !string.IsNullOrEmpty(nowPagerInput.Order))
         {
             var col = queryable.Context.EntityMaintenance.GetEntityInfo<T>().Columns.FirstOrDefault(u => u.PropertyName.Equals(nowPagerInput.Field, StringComparison.CurrentCultureIgnoreCase));
-            orderStr = col != null
-                ? $"{prefix}{col.DbColumnName} {(nowPagerInput.Order == nowPagerInput.DescStr ? "Desc" : "Asc")}"
-                : $"{prefix}{nowPagerInput.Field} {(nowPagerInput.Order == nowPagerInput.DescStr ? "Desc" : "Asc")}";
+            var dbColumnName = col != null ? col.DbColumnName : nowPagerInput.Field;
+            orderStr = $"{prefix}{iSqlBuilder.GetTranslationColumnName(dbColumnName)} {(nowPagerInput.Order == nowPagerInput.DescStr ? "Desc" : "Asc")}";
         }
         return queryable.OrderByIF(!string.IsNullOrWhiteSpace(orderStr), orderStr);
     }

+ 1 - 0
Admin.NET/Admin.NET.Core/Hub/OnlineUserHub.cs

@@ -48,6 +48,7 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
         var realName = httpContext.User.FindFirst(ClaimConst.RealName)?.Value;
         var tenantId = (httpContext.User.FindFirst(ClaimConst.TenantId)?.Value).ToLong();
 
+        if (userId < 0 || string.IsNullOrWhiteSpace(account)) return;
         var user = new SysOnlineUser
         {
             ConnectionId = Context.ConnectionId,

+ 1 - 1
Admin.NET/Admin.NET.Core/Logging/LoggingSetup.cs

@@ -45,7 +45,7 @@ public static class LoggingSetup
                     options.WithTraceId = true; // 显示线程Id
                     options.WithStackFrame = true; // 显示程序集
                     options.FileNameRule = fileName => string.Format(fileName, DateTime.Now, logLevel.ToString()); // 每天创建一个文件
-                    options.WriteFilter = logMsg => logMsg.LogLevel == logLevel; // 日志级别
+                    options.WriteFilter = logMsg => logMsg.LogLevel >= logLevel; // 日志级别
                     options.HandleWriteError = (writeError) => // 写入失败时启用备用文件
                     {
                         writeError.UseRollbackFileName(Path.GetFileNameWithoutExtension(writeError.CurrentFileName) + "-oops" + Path.GetExtension(writeError.CurrentFileName));

+ 2 - 2
Admin.NET/Admin.NET.Core/SeedData/SysConfigSeedData.cs

@@ -29,7 +29,7 @@ public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
             new SysConfig{ Id=1300000000171, Name="图形验证码", Code=ConfigConst.SysCaptcha, Value="True", SysFlag=YesNoEnum.Y, Remark="是否开启图形验证码", OrderNo=80, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000181, Name="Token过期时间", Code=ConfigConst.SysTokenExpire, Value="10080", SysFlag=YesNoEnum.Y, Remark="Token过期时间(分钟)", OrderNo=90, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000191, Name="RefreshToken过期时间", Code=ConfigConst.SysRefreshTokenExpire, Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间(分钟)(一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=100, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000201, Name="发送异常日志邮件", Code=ConfigConst.SysErrorMail, Value="True", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=110, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000201, Name="发送异常日志邮件", Code=ConfigConst.SysErrorMail, Value="False", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=110, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000211, Name="域登录验证", Code=ConfigConst.SysDomainLogin, Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启域登录验证", OrderNo=120, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000221, Name="数据校验日志", Code=ConfigConst.SysValidationLog, Value="True", SysFlag=YesNoEnum.Y, Remark="是否数据校验日志", OrderNo=130, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000231, Name="行政区域同步层级", Code=ConfigConst.SysRegionSyncLevel, Value="3", SysFlag=YesNoEnum.Y, Remark="行政区域同步层级 1-省级,2-市级,3-区县级,4-街道级,5-村级", OrderNo=140, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
@@ -38,7 +38,7 @@ public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
             new SysConfig{ Id=1300000000321, Name="系统描述", Code=ConfigConst.SysWebViceDesc, Value="站在巨人肩膀上的 .NET 通用权限开发框架", SysFlag=YesNoEnum.Y, Remark="系统描述", OrderNo=320, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000331, Name="水印内容", Code=ConfigConst.SysWebWatermark, Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="水印内容", OrderNo=330, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000341, Name="版权说明", Code=ConfigConst.SysWebCopyright, Value="Copyright © 2021-present Admin.NET All rights reserved.", SysFlag=YesNoEnum.Y, Remark="版权说明", OrderNo=340, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000351, Name="系统图标", Code=ConfigConst.SysWebLogo, Value="/Upload/logo.png", SysFlag=YesNoEnum.Y, Remark="系统图标", OrderNo=350, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000351, Name="系统图标", Code=ConfigConst.SysWebLogo, Value="/upload/logo.png", SysFlag=YesNoEnum.Y, Remark="系统图标", OrderNo=350, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000361, Name="ICP备案号", Code=ConfigConst.SysWebIcp, Value="省ICP备12345678号", SysFlag=YesNoEnum.Y, Remark="ICP备案号", OrderNo=360, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000371, Name="ICP地址", Code=ConfigConst.SysWebIcpUrl, Value="https://beian.miit.gov.cn", SysFlag=YesNoEnum.Y, Remark="ICP地址", OrderNo=370, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
         };

+ 2 - 1
Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs

@@ -102,7 +102,7 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
             new SysMenu{ Id=1310000000319, Pid=1310000000311, Title="设置状态", Permission="sysTenant:setStatus", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
 
             new SysMenu{ Id=1310000000321, Pid=1310000000301, Title="菜单管理", Path="/platform/menu", Name="sysMenu", Component="/system/menu/index", Icon="ele-Menu", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
-            new SysMenu{ Id=1310000000322, Pid=1310000000321, Title="查询", Permission="sysMenu:list", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            //new SysMenu{ Id=1310000000322, Pid=1310000000321, Title="查询", Permission="sysMenu:list", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
             new SysMenu{ Id=1310000000323, Pid=1310000000321, Title="编辑", Permission="sysMenu:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
             new SysMenu{ Id=1310000000324, Pid=1310000000321, Title="增加", Permission="sysMenu:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
             new SysMenu{ Id=1310000000325, Pid=1310000000321, Title="删除", Permission="sysMenu:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
@@ -130,6 +130,7 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
             new SysMenu{ Id=1310000000371, Pid=1310000000301, Title="缓存管理", Path="/platform/cache", Name="sysCache", Component="/system/cache/index", Icon="ele-Loading", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=160 },
             new SysMenu{ Id=1310000000372, Pid=1310000000371, Title="查询", Permission="sysCache:keyList", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
             new SysMenu{ Id=1310000000373, Pid=1310000000371, Title="删除", Permission="sysCache:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000374, Pid=1310000000371, Title="清空", Permission="sysCache:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
 
             new SysMenu{ Id=1310000000381, Pid=1310000000301, Title="行政区域", Path="/platform/region", Name="sysRegion", Component="/system/region/index", Icon="ele-LocationInformation", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=170 },
             new SysMenu{ Id=1310000000382, Pid=1310000000381, Title="查询", Permission="sysRegion:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },

+ 6 - 17
Admin.NET/Admin.NET.Core/SeedData/SysOrgSeedData.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -20,22 +20,11 @@ public class SysOrgSeedData : ISqlSugarEntitySeedData<SysOrg>
     {
         return new[]
         {
-            new SysOrg{ Id=1300000000101, Pid=0, Name="XXX公司", Code="1001", Type="101", Level=1, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="XXX公司", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000102, Pid=1300000000101, Name="市场部", Code="100101", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="市场部", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000103, Pid=1300000000101, Name="研发部", Code="100102", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="研发部", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000104, Pid=1300000000101, Name="财务部", Code="100103", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="财务部", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000105, Pid=1300000000104, Name="财务部1", Code="10010301", Level=3, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="财务部1", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000106, Pid=1300000000104, Name="财务部2", Code="10010302", Level=3, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="财务部2", TenantId=SqlSugarConst.DefaultTenantId },
-
-            new SysOrg{ Id=1300000000201, Pid=0, Name="分公司1", Code="1002", Type="201", Level=1, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="分公司1", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000202, Pid=1300000000201, Name="市场部", Code="100201", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="市场部", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000203, Pid=1300000000201, Name="研发部", Code="100202", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="研发部", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000204, Pid=1300000000201, Name="财务部", Code="100203", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="财务部", TenantId=SqlSugarConst.DefaultTenantId },
-
-            new SysOrg{ Id=1300000000301, Pid=0, Name="分公司2", Code="1003", Type="201", Level=1, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="分公司2", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000302, Pid=1300000000301, Name="市场部", Code="100301", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="市场部", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000303, Pid=1300000000301, Name="研发部", Code="100302", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="市场部", TenantId=SqlSugarConst.DefaultTenantId },
-            new SysOrg{ Id=1300000000304, Pid=1300000000301, Name="财务部", Code="100303", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="市场部", TenantId=SqlSugarConst.DefaultTenantId },
+            new SysOrg{ Id=SqlSugarConst.DefaultTenantId, Pid=0, Name="系统默认", Code="1001", Type="101", Level=1, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="系统默认", TenantId=SqlSugarConst.DefaultTenantId },
+            new SysOrg{ Id=SqlSugarConst.DefaultTenantId + 1, Pid=SqlSugarConst.DefaultTenantId, Name="市场部", Code="100101", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="市场部", TenantId=SqlSugarConst.DefaultTenantId },
+            new SysOrg{ Id=SqlSugarConst.DefaultTenantId + 2, Pid=SqlSugarConst.DefaultTenantId, Name="开发部", Code="100102", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="开发部", TenantId=SqlSugarConst.DefaultTenantId },
+            new SysOrg{ Id=SqlSugarConst.DefaultTenantId + 3, Pid=SqlSugarConst.DefaultTenantId, Name="售后部", Code="100103", Level=2, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="售后部", TenantId=SqlSugarConst.DefaultTenantId },
+            new SysOrg{ Id=SqlSugarConst.DefaultTenantId + 4, Pid=SqlSugarConst.DefaultTenantId, Name="其他", Code="10010301", Level=3, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Remark="其他", TenantId=SqlSugarConst.DefaultTenantId },
         };
     }
 }

+ 32 - 12
Admin.NET/Admin.NET.Core/SeedData/SysRoleMenuSeedData.cs

@@ -19,12 +19,11 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
     {
         return new[]
         {
-            // 数据面板【admin/1300000000101】
+            ////// 数据面板【admin/1300000000101】
             new SysRoleMenu{ Id=1300000000101, RoleId=1300000000101, MenuId=1300000000101 },
             new SysRoleMenu{ Id=1300000000102, RoleId=1300000000101, MenuId=1300000000111 },
             new SysRoleMenu{ Id=1300000000103, RoleId=1300000000101, MenuId=1300000000121 },
-
-            // 系统管理
+            ////// 系统管理
             new SysRoleMenu{ Id=1300000000111, RoleId=1300000000101, MenuId=1310000000101 },
             // 账号管理
             new SysRoleMenu{ Id=1300000000121, RoleId=1300000000101, MenuId=1310000000111 },
@@ -37,6 +36,7 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=1300000000128, RoleId=1300000000101, MenuId=1310000000118 },
             new SysRoleMenu{ Id=1300000000129, RoleId=1300000000101, MenuId=1310000000119 },
             new SysRoleMenu{ Id=1300000000130, RoleId=1300000000101, MenuId=1310000000120 },
+            new SysRoleMenu{ Id=1300000000131, RoleId=1300000000101, MenuId=1310000000121 },
             // 角色管理
             new SysRoleMenu{ Id=1300000000141, RoleId=1300000000101, MenuId=1310000000131 },
             new SysRoleMenu{ Id=1300000000142, RoleId=1300000000101, MenuId=1310000000132 },
@@ -58,6 +58,7 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=1300000000163, RoleId=1300000000101, MenuId=1310000000153 },
             new SysRoleMenu{ Id=1300000000164, RoleId=1300000000101, MenuId=1310000000154 },
             new SysRoleMenu{ Id=1300000000165, RoleId=1300000000101, MenuId=1310000000155 },
+            new SysRoleMenu{ Id=1300000000166, RoleId=1300000000101, MenuId=1310000000156 },
             // 个人中心
             new SysRoleMenu{ Id=1300000000171, RoleId=1300000000101, MenuId=1310000000161 },
             new SysRoleMenu{ Id=1300000000172, RoleId=1300000000101, MenuId=1310000000162 },
@@ -78,9 +79,22 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=1300000000193, RoleId=1300000000101, MenuId=1310000000183 },
             new SysRoleMenu{ Id=1300000000194, RoleId=1300000000101, MenuId=1310000000184 },
             new SysRoleMenu{ Id=1300000000195, RoleId=1300000000101, MenuId=1310000000185 },
-
-            //// 平台管理
-            //new SysRoleMenu{ Id=1300000000201, RoleId=1300000000101, MenuId=1310000000301 },
+            ////// 平台管理
+            new SysRoleMenu{ Id=1300000000201, RoleId=1300000000101, MenuId=1310000000301 },
+            // 菜单管理
+            new SysRoleMenu{ Id=1300000000221, RoleId=1300000000101, MenuId=1310000000322 },
+            // 字典管理
+            new SysRoleMenu{ Id=1300000000231, RoleId=1300000000101, MenuId=1310000000331 },
+            new SysRoleMenu{ Id=1300000000232, RoleId=1300000000101, MenuId=1310000000332 },
+            new SysRoleMenu{ Id=1300000000233, RoleId=1300000000101, MenuId=1310000000333 },
+            new SysRoleMenu{ Id=1300000000234, RoleId=1300000000101, MenuId=1310000000334 },
+            new SysRoleMenu{ Id=1300000000235, RoleId=1300000000101, MenuId=1310000000335 },
+            // 字典管理
+            new SysRoleMenu{ Id=1300000000241, RoleId=1300000000101, MenuId=1310000000341 },
+            new SysRoleMenu{ Id=1300000000242, RoleId=1300000000101, MenuId=1310000000342 },
+            new SysRoleMenu{ Id=1300000000243, RoleId=1300000000101, MenuId=1310000000343 },
+            new SysRoleMenu{ Id=1300000000244, RoleId=1300000000101, MenuId=1310000000344 },
+            new SysRoleMenu{ Id=1300000000245, RoleId=1300000000101, MenuId=1310000000345 },
             // 任务调度
             new SysRoleMenu{ Id=1300000000251, RoleId=1300000000101, MenuId=1310000000351 },
             new SysRoleMenu{ Id=1300000000252, RoleId=1300000000101, MenuId=1310000000352 },
@@ -93,6 +107,7 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=1300000000271, RoleId=1300000000101, MenuId=1310000000371 },
             new SysRoleMenu{ Id=1300000000272, RoleId=1300000000101, MenuId=1310000000372 },
             new SysRoleMenu{ Id=1300000000273, RoleId=1300000000101, MenuId=1310000000373 },
+            new SysRoleMenu{ Id=1300000000274, RoleId=1300000000101, MenuId=1310000000374 },
             // 行政区域
             new SysRoleMenu{ Id=1300000000281, RoleId=1300000000101, MenuId=1310000000381 },
             new SysRoleMenu{ Id=1300000000282, RoleId=1300000000101, MenuId=1310000000382 },
@@ -107,9 +122,8 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=1300000000294, RoleId=1300000000101, MenuId=1310000000394 },
             new SysRoleMenu{ Id=1300000000295, RoleId=1300000000101, MenuId=1310000000395 },
             new SysRoleMenu{ Id=1300000000296, RoleId=1300000000101, MenuId=1310000000396 },
-
-            //// 日志管理
-            //new SysRoleMenu{ Id=1300000000301, RoleId=1300000000101, MenuId=1310000000501 },
+            ////// 日志管理
+            new SysRoleMenu{ Id=1300000000301, RoleId=1300000000101, MenuId=1310000000501 },
             new SysRoleMenu{ Id=1300000000311, RoleId=1300000000101, MenuId=1310000000511 },
             new SysRoleMenu{ Id=1300000000312, RoleId=1300000000101, MenuId=1310000000512 },
             new SysRoleMenu{ Id=1300000000313, RoleId=1300000000101, MenuId=1310000000513 },
@@ -119,12 +133,18 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=1300000000324, RoleId=1300000000101, MenuId=1310000000524 },
             new SysRoleMenu{ Id=1300000000331, RoleId=1300000000101, MenuId=1310000000531 },
             new SysRoleMenu{ Id=1300000000332, RoleId=1300000000101, MenuId=1310000000532 },
-            new SysRoleMenu{ Id=1300000000333, RoleId=1300000000101, MenuId=1310000000543 },
-
-            // 帮助文档
+            new SysRoleMenu{ Id=1300000000333, RoleId=1300000000101, MenuId=1310000000533 },
+            new SysRoleMenu{ Id=1300000000334, RoleId=1300000000101, MenuId=1310000000534 },
+            new SysRoleMenu{ Id=1300000000341, RoleId=1300000000101, MenuId=1310000000541 },
+            new SysRoleMenu{ Id=1300000000342, RoleId=1300000000101, MenuId=1310000000542 },
+            new SysRoleMenu{ Id=1300000000343, RoleId=1300000000101, MenuId=1310000000543 },
+            ////// 帮助文档
             new SysRoleMenu{ Id=1300000000401, RoleId=1300000000101, MenuId=1310000000701 },
             new SysRoleMenu{ Id=1300000000402, RoleId=1300000000101, MenuId=1310000000711 },
             new SysRoleMenu{ Id=1300000000403, RoleId=1300000000101, MenuId=1310000000712 },
+            new SysRoleMenu{ Id=1300000000404, RoleId=1300000000101, MenuId=1310000000713 },
+            new SysRoleMenu{ Id=1300000000405, RoleId=1300000000101, MenuId=1310000000714 },
+            new SysRoleMenu{ Id=1300000000455, RoleId=1300000000101, MenuId=1310000000801 },
 
             // 其他角色默认菜单
             // 数据面板【1300000000102】

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

@@ -21,7 +21,7 @@ public class SysTenantSeedData : ISqlSugarEntitySeedData<SysTenant>
 
         return new[]
         {
-            new SysTenant{ Id=SqlSugarConst.DefaultTenantId, OrgId=1300000000101, UserId=1300000000111, Host="https://gitee.com", TenantType=TenantTypeEnum.Id, DbType=defaultDbConfig.DbType, Connection=defaultDbConfig.ConnectionString, ConfigId=SqlSugarConst.MainConfigId, Remark="系统默认", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysTenant{ Id=SqlSugarConst.DefaultTenantId, OrgId=SqlSugarConst.DefaultTenantId, UserId=1300000000111, Host="https://gitee.com", TenantType=TenantTypeEnum.Id, DbType=defaultDbConfig.DbType, Connection=defaultDbConfig.ConnectionString, ConfigId=SqlSugarConst.MainConfigId, Remark="系统默认", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
         };
     }
 }

+ 5 - 5
Admin.NET/Admin.NET.Core/SeedData/SysUserSeedData.cs

@@ -23,11 +23,11 @@ public class SysUserSeedData : ISqlSugarEntitySeedData<SysUser>
         return new[]
         {
             new SysUser{ Id=1300000000101, Account="superadmin", Password=encryptPassword, NickName="超级管理员", RealName="超级管理员", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Male, AccountType=AccountTypeEnum.SuperAdmin, Remark="超级管理员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), TenantId=SqlSugarConst.DefaultTenantId },
-            new SysUser{ Id=1300000000111, Account="admin", Password=encryptPassword, NickName="系统管理员", RealName="系统管理员", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Male, AccountType=AccountTypeEnum.SysAdmin, Remark="系统管理员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=1300000000101, PosId=1300000000102, TenantId=SqlSugarConst.DefaultTenantId },
-            new SysUser{ Id=1300000000112, Account="user1", Password=encryptPassword, NickName="部门主管", RealName="部门主管", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="部门主管", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=1300000000102, PosId=1300000000108, TenantId=SqlSugarConst.DefaultTenantId },
-            new SysUser{ Id=1300000000113, Account="user2", Password=encryptPassword, NickName="部门职员", RealName="部门职员", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="部门职员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=1300000000103, PosId=1300000000110, TenantId=SqlSugarConst.DefaultTenantId },
-            new SysUser{ Id=1300000000114, Account="user3", Password=encryptPassword, NickName="普通用户", RealName="普通用户", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="普通用户", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=1300000000104, PosId=1300000000115, TenantId=SqlSugarConst.DefaultTenantId },
-            new SysUser{ Id=1300000000115, Account="user4", Password=encryptPassword, NickName="其他", RealName="其他", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.Member, Remark="会员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=1300000000105, PosId=1300000000116, TenantId=SqlSugarConst.DefaultTenantId },
+            new SysUser{ Id=1300000000111, Account="admin", Password=encryptPassword, NickName="系统管理员", RealName="系统管理员", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Male, AccountType=AccountTypeEnum.SysAdmin, Remark="系统管理员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId, PosId=1300000000102, TenantId=SqlSugarConst.DefaultTenantId },
+            new SysUser{ Id=1300000000112, Account="user1", Password=encryptPassword, NickName="部门主管", RealName="部门主管", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="部门主管", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 1, PosId=1300000000108, TenantId=SqlSugarConst.DefaultTenantId },
+            new SysUser{ Id=1300000000113, Account="user2", Password=encryptPassword, NickName="部门职员", RealName="部门职员", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="部门职员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 2, PosId=1300000000110, TenantId=SqlSugarConst.DefaultTenantId },
+            new SysUser{ Id=1300000000114, Account="user3", Password=encryptPassword, NickName="普通用户", RealName="普通用户", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="普通用户", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 3, PosId=1300000000115, TenantId=SqlSugarConst.DefaultTenantId },
+            new SysUser{ Id=1300000000115, Account="user4", Password=encryptPassword, NickName="其他", RealName="其他", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.Member, Remark="会员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 4, PosId=1300000000116, TenantId=SqlSugarConst.DefaultTenantId },
         };
     }
 }

+ 4 - 1
Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs

@@ -66,7 +66,10 @@ public class SysAuthService : IDynamicApiController, ITransient
         var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{input.Account}";
         var passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes);
         var passwordMaxErrorTimes = await _sysConfigService.GetConfigValue<int>(ConfigConst.SysPasswordMaxErrorTimes);
-        if (passwordErrorTimes >= passwordMaxErrorTimes)
+        // 若未配置或误配置为0、负数, 则默认密码错误次数最大为10次
+        if (passwordMaxErrorTimes < 1)
+            passwordMaxErrorTimes = 10;
+        if (passwordErrorTimes > passwordMaxErrorTimes)
             throw Oops.Oh(ErrorCodeEnum.D1027);
 
         // 是否开启验证码

+ 33 - 0
Admin.NET/Admin.NET.Core/Service/Cache/SysCacheService.cs

@@ -23,6 +23,26 @@ public class SysCacheService : IDynamicApiController, ISingleton
         _cacheOptions = cacheOptions.Value;
     }
 
+    /// <summary>
+    /// 申请分布式锁
+    /// </summary>
+    /// <param name="key">要锁定的key</param>
+    /// <param name="msTimeout">申请锁等待的时间,单位毫秒</param>
+    /// <param name="msExpire">锁过期时间,超过该时间没有主动是放则自动是放,必须整数秒,单位毫秒</param>
+    /// <param name="throwOnFailure">失败时是否抛出异常,如不抛出异常,可通过判断返回null得知申请锁失败</param>
+    /// <returns></returns>
+    public IDisposable? BeginCacheLock(string key, int msTimeout = 500, int msExpire = 10000, bool throwOnFailure = true)
+    {
+        try
+        {
+            return _cacheProvider.Cache.AcquireLock(key, msTimeout, msExpire, throwOnFailure);
+        }
+        catch
+        {
+            return null;
+        }
+    }
+
     /// <summary>
     /// 获取缓存键名集合 🔖
     /// </summary>
@@ -86,6 +106,19 @@ public class SysCacheService : IDynamicApiController, ISingleton
         return _cacheProvider.Cache.Remove($"{_cacheOptions.Prefix}{key}");
     }
 
+    /// <summary>
+    /// 清空所有缓存 🔖
+    /// </summary>
+    /// <returns></returns>
+    [DisplayName("清空所有缓存")]
+    [ApiDescriptionSettings(Name = "Clear"), HttpPost]
+    public void Clear()
+    {
+        _cacheProvider.Cache.Clear();
+
+        Cache.Default.Clear();
+    }
+
     /// <summary>
     /// 检查缓存是否存在
     /// </summary>

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

@@ -356,7 +356,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
         };
         // 模板目录
         var templatePathList = GetTemplatePathList(input);
-        var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "Template");
+        var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "template");
 
         for (var i = 0; i < templatePathList.Count; i++)
         {
@@ -422,7 +422,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
 
         // 获取模板文件并替换
         var templatePathList = GetTemplatePathList();
-        var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "Template");
+        var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "template");
 
         var result = new Dictionary<string, string>();
         for (var i = 0; i < templatePathList.Count; i++)

+ 1 - 1
Admin.NET/Admin.NET.Core/Service/Common/SysProcService.cs

@@ -51,7 +51,7 @@ public class SysProcService : IDynamicApiController, ITransient
         var dt = await db.Ado.UseStoredProcedure().GetDataTableAsync(input.ProcId, input.ProcParams);
 
         var excelExporter = new ExcelExporter();
-        string template = AppDomain.CurrentDomain.BaseDirectory + "/wwwroot/Template/" + input.Template + ".xlsx";
+        string template = AppDomain.CurrentDomain.BaseDirectory + "/wwwroot/template/" + input.Template + ".xlsx";
         var bs = await excelExporter.ExportBytesByTemplate(dt, template);
         return new FileContentResult(bs, "application/octet-stream") { FileDownloadName = input.ProcId + ".xlsx" };
     }

+ 1 - 1
Admin.NET/Admin.NET.Core/Service/Config/SysConfigService.cs

@@ -285,7 +285,7 @@ public class SysConfigService : IDynamicApiController, ITransient
             // 根据文件名取扩展名
             var ext = string.IsNullOrWhiteSpace(input.SysLogoFileName) ? ".png" : Path.GetExtension(input.SysLogoFileName);
             // 本地图标保存路径
-            var path = "Upload";
+            var path = "upload";
             var absoluteFilePath = Path.Combine(App.WebHostEnvironment.WebRootPath, path, $"logo{ext}");
 
             // 删除已存在文件

+ 0 - 1
Admin.NET/Admin.NET.Core/Service/Const/SysConstService.cs

@@ -10,7 +10,6 @@ namespace Admin.NET.Core.Service;
 /// 系统常量服务 🧩
 /// </summary>
 [ApiDescriptionSettings(Order = 280)]
-[AllowAnonymous]
 public class SysConstService : IDynamicApiController, ITransient
 {
     private readonly SysCacheService _sysCacheService;

+ 2 - 3
Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs

@@ -105,7 +105,6 @@ public class SysDatabaseService : IDynamicApiController, ITransient
     /// <param name="tableName">表名</param>
     /// <param name="configId">ConfigId</param>
     /// <returns></returns>
-    [AllowAnonymous]
     [DisplayName("获取字段列表")]
     public List<DbColumnOutput> GetColumnList(string tableName, string configId = SqlSugarConst.MainConfigId)
     {
@@ -468,7 +467,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
     /// <returns></returns>
     private static string GetEntityTemplatePath()
     {
-        var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "Template");
+        var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "template");
         return Path.Combine(templatePath, "Entity.cs.vm");
     }
 
@@ -478,7 +477,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
     /// <returns></returns>
     private static string GetSeedDataTemplatePath()
     {
-        var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "Template");
+        var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "template");
         return Path.Combine(templatePath, "SeedData.cs.vm");
     }
 

+ 0 - 1
Admin.NET/Admin.NET.Core/Service/Dict/SysDictDataService.cs

@@ -10,7 +10,6 @@ namespace Admin.NET.Core.Service;
 /// 系统字典值服务 🧩
 /// </summary>
 [ApiDescriptionSettings(Order = 420)]
-[AllowAnonymous]
 public class SysDictDataService : IDynamicApiController, ITransient
 {
     private readonly SqlSugarRepository<SysDictData> _sysDictDataRep;

+ 0 - 4
Admin.NET/Admin.NET.Core/Service/Dict/SysDictTypeService.cs

@@ -10,7 +10,6 @@ namespace Admin.NET.Core.Service;
 /// 系统字典类型服务 🧩
 /// </summary>
 [ApiDescriptionSettings(Order = 430)]
-[AllowAnonymous]
 public class SysDictTypeService : IDynamicApiController, ITransient
 {
     private readonly SqlSugarRepository<SysDictType> _sysDictTypeRep;
@@ -55,8 +54,6 @@ public class SysDictTypeService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    [UnitOfWork]
-    [AllowAnonymous]
     [DisplayName("获取字典类型-值列表")]
     public async Task<List<SysDictData>> GetDataList([FromQuery] GetDataDictTypeInput input)
     {
@@ -148,7 +145,6 @@ public class SysDictTypeService : IDynamicApiController, ITransient
     /// 获取所有字典集合 🔖
     /// </summary>
     /// <returns></returns>
-    [AllowAnonymous]
     [DisplayName("获取所有字典集合")]
     public async Task<dynamic> GetAllDictList()
     {

+ 0 - 1
Admin.NET/Admin.NET.Core/Service/Enum/SysEnumService.cs

@@ -10,7 +10,6 @@ namespace Admin.NET.Core.Service;
 /// 系统枚举服务 🧩
 /// </summary>
 [ApiDescriptionSettings(Order = 275)]
-[AllowAnonymous]
 public class SysEnumService : IDynamicApiController, ITransient
 {
     private readonly EnumOptions _enumOptions;

+ 24 - 0
Admin.NET/Admin.NET.Core/Service/File/Dto/FileInput.cs

@@ -18,6 +18,12 @@ public class FileInput : BaseIdInput
     /// </summary>
     public string FileType { get; set; }
 
+    /// <summary>
+    /// 是否公开
+    /// 若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+    /// </summary>
+    public bool IsPublic { get; set; }
+
     /// <summary>
     /// 文件Url
     /// </summary>
@@ -72,6 +78,12 @@ public class UploadFileFromBase64Input
     /// 文件类型
     /// </summary>
     public string FileType { get; set; }
+
+    /// <summary>
+    /// 是否公开
+    /// 若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+    /// </summary>
+    public bool IsPublic { get; set; }
 }
 
 /// <summary>
@@ -90,6 +102,12 @@ public class FileUploadInput
     /// </summary>
     public string FileType { get; set; }
 
+    /// <summary>
+    /// 是否公开
+    /// 若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+    /// </summary>
+    public bool IsPublic { get; set; }
+
     /// <summary>
     /// 文件路径
     /// </summary>
@@ -168,6 +186,12 @@ public class FileOutput
     /// </summary>
     public string FileType { get; set; }
 
+    /// <summary>
+    /// 是否公开
+    /// 若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+    /// </summary>
+    public bool IsPublic { get; set; }
+
     /// <summary>
     /// 上传人
     /// </summary>

+ 32 - 40
Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs

@@ -44,12 +44,16 @@ public class SysFileService : IDynamicApiController, ITransient
     [DisplayName("获取文件分页列表")]
     public async Task<SqlSugarPagedList<SysFile>> Page(PageFileInput input)
     {
-        return await _sysFileRep.AsQueryable()
-            .WhereIF(!string.IsNullOrWhiteSpace(input.FileName), u => u.FileName.Contains(input.FileName.Trim()))
-            .WhereIF(!string.IsNullOrWhiteSpace(input.StartTime.ToString()) && !string.IsNullOrWhiteSpace(input.EndTime.ToString()),
-                        u => u.CreateTime >= input.StartTime && u.CreateTime <= input.EndTime)
-            .OrderBy(u => u.CreateTime, OrderByType.Desc)
-            .ToPagedListAsync(input.Page, input.PageSize);
+        //获取所有公开附件
+        var publicList = _sysFileRep.AsQueryable().ClearFilter().Where(u => u.IsPublic == true);
+        //获取私有附件
+        var privateList = _sysFileRep.AsQueryable().Where(u => u.IsPublic == false);
+        //合并公开和私有附件并分页
+        return await _sysFileRep.Context.UnionAll(publicList, privateList).WhereIF(!string.IsNullOrWhiteSpace(input.FileName), u => u.FileName.Contains(input.FileName.Trim()))
+             .WhereIF(!string.IsNullOrWhiteSpace(input.StartTime.ToString()) && !string.IsNullOrWhiteSpace(input.EndTime.ToString()),
+                         u => u.CreateTime >= input.StartTime && u.CreateTime <= input.EndTime)
+             .OrderBy(u => u.CreateTime, OrderByType.Desc)
+             .ToPagedListAsync(input.Page, input.PageSize);
     }
 
     /// <summary>
@@ -60,7 +64,7 @@ public class SysFileService : IDynamicApiController, ITransient
     [DisplayName("上传文件")]
     public async Task<SysFile> UploadFile([FromForm] FileUploadInput input)
     {
-        return await HandleUploadFile(input.File, input.Path, fileType: input.FileType);
+        return await HandleUploadFile(input.File, input.Path, fileType: input.FileType, isPublic: input.IsPublic);
     }
 
     /// <summary>
@@ -84,7 +88,7 @@ public class SysFileService : IDynamicApiController, ITransient
             Headers = new HeaderDictionary(),
             ContentType = input.ContentType
         };
-        return await UploadFile(new FileUploadInput { File = formFile, Path = input.Path, FileType = input.FileType });
+        return await UploadFile(new FileUploadInput { File = formFile, Path = input.Path, FileType = input.FileType, IsPublic = input.IsPublic });
     }
 
     /// <summary>
@@ -113,59 +117,55 @@ public class SysFileService : IDynamicApiController, ITransient
     {
         var file = input.Id > 0 ? await GetFile(input) : await _sysFileRep.CopyNew().GetFirstAsync(u => u.Url == input.Url);
         var fileName = HttpUtility.UrlEncode(file.FileName, Encoding.GetEncoding("UTF-8"));
+        var filePath = Path.Combine(file.FilePath, file.Id.ToString() + file.Suffix);
 
         if (_OSSProviderOptions.IsEnable)
         {
-            var filePath = string.Concat(file.FilePath, "/", file.Id.ToString() + file.Suffix);
             var stream = await (await _OSSService.PresignedGetObjectAsync(file.BucketName.ToString(), filePath, 5)).GetAsStreamAsync();
             return new FileStreamResult(stream.Stream, "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
         }
         else if (App.Configuration["SSHProvider:IsEnable"].ToBoolean())
         {
-            var fullPath = string.Concat(file.FilePath, "/", file.Id + file.Suffix);
             using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"],
                App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]))
             {
-                return new FileStreamResult(helper.OpenRead(fullPath), "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
+                return new FileStreamResult(helper.OpenRead(filePath), "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
             }
         }
         else
         {
-            var filePath = Path.Combine(file.FilePath, file.Id.ToString() + file.Suffix);
             var path = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath);
             return new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
         }
     }
 
     /// <summary>
-    /// 文件预览
+    /// 文件预览 🔖
     /// </summary>
     /// <param name="Id"></param>
     /// <returns></returns>
-    [AllowAnonymous]
+    [DisplayName("文件预览")]
     public async Task<IActionResult> GetPreview([FromRoute] long Id)
     {
         var file = await GetFile(new FileInput { Id = Id });
         //var fileName = HttpUtility.UrlEncode(file.FileName, Encoding.GetEncoding("UTF-8"));
+        var filePath = Path.Combine(file.FilePath, file.Id.ToString() + file.Suffix);
 
         if (_OSSProviderOptions.IsEnable)
         {
-            var filePath = string.Concat(file.FilePath, "/", file.Id.ToString() + file.Suffix);
             var stream = await (await _OSSService.PresignedGetObjectAsync(file.BucketName.ToString(), filePath, 5)).GetAsStreamAsync();
             return new FileStreamResult(stream.Stream, "application/octet-stream");
         }
         else if (App.Configuration["SSHProvider:IsEnable"].ToBoolean())
         {
-            var fullPath = string.Concat(file.FilePath, "/", file.Id + file.Suffix);
             using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"],
                App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]))
             {
-                return new FileStreamResult(helper.OpenRead(fullPath), "application/octet-stream");
+                return new FileStreamResult(helper.OpenRead(filePath), "application/octet-stream");
             }
         }
         else
         {
-            var filePath = Path.Combine(file.FilePath, file.Id.ToString() + file.Suffix);
             var path = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath);
             return new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream");
         }
@@ -176,7 +176,6 @@ public class SysFileService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="url"></param>
     /// <returns></returns>
-    [AllowAnonymous]
     [DisplayName("下载指定文件Base64格式")]
     public async Task<string> DownloadFileBase64([FromBody] string url)
     {
@@ -270,15 +269,16 @@ public class SysFileService : IDynamicApiController, ITransient
         var isExist = await _sysFileRep.IsAnyAsync(u => u.Id == input.Id);
         if (!isExist) throw Oops.Oh(ErrorCodeEnum.D8000);
 
-        await _sysFileRep.UpdateAsync(u => new SysFile() { FileName = input.FileName, FileType = input.FileType }, u => u.Id == input.Id);
+        await _sysFileRep.UpdateAsync(u => new SysFile() { FileName = input.FileName, FileType = input.FileType, IsPublic = input.IsPublic }, u => u.Id == input.Id);
     }
 
     /// <summary>
-    /// 获取文件
+    /// 获取文件 🔖
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    private async Task<SysFile> GetFile([FromQuery] FileInput input)
+    [DisplayName("获取文件")]
+    public async Task<SysFile> GetFile([FromQuery] FileInput input)
     {
         var file = await _sysFileRep.CopyNew().GetFirstAsync(u => u.Id == input.Id);
         return file ?? throw Oops.Oh(ErrorCodeEnum.D8000);
@@ -291,13 +291,14 @@ public class SysFileService : IDynamicApiController, ITransient
     /// <param name="savePath">路径</param>
     /// <param name="allowSuffix">允许格式:.jpg.png.gif.tif.bmp</param>
     /// <param name="fileType">类型</param>
+    /// <param name="isPublic">是否公开</param>
     /// <returns></returns>
-    private async Task<SysFile> HandleUploadFile(IFormFile file, string savePath, string allowSuffix = "", string fileType = "")
+    private async Task<SysFile> HandleUploadFile(IFormFile file, string savePath, string allowSuffix = "", string fileType = "", bool isPublic = false)
     {
         if (file == null) throw Oops.Oh(ErrorCodeEnum.D8000);
 
         // 判断是否重复上传的文件
-        var sizeKb = (long)(file.Length / 1024.0); // 大小KB
+        var sizeKb = file.Length / 1024; // 大小KB
         var fileMd5 = string.Empty;
         if (_uploadOptions.EnableMd5)
         {
@@ -356,7 +357,8 @@ public class SysFileService : IDynamicApiController, ITransient
             SizeKb = sizeKb,
             FilePath = path,
             FileMd5 = fileMd5,
-            FileType = fileType
+            FileType = fileType,
+            IsPublic = isPublic,
         };
 
         var finalName = newFile.Id + suffix; // 文件最终名称
@@ -387,7 +389,7 @@ public class SysFileService : IDynamicApiController, ITransient
         }
         else if (App.Configuration["SSHProvider:IsEnable"].ToBoolean())
         {
-            var fullPath = string.Concat(path.StartsWith("/") ? path : "/" + path, "/", finalName);
+            var fullPath = string.Concat(path.StartsWith('/') ? path : "/" + path, "/", finalName);
             using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"],
                App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]))
             {
@@ -417,17 +419,6 @@ public class SysFileService : IDynamicApiController, ITransient
         return newFile;
     }
 
-    ///// <summary>
-    ///// 获取Minio文件的下载或者预览地址
-    ///// </summary>
-    ///// <param name="bucketName">桶名</param>
-    ///// <param name="fileName">文件名</param>
-    ///// <returns></returns>
-    //private async Task<string> GetMinioPreviewFileUrl(string bucketName, string fileName)
-    //{
-    //    return await _OSSService.PresignedGetObjectAsync(bucketName, fileName, 7);
-    //}
-
     /// <summary>
     /// 上传头像 🔖
     /// </summary>
@@ -436,7 +427,7 @@ public class SysFileService : IDynamicApiController, ITransient
     [DisplayName("上传头像")]
     public async Task<SysFile> UploadAvatar([Required] IFormFile file)
     {
-        var sysFile = await HandleUploadFile(file, "Upload/Avatar", _imageType);
+        var sysFile = await HandleUploadFile(file, "upload/avatar", _imageType);
 
         var sysUserRep = _sysFileRep.ChangeRepository<SqlSugarRepository<SysUser>>();
         var user = sysUserRep.GetFirst(u => u.Id == _userManager.UserId);
@@ -458,7 +449,7 @@ public class SysFileService : IDynamicApiController, ITransient
     [DisplayName("上传电子签名")]
     public async Task<SysFile> UploadSignature([Required] IFormFile file)
     {
-        var sysFile = await HandleUploadFile(file, "Upload/Signature", _imageType);
+        var sysFile = await HandleUploadFile(file, "upload/signature", _imageType);
 
         var sysUserRep = _sysFileRep.ChangeRepository<SqlSugarRepository<SysUser>>();
         var user = sysUserRep.GetFirst(u => u.Id == _userManager.UserId);
@@ -473,7 +464,7 @@ public class SysFileService : IDynamicApiController, ITransient
     }
 
     /// <summary>
-    /// 修改附件关联对象
+    /// 修改附件关联对象 🔖
     /// </summary>
     /// <param name="ids"></param>
     /// <param name="relationName"></param>
@@ -499,6 +490,7 @@ public class SysFileService : IDynamicApiController, ITransient
     /// <param name="input"></param>
     /// <returns></returns>
     /// <exception cref="ArgumentNullException"></exception>
+    [DisplayName("根据关联查询附件")]
     public async Task<List<FileOutput>> GetRelationFiles([FromQuery] RelationQueryInput input)
     {
         return await _sysFileRep.AsQueryable()

+ 0 - 0
Admin.NET/Admin.NET.Core/Service/Logging/Dto/ExportLogDto.cs → Admin.NET/Admin.NET.Core/Service/Log/Dto/ExportLogDto.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Service/Logging/Dto/LogInput.cs → Admin.NET/Admin.NET.Core/Service/Log/Dto/LogInput.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Service/Logging/Dto/LogVisOutput.cs → Admin.NET/Admin.NET.Core/Service/Log/Dto/LogVisOutput.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Service/Logging/SysLogDiffService.cs → Admin.NET/Admin.NET.Core/Service/Log/SysLogDiffService.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Service/Logging/SysLogExService.cs → Admin.NET/Admin.NET.Core/Service/Log/SysLogExService.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Service/Logging/SysLogOpService.cs → Admin.NET/Admin.NET.Core/Service/Log/SysLogOpService.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Service/Logging/SysLogVisService.cs → Admin.NET/Admin.NET.Core/Service/Log/SysLogVisService.cs


+ 0 - 1
Admin.NET/Admin.NET.Core/Service/Menu/SysMenuService.cs

@@ -76,7 +76,6 @@ public class SysMenuService : IDynamicApiController, ITransient
     /// 获取菜单列表 🔖
     /// </summary>
     /// <returns></returns>
-    [AllowAnonymous]
     [DisplayName("获取菜单列表")]
     public async Task<List<SysMenu>> GetList([FromQuery] MenuInput input)
     {

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

@@ -238,12 +238,13 @@ public class SysTenantService : IDynamicApiController, ITransient
 
         // 默认租户管理员角色菜单集合
         var menuIdList = new List<long> { 1300000000111,1300000000121, // 工作台
-            1310000000111,1310000000112,1310000000113,1310000000114,1310000000115,1310000000116,1310000000117,1310000000118,1310000000119,1310000000120, // 账号
+            1310000000111,1310000000112,1310000000113,1310000000114,1310000000115,1310000000116,1310000000117,1310000000118,1310000000119,1310000000120,1310000000121, // 账号
             1310000000131,1310000000132,1310000000133,1310000000134,1310000000135,1310000000136,1310000000137,1310000000138, // 角色
             1310000000141,1310000000142,1310000000143,1310000000144,1310000000145, // 机构
-            1310000000151,1310000000152,1310000000153,1310000000154,1310000000155, // 职位
-            1310000000161,1310000000162,1310000000163,1310000000164, // 个人中心
-            1310000000171,1310000000172,1310000000173,1310000000174,1310000000175,1310000000176,1310000000177 // 通知公告
+            1310000000151,1310000000152,1310000000153,1310000000154,1310000000155,1310000000156, // 职位
+            1310000000161,1310000000162,1310000000163,1310000000164,1310000000165, // 个人中心
+            1310000000171,1310000000172,1310000000173,1310000000174,1310000000175,1310000000176, // 通知公告
+            1310000000801  // 关于项目
         };
         await _sysRoleMenuService.GrantRoleMenu(new RoleMenuInput() { Id = newRole.Id, MenuIdList = menuIdList });
     }

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

@@ -26,6 +26,7 @@ public class SysUserMenuService : IDynamicApiController, ITransient
     /// <returns></returns>
     [UnitOfWork]
     [ApiDescriptionSettings(Name = "Add"), HttpPost]
+    [DisplayName("收藏菜单")]
     public async Task AddUserMenu(UserMenuInput input)
     {
         await _sysUserMenuRep.DeleteAsync(u => u.UserId == input.UserId);
@@ -45,6 +46,7 @@ public class SysUserMenuService : IDynamicApiController, ITransient
     /// <param name="input"></param>
     /// <returns></returns>
     [ApiDescriptionSettings(Name = "Delete"), HttpPost]
+    [DisplayName("取消收藏菜单")]
     public async Task DeleteUserMenu(UserMenuInput input)
     {
         await _sysUserMenuRep.DeleteAsync(u => u.UserId == input.UserId && input.MenuIdList.Contains(u.MenuId));
@@ -56,6 +58,7 @@ public class SysUserMenuService : IDynamicApiController, ITransient
     /// <param name="userId"></param>
     /// <returns></returns>
     [ApiDescriptionSettings(Name = "DeleteByUserId"), HttpPost]
+    [DisplayName("根据用户Id删除收藏菜单")]
     public async Task DeleteByUserId(long userId)
     {
         await _sysUserMenuRep.DeleteAsync(u => u.UserId == userId);
@@ -66,6 +69,7 @@ public class SysUserMenuService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="userId"></param>
     /// <returns></returns>
+    [DisplayName("根据用户Id获取收藏菜单集合")]
     public async Task<List<MenuOutput>> GetUserMenuList(long userId)
     {
         var sysUserMenuList = await _sysUserMenuRep.AsQueryable()
@@ -79,6 +83,7 @@ public class SysUserMenuService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="userId"></param>
     /// <returns></returns>
+    [DisplayName("根据用户Id获取收藏菜单Id集合")]
     public async Task<List<long>> GetUserMenuIdList(long userId)
     {
         return await _sysUserMenuRep.AsQueryable()

+ 12 - 1
Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarFilter.cs

@@ -55,7 +55,7 @@ public static class SqlSugarFilter
             Scoped.Create((factory, scope) =>
             {
                 var services = scope.ServiceProvider;
-                orgIds = services.GetService<SysOrgService>().GetUserOrgIdList().GetAwaiter().GetResult();
+                orgIds = services.GetRequiredService<SysOrgService>().GetUserOrgIdList().GetAwaiter().GetResult();
             });
             if (orgIds == null || orgIds.Count == 0) return;
 
@@ -100,6 +100,17 @@ public static class SqlSugarFilter
 
         // 获取用户最大数据范围---仅本人数据
         maxDataScope = App.GetRequiredService<SysCacheService>().Get<int>(CacheConst.KeyRoleMaxDataScope + userId);
+        // 若为0则获取用户机构组织集合建立缓存
+        if (maxDataScope == 0)
+        {
+            // 获取用户所属机构,保证同一作用域
+            Scoped.Create((factory, scope) =>
+            {
+                var services = scope.ServiceProvider;
+                services.GetRequiredService<SysOrgService>().GetUserOrgIdList().GetAwaiter().GetResult();
+                maxDataScope = services.GetRequiredService<SysCacheService>().Get<int>(CacheConst.KeyRoleMaxDataScope + userId);
+            });
+        }
         if (maxDataScope != (int)DataScopeEnum.Self) return maxDataScope;
 
         // 配置用户数据范围缓存

+ 1 - 1
Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarRepository.cs

@@ -40,7 +40,7 @@ public class SqlSugarRepository<T> : SimpleClient<T>, ISqlSugarRepository<T> whe
         var tenantId = App.User?.FindFirst(ClaimConst.TenantId)?.Value;
         if (string.IsNullOrWhiteSpace(tenantId) || tenantId == SqlSugarConst.MainConfigId) return;
 
-        // 根据租户Id切换库连接, 为空则返回默认库连接
+        // 根据租户Id切换库连接 为空则返回默认库连接
         var sqlSugarScopeProviderTenant = App.GetRequiredService<SysTenantService>().GetTenantDbConnectionScope(long.Parse(tenantId));
         if (sqlSugarScopeProviderTenant == null) return;
         base.Context = sqlSugarScopeProviderTenant;

+ 18 - 3
Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs

@@ -11,6 +11,9 @@ public static class SqlSugarSetup
     // 多租户实例
     public static ITenant ITenant { get; set; }
 
+    // 是否正在处理种子数据
+    private static bool _isHandlingSeedData = false;
+
     /// <summary>
     /// SqlSugar 上下文初始化
     /// </summary>
@@ -165,6 +168,9 @@ public static class SqlSugarSetup
         // 数据审计
         db.Aop.DataExecuting = (oldValue, entityInfo) =>
         {
+            // 若正在处理种子数据则直接返回
+            if (_isHandlingSeedData) return;
+
             // 新增/插入
             if (entityInfo.OperationType == DataFilterType.InsertByObject)
             {
@@ -327,6 +333,8 @@ public static class SqlSugarSetup
         // 初始化种子数据
         if (config.SeedSettings.EnableInitSeed)
         {
+            _isHandlingSeedData = true;
+
             Log.Information($"初始化种子数据 {config.DbType} - {config.ConfigId}");
             var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))))
                 .WhereIF(config.SeedSettings.EnableIncreSeed, u => u.IsDefined(typeof(IncreSeedAttribute), false))
@@ -358,7 +366,7 @@ public static class SqlSugarSetup
                 if (seedData == null) continue;
 
                 var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType);
-                Console.WriteLine($"添加数据 {entityInfo.DbTableName} ({config.ConfigId} - {++count}/{sum})");
+                Console.WriteLine($"添加数据 {entityInfo.DbTableName} ({config.ConfigId} - {++count}/{sum},数据量:{seedData.Count()})");
 
                 if (entityType.GetCustomAttribute<SplitTableAttribute>(true) != null)
                 {
@@ -374,9 +382,15 @@ public static class SqlSugarSetup
                     {
                         // 按主键进行批量增加和更新
                         var storage = dbProvider.StorageableByObject(seedData.ToList()).ToStorage();
-                        storage.AsInsertable.ExecuteCommand();
+
+                        // 先修改再插入,否则会更新修改时间字段
                         if (seedType.GetCustomAttribute<IgnoreUpdateSeedAttribute>() == null) // 有忽略更新种子特性时则不更新
-                            storage.AsUpdateable.IgnoreColumns(entityInfo.Columns.Where(u => u.PropertyInfo.GetCustomAttribute<IgnoreUpdateSeedColumnAttribute>() != null).Select(u => u.PropertyName).ToArray()).ExecuteCommand();
+                        {
+                            int updateCount = storage.AsUpdateable.IgnoreColumns(entityInfo.Columns.Where(u => u.PropertyInfo.GetCustomAttribute<IgnoreUpdateSeedColumnAttribute>() != null).Select(u => u.PropertyName).ToArray()).ExecuteCommand();
+                            Console.WriteLine($"  修改 {updateCount}/{seedData.Count()} 条记录");
+                        }
+                        int insertCount = storage.AsInsertable.ExecuteCommand();
+                        Console.WriteLine($"  插入 {insertCount}/{seedData.Count()} 条记录");
                     }
                     else
                     {
@@ -386,6 +400,7 @@ public static class SqlSugarSetup
                     }
                 }
             }
+            _isHandlingSeedData = false;
         }
     }
 

+ 2 - 2
Admin.NET/Admin.NET.Core/Util/ComputerUtil.cs

@@ -147,16 +147,16 @@ public static class ComputerUtil
     /// <returns></returns>
     public static string GetIpFromOnline()
     {
-        var url = "https://www.ip.cn/api/index?ip&type=0";
         try
         {
+            var url = "https://www.ip.cn/api/index?ip&type=0";
             var str = url.GetAsStringAsync().GetAwaiter().GetResult();
             var resp = JSON.Deserialize<IpCnResp>(str);
             return resp.Ip + " " + resp.Address;
         }
         catch
         {
-            return "";
+            return "unknow";
         }
     }
 

+ 3 - 3
Admin.NET/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj

@@ -23,12 +23,12 @@
   </ItemGroup>
 
   <ItemGroup>
-    <Content Update="wwwroot\Avatar\**" CopyToPublishDirectory="Never" />
+    <Content Update="wwwroot\avatar\**" CopyToPublishDirectory="Never" />
     <Content Update="wwwroot\upload\**" CopyToPublishDirectory="Never" />
   </ItemGroup>
 
   <ItemGroup>
-    <Content Update="wwwroot\Template\**">
+    <Content Update="wwwroot\template\**">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
   </ItemGroup>
@@ -49,7 +49,7 @@
   </ItemGroup>
 	
   <ItemGroup>
-    <Content Update="wwwroot\Upload\logo.png">
+    <Content Update="wwwroot\upload\logo.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
   </ItemGroup>

+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/Dto.cs.vm → Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Dto.cs.vm


+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/Entity.cs.vm → Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Entity.cs.vm


+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/Input.cs.vm → Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Input.cs.vm


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


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


+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/SeedData.cs.vm → Admin.NET/Admin.NET.Web.Entry/wwwroot/template/SeedData.cs.vm


+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/Service.cs.vm → Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Service.cs.vm


+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/data.data.ts.vm → Admin.NET/Admin.NET.Web.Entry/wwwroot/template/data.data.ts.vm


+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/dataModal.vue.vm → Admin.NET/Admin.NET.Web.Entry/wwwroot/template/dataModal.vue.vm


+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/editDialog.vue.vm → Admin.NET/Admin.NET.Web.Entry/wwwroot/template/editDialog.vue.vm


+ 6 - 3
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/index.vue.vm → Admin.NET/Admin.NET.Web.Entry/wwwroot/template/index.vue.vm

@@ -263,7 +263,10 @@
   const tableParams = ref({
     page: 1,
     pageSize: 10,
-    total: 0,
+    field: 'createTime', // 默认的排序字段
+    order: 'descending', // 排序方向
+    descstr: 'descending', // 降序排序的关键字符
+    total: 0 as any,
   });
 
   const print@(@Model.ClassName)Title = ref("");
@@ -285,8 +288,8 @@
 
   // 列排序
   const sortChange = async (column: any) => {
-	queryParams.value.field = column.prop;
-	queryParams.value.order = column.order;
+	tableParams.value.field = column.prop;
+	tableParams.value.order = column.order;
 	await handleQuery();
   };
 

+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/wwwroot/Upload/logo.png → Admin.NET/Admin.NET.Web.Entry/wwwroot/upload/logo.png


+ 7 - 1
Web/.env

@@ -10,5 +10,11 @@ VITE_OPEN_CDN = false
 # public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可
 VITE_PUBLIC_PATH =
 
-# SM公钥
+# 登陆界面默认用户
+VITE_DEFAULT_USER = superadmin
+
+# 登陆界面默认密码
+VITE_DEFAULT_USER_PASSWORD = 123456
+
+# 国密SM公钥
 VITE_SM_PUBLIC_KEY = "0484C7466D950E120E5ECE5DD85D0C90EAA85081A3A2BD7C57AE6DC822EFCCBD66620C67B0103FC8DD280E36C3B282977B722AAEC3C56518EDCEBAFB72C5A05312"

+ 23 - 23
Web/package.json

@@ -2,7 +2,7 @@
 	"name": "admin.net",
 	"type": "module",
 	"version": "2.4.33",
-	"lastBuildTime": "2024.08.04",
+	"lastBuildTime": "2024.08.19",
 	"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
 	"author": "zuohuaijun",
 	"license": "MIT",
@@ -21,78 +21,78 @@
 		"@vue-office/docx": "^1.6.2",
 		"@vue-office/excel": "^1.7.11",
 		"@vue-office/pdf": "^2.0.2",
-		"@vueuse/core": "^10.11.0",
+		"@vueuse/core": "^10.11.1",
 		"@wangeditor/editor": "^5.1.23",
 		"@wangeditor/editor-for-vue": "^5.1.12",
 		"animate.css": "^4.1.1",
 		"async-validator": "^4.2.5",
-		"axios": "^1.7.3",
+		"axios": "^1.7.4",
 		"countup.js": "^2.8.0",
 		"cropperjs": "^1.6.2",
 		"echarts": "^5.5.1",
 		"echarts-gl": "^2.0.9",
 		"echarts-wordcloud": "^2.1.0",
-		"element-plus": "^2.7.8",
+		"element-plus": "^2.8.0",
 		"ezuikit": "^1.0.0",
-		"ezuikit-js": "^8.0.8",
+		"ezuikit-js": "^8.0.9",
 		"js-cookie": "^3.0.5",
 		"js-table2excel": "^1.1.2",
 		"jsplumb": "^2.15.6",
 		"lodash-es": "^4.17.21",
-		"md-editor-v3": "^4.17.4",
+		"md-editor-v3": "^4.18.1",
 		"mitt": "^3.0.1",
 		"monaco-editor": "^0.50.0",
 		"mqtt": "^4.3.8",
 		"nprogress": "^0.2.0",
-		"pinia": "^2.2.0",
+		"pinia": "^2.2.2",
 		"print-js": "^1.6.0",
 		"push.js": "^1.0.12",
 		"qrcodejs2-fixes": "^0.0.2",
 		"qs": "^6.13.0",
 		"relation-graph": "^2.2.3",
 		"screenfull": "^6.0.2",
-		"sm-crypto-v2": "^1.9.1",
+		"sm-crypto-v2": "^1.9.2",
 		"sortablejs": "^1.15.2",
 		"splitpanes": "^3.1.5",
 		"vcrontab-3": "^3.3.22",
 		"vform3-builds": "^3.0.10",
-		"vue": "^3.4.35",
+		"vue": "^3.4.38",
 		"vue-clipboard3": "^2.0.0",
-		"vue-demi": "^0.14.10",
+		"vue-demi": "^0.14.6",
+		"vue-draggable-plus": "^0.5.3",
 		"vue-grid-layout": "3.0.0-beta1",
-		"vue-i18n": "^9.13.1",
+		"vue-i18n": "^9.14.0",
 		"vue-json-pretty": "^2.4.0",
-		"vue-plugin-hiprint": "0.0.57-beta20",
-		"vue-router": "^4.4.2",
+		"vue-plugin-hiprint": "0.0.57-beta27",
+		"vue-router": "^4.4.3",
 		"vue-signature-pad": "^3.0.2",
 		"vue3-tree-org": "^4.2.2",
-		"vuedraggable": "4.1.0",
 		"xlsx-js-style": "^1.2.0"
 	},
 	"devDependencies": {
 		"@plugin-web-update-notification/vite": "^1.7.1",
 		"@types/lodash-es": "^4.17.12",
-		"@types/node": "^20.14.13",
+		"@types/node": "^20.14.15",
 		"@types/nprogress": "^0.2.3",
 		"@types/sortablejs": "^1.15.8",
-		"@typescript-eslint/eslint-plugin": "^7.18.0",
-		"@typescript-eslint/parser": "^7.18.0",
+		"@typescript-eslint/eslint-plugin": "^8.1.0",
+		"@typescript-eslint/parser": "^8.1.0",
 		"@vitejs/plugin-vue": "^5.1.2",
-		"@vitejs/plugin-vue-jsx": "^4.0.0",
-		"@vue/compiler-sfc": "^3.4.35",
+		"@vitejs/plugin-vue-jsx": "^4.0.1",
+		"@vue/compiler-sfc": "^3.4.38",
 		"code-inspector-plugin": "^0.15.2",
 		"eslint": "^8.57.0",
 		"eslint-plugin-vue": "^9.27.0",
-		"globals": "^15.8.0",
+		"globals": "^15.9.0",
 		"less": "^4.2.0",
 		"prettier": "^3.3.3",
 		"rollup-plugin-visualizer": "^5.12.0",
 		"sass": "^1.77.8",
-		"terser": "^5.31.3",
+		"terser": "^5.31.6",
 		"typescript": "^5.5.4",
-		"vite": "^5.3.5",
+		"vite": "^5.4.1",
 		"vite-plugin-cdn-import": "^1.0.1",
-		"vite-plugin-compression2": "^1.1.3",
+		"vite-plugin-compression2": "^1.2.0",
 		"vite-plugin-vue-setup-extend": "^0.4.0",
 		"vue-eslint-parser": "^9.4.3"
 	},

+ 3 - 0
Web/public/print-lock.css

@@ -220,6 +220,9 @@ table.hiprint-printElement-tableTarget {
 .hiprint-printElement-tableTarget-border-td-all td:not(:nth-last-child(-n+2)) {
   border-right: 1px solid;
 }
+.hiprint-printElement-tableTarget-border-td-all td:not(last-child) {
+  border-right: 1px solid;
+}
 .hiprint-printElement-tableTarget-border-td-all td:last-child {
   border-left: 1px solid;
 }

+ 187 - 0
Web/src/api-services/apis/sys-cache-api.ts

@@ -17,6 +17,7 @@ import { Configuration } from '../configuration';
 // Some imports not used depending on template conditions
 // @ts-ignore
 import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
+import { AdminResultIDisposable } from '../models';
 import { AdminResultInt32 } from '../models';
 import { AdminResultListString } from '../models';
 import { AdminResultObject } from '../models';
@@ -26,6 +27,116 @@ import { AdminResultObject } from '../models';
  */
 export const SysCacheApiAxiosParamCreator = function (configuration?: Configuration) {
     return {
+        /**
+         * 
+         * @summary 申请分布式锁
+         * @param {string} key 要锁定的key
+         * @param {number} msTimeout 申请锁等待的时间,单位毫秒
+         * @param {number} msExpire 锁过期时间,超过该时间没有主动是放则自动是放,必须整数秒,单位毫秒
+         * @param {boolean} throwOnFailure 失败时是否抛出异常,如不抛出异常,可通过判断返回null得知申请锁失败
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost: async (key: string, msTimeout: number, msExpire: number, throwOnFailure: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'key' is not null or undefined
+            if (key === null || key === undefined) {
+                throw new RequiredError('key','Required parameter key was null or undefined when calling apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost.');
+            }
+            // verify required parameter 'msTimeout' is not null or undefined
+            if (msTimeout === null || msTimeout === undefined) {
+                throw new RequiredError('msTimeout','Required parameter msTimeout was null or undefined when calling apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost.');
+            }
+            // verify required parameter 'msExpire' is not null or undefined
+            if (msExpire === null || msExpire === undefined) {
+                throw new RequiredError('msExpire','Required parameter msExpire was null or undefined when calling apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost.');
+            }
+            // verify required parameter 'throwOnFailure' is not null or undefined
+            if (throwOnFailure === null || throwOnFailure === undefined) {
+                throw new RequiredError('throwOnFailure','Required parameter throwOnFailure was null or undefined when calling apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost.');
+            }
+            const localVarPath = `/api/sysCache/beginCacheLock/{key}/{msTimeout}/{msExpire}/{throwOnFailure}`
+                .replace(`{${"key"}}`, encodeURIComponent(String(key)))
+                .replace(`{${"msTimeout"}}`, encodeURIComponent(String(msTimeout)))
+                .replace(`{${"msExpire"}}`, encodeURIComponent(String(msExpire)))
+                .replace(`{${"throwOnFailure"}}`, encodeURIComponent(String(throwOnFailure)));
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            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 {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysCacheClearPost: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysCache/clear`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            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 根据键名前缀删除缓存 🔖
@@ -274,6 +385,36 @@ export const SysCacheApiAxiosParamCreator = function (configuration?: Configurat
  */
 export const SysCacheApiFp = function(configuration?: Configuration) {
     return {
+        /**
+         * 
+         * @summary 申请分布式锁
+         * @param {string} key 要锁定的key
+         * @param {number} msTimeout 申请锁等待的时间,单位毫秒
+         * @param {number} msExpire 锁过期时间,超过该时间没有主动是放则自动是放,必须整数秒,单位毫秒
+         * @param {boolean} throwOnFailure 失败时是否抛出异常,如不抛出异常,可通过判断返回null得知申请锁失败
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost(key: string, msTimeout: number, msExpire: number, throwOnFailure: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultIDisposable>>> {
+            const localVarAxiosArgs = await SysCacheApiAxiosParamCreator(configuration).apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost(key, msTimeout, msExpire, throwOnFailure, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 清空所有缓存 🔖
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysCacheClearPost(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysCacheApiAxiosParamCreator(configuration).apiSysCacheClearPost(options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
         /**
          * 
          * @summary 根据键名前缀删除缓存 🔖
@@ -352,6 +493,28 @@ export const SysCacheApiFp = function(configuration?: Configuration) {
  */
 export const SysCacheApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
     return {
+        /**
+         * 
+         * @summary 申请分布式锁
+         * @param {string} key 要锁定的key
+         * @param {number} msTimeout 申请锁等待的时间,单位毫秒
+         * @param {number} msExpire 锁过期时间,超过该时间没有主动是放则自动是放,必须整数秒,单位毫秒
+         * @param {boolean} throwOnFailure 失败时是否抛出异常,如不抛出异常,可通过判断返回null得知申请锁失败
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost(key: string, msTimeout: number, msExpire: number, throwOnFailure: boolean, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultIDisposable>> {
+            return SysCacheApiFp(configuration).apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost(key, msTimeout, msExpire, throwOnFailure, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 清空所有缓存 🔖
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysCacheClearPost(options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysCacheApiFp(configuration).apiSysCacheClearPost(options).then((request) => request(axios, basePath));
+        },
         /**
          * 
          * @summary 根据键名前缀删除缓存 🔖
@@ -411,6 +574,30 @@ export const SysCacheApiFactory = function (configuration?: Configuration, baseP
  * @extends {BaseAPI}
  */
 export class SysCacheApi extends BaseAPI {
+    /**
+     * 
+     * @summary 申请分布式锁
+     * @param {string} key 要锁定的key
+     * @param {number} msTimeout 申请锁等待的时间,单位毫秒
+     * @param {number} msExpire 锁过期时间,超过该时间没有主动是放则自动是放,必须整数秒,单位毫秒
+     * @param {boolean} throwOnFailure 失败时是否抛出异常,如不抛出异常,可通过判断返回null得知申请锁失败
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysCacheApi
+     */
+    public async apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost(key: string, msTimeout: number, msExpire: number, throwOnFailure: boolean, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultIDisposable>> {
+        return SysCacheApiFp(this.configuration).apiSysCacheBeginCacheLockKeyMsTimeoutMsExpireThrowOnFailurePost(key, msTimeout, msExpire, throwOnFailure, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 清空所有缓存 🔖
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysCacheApi
+     */
+    public async apiSysCacheClearPost(options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysCacheApiFp(this.configuration).apiSysCacheClearPost(options).then((request) => request(this.axios, this.basePath));
+    }
     /**
      * 
      * @summary 根据键名前缀删除缓存 🔖

+ 138 - 11
Web/src/api-services/apis/sys-file-api.ts

@@ -177,6 +177,78 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
                 options: localVarRequestOptions,
             };
         },
+        /**
+         * 
+         * @summary 获取文件 🔖
+         * @param {number} id 主键Id
+         * @param {string} [fileName] 文件名称
+         * @param {string} [fileType] 文件类型
+         * @param {boolean} [isPublic] 是否公开  若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+         * @param {string} [url] 文件Url
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysFileFileGet: async (id: number, fileName?: string, fileType?: string, isPublic?: boolean, url?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'id' is not null or undefined
+            if (id === null || id === undefined) {
+                throw new RequiredError('id','Required parameter id was null or undefined when calling apiSysFileFileGet.');
+            }
+            const localVarPath = `/api/sysFile/file`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            if (fileName !== undefined) {
+                localVarQueryParameter['FileName'] = fileName;
+            }
+
+            if (fileType !== undefined) {
+                localVarQueryParameter['FileType'] = fileType;
+            }
+
+            if (isPublic !== undefined) {
+                localVarQueryParameter['IsPublic'] = isPublic;
+            }
+
+            if (url !== undefined) {
+                localVarQueryParameter['Url'] = url;
+            }
+
+            if (id !== undefined) {
+                localVarQueryParameter['Id'] = id;
+            }
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
         /**
          * 
          * @summary 获取文件分页列表 🔖
@@ -227,7 +299,7 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
         },
         /**
          * 
-         * @summary 文件预览
+         * @summary 文件预览 🔖
          * @param {number} id 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
@@ -490,11 +562,12 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
          * @summary 上传文件 🔖
          * @param {Blob} [file] 
          * @param {string} [fileType] 
+         * @param {boolean} [isPublic] 
          * @param {string} [path] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        apiSysFileUploadFilePostForm: async (file?: Blob, fileType?: string, path?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+        apiSysFileUploadFilePostForm: async (file?: Blob, fileType?: string, isPublic?: boolean, path?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
             const localVarPath = `/api/sysFile/uploadFile`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
@@ -525,6 +598,10 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
                 localVarFormParams.append('FileType', fileType as any);
             }
 
+            if (isPublic !== undefined) { 
+                localVarFormParams.append('IsPublic', isPublic as any);
+            }
+
             if (path !== undefined) { 
                 localVarFormParams.append('Path', path as any);
             }
@@ -703,6 +780,24 @@ export const SysFileApiFp = function(configuration?: Configuration) {
                 return axios.request(axiosRequestArgs);
             };
         },
+        /**
+         * 
+         * @summary 获取文件 🔖
+         * @param {number} id 主键Id
+         * @param {string} [fileName] 文件名称
+         * @param {string} [fileType] 文件类型
+         * @param {boolean} [isPublic] 是否公开  若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+         * @param {string} [url] 文件Url
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysFileFileGet(id: number, fileName?: string, fileType?: string, isPublic?: boolean, url?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSysFile>>> {
+            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).apiSysFileFileGet(id, fileName, fileType, isPublic, url, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
         /**
          * 
          * @summary 获取文件分页列表 🔖
@@ -719,7 +814,7 @@ export const SysFileApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 文件预览
+         * @summary 文件预览 🔖
          * @param {number} id 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
@@ -795,12 +890,13 @@ export const SysFileApiFp = function(configuration?: Configuration) {
          * @summary 上传文件 🔖
          * @param {Blob} [file] 
          * @param {string} [fileType] 
+         * @param {boolean} [isPublic] 
          * @param {string} [path] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, path?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSysFile>>> {
-            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).apiSysFileUploadFilePostForm(file, fileType, path, options);
+        async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, isPublic?: boolean, path?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSysFile>>> {
+            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).apiSysFileUploadFilePostForm(file, fileType, isPublic, path, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -873,6 +969,20 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
         async apiSysFileDownloadFilePost(body?: FileInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultIActionResult>> {
             return SysFileApiFp(configuration).apiSysFileDownloadFilePost(body, options).then((request) => request(axios, basePath));
         },
+        /**
+         * 
+         * @summary 获取文件 🔖
+         * @param {number} id 主键Id
+         * @param {string} [fileName] 文件名称
+         * @param {string} [fileType] 文件类型
+         * @param {boolean} [isPublic] 是否公开  若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+         * @param {string} [url] 文件Url
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysFileFileGet(id: number, fileName?: string, fileType?: string, isPublic?: boolean, url?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysFile>> {
+            return SysFileApiFp(configuration).apiSysFileFileGet(id, fileName, fileType, isPublic, url, options).then((request) => request(axios, basePath));
+        },
         /**
          * 
          * @summary 获取文件分页列表 🔖
@@ -885,7 +995,7 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
         },
         /**
          * 
-         * @summary 文件预览
+         * @summary 文件预览 🔖
          * @param {number} id 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
@@ -941,12 +1051,13 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
          * @summary 上传文件 🔖
          * @param {Blob} [file] 
          * @param {string} [fileType] 
+         * @param {boolean} [isPublic] 
          * @param {string} [path] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, path?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysFile>> {
-            return SysFileApiFp(configuration).apiSysFileUploadFilePostForm(file, fileType, path, options).then((request) => request(axios, basePath));
+        async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, isPublic?: boolean, path?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysFile>> {
+            return SysFileApiFp(configuration).apiSysFileUploadFilePostForm(file, fileType, isPublic, path, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -1011,6 +1122,21 @@ export class SysFileApi extends BaseAPI {
     public async apiSysFileDownloadFilePost(body?: FileInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultIActionResult>> {
         return SysFileApiFp(this.configuration).apiSysFileDownloadFilePost(body, options).then((request) => request(this.axios, this.basePath));
     }
+    /**
+     * 
+     * @summary 获取文件 🔖
+     * @param {number} id 主键Id
+     * @param {string} [fileName] 文件名称
+     * @param {string} [fileType] 文件类型
+     * @param {boolean} [isPublic] 是否公开  若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+     * @param {string} [url] 文件Url
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysFileApi
+     */
+    public async apiSysFileFileGet(id: number, fileName?: string, fileType?: string, isPublic?: boolean, url?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysFile>> {
+        return SysFileApiFp(this.configuration).apiSysFileFileGet(id, fileName, fileType, isPublic, url, options).then((request) => request(this.axios, this.basePath));
+    }
     /**
      * 
      * @summary 获取文件分页列表 🔖
@@ -1024,7 +1150,7 @@ export class SysFileApi extends BaseAPI {
     }
     /**
      * 
-     * @summary 文件预览
+     * @summary 文件预览 🔖
      * @param {number} id 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
@@ -1085,13 +1211,14 @@ export class SysFileApi extends BaseAPI {
      * @summary 上传文件 🔖
      * @param {Blob} [file] 
      * @param {string} [fileType] 
+     * @param {boolean} [isPublic] 
      * @param {string} [path] 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof SysFileApi
      */
-    public async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, path?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysFile>> {
-        return SysFileApiFp(this.configuration).apiSysFileUploadFilePostForm(file, fileType, path, options).then((request) => request(this.axios, this.basePath));
+    public async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, isPublic?: boolean, path?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysFile>> {
+        return SysFileApiFp(this.configuration).apiSysFileUploadFilePostForm(file, fileType, isPublic, path, options).then((request) => request(this.axios, this.basePath));
     }
     /**
      * 

+ 69 - 0
Web/src/api-services/models/admin-result-idisposable.ts

@@ -0,0 +1,69 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { IDisposable } from './idisposable';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultIDisposable
+ */
+export interface AdminResultIDisposable {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultIDisposable
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultIDisposable
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultIDisposable
+     */
+    message?: string | null;
+
+    /**
+     * @type {IDisposable}
+     * @memberof AdminResultIDisposable
+     */
+    result?: IDisposable;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultIDisposable
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultIDisposable
+     */
+    time?: Date;
+}

+ 8 - 0
Web/src/api-services/models/file-input.ts

@@ -44,6 +44,14 @@ export interface FileInput {
      */
     fileType?: string | null;
 
+    /**
+     * 是否公开  若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+     *
+     * @type {boolean}
+     * @memberof FileInput
+     */
+    isPublic?: boolean;
+
     /**
      * 文件Url
      *

+ 8 - 0
Web/src/api-services/models/file-output.ts

@@ -76,6 +76,14 @@ export interface FileOutput {
      */
     fileType?: string | null;
 
+    /**
+     * 是否公开  若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+     *
+     * @type {boolean}
+     * @memberof FileOutput
+     */
+    isPublic?: boolean;
+
     /**
      * 上传人
      *

+ 22 - 0
Web/src/api-services/models/idisposable.ts

@@ -0,0 +1,22 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 
+ *
+ * @export
+ * @interface IDisposable
+ */
+export interface IDisposable {
+}

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

@@ -25,6 +25,7 @@ export * from './admin-result-data-table';
 export * from './admin-result-dictionary-string-string';
 export * from './admin-result-generate-qrimage-output';
 export * from './admin-result-iaction-result';
+export * from './admin-result-idisposable';
 export * from './admin-result-int32';
 export * from './admin-result-int64';
 export * from './admin-result-jobject';
@@ -203,6 +204,7 @@ export * from './iaction-result';
 export * from './icomponent';
 export * from './icontainer';
 export * from './icustom-attribute-provider';
+export * from './idisposable';
 export * from './isite';
 export * from './info-save-input';
 export * from './int-ptr';

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

@@ -36,6 +36,14 @@ export interface SysFileUploadFileBody {
      */
     fileType?: string;
 
+    /**
+     * 是否公开  若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+     *
+     * @type {boolean}
+     * @memberof SysFileUploadFileBody
+     */
+    isPublic?: boolean;
+
     /**
      * 文件路径
      *

+ 32 - 0
Web/src/api-services/models/sys-file.ts

@@ -84,6 +84,30 @@ export interface SysFile {
      */
     isDelete?: boolean;
 
+    /**
+     * 创建者部门Id
+     *
+     * @type {number}
+     * @memberof SysFile
+     */
+    createOrgId?: number | null;
+
+    /**
+     * 创建者部门名称
+     *
+     * @type {string}
+     * @memberof SysFile
+     */
+    createOrgName?: string | null;
+
+    /**
+     * 租户Id
+     *
+     * @type {number}
+     * @memberof SysFile
+     */
+    tenantId?: number | null;
+
     /**
      * 提供者
      *
@@ -187,4 +211,12 @@ export interface SysFile {
      * @memberof SysFile
      */
     fileType?: string | null;
+
+    /**
+     * 是否公开  若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+     *
+     * @type {boolean}
+     * @memberof SysFile
+     */
+    isPublic?: boolean;
 }

+ 8 - 0
Web/src/api-services/models/upload-file-from-base64-input.ts

@@ -59,4 +59,12 @@ export interface UploadFileFromBase64Input {
      * @memberof UploadFileFromBase64Input
      */
     fileType?: string | null;
+
+    /**
+     * 是否公开  若为true则所有人都可以查看,默认只有自己或有权限的可以查看
+     *
+     * @type {boolean}
+     * @memberof UploadFileFromBase64Input
+     */
+    isPublic?: boolean;
 }

+ 14 - 9
Web/src/layout/navBars/topBar/user.vue

@@ -52,7 +52,7 @@
 				<ele-User />
 			</el-icon>
 		</div>
-		<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
+		<el-dropdown :show-timeout="70" :hide-timeout="50" size="large" @command="onHandleCommandClick">
 			<span class="layout-navbars-breadcrumb-user-link">
 				<el-tooltip effect="dark" placement="left">
 					<template #content>
@@ -65,7 +65,6 @@
 					</template>
 					<img :src="userInfos.avatar" class="layout-navbars-breadcrumb-user-link-photo mr5" />
 				</el-tooltip>
-
 				{{ userInfos.realName == '' ? userInfos.account : userInfos.realName }}
 				<el-icon class="el-icon--right">
 					<ele-ArrowDown />
@@ -74,8 +73,9 @@
 			<template #dropdown>
 				<el-dropdown-menu>
 					<!-- <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-item :icon="Avatar" command="/system/userCenter">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
+					<el-dropdown-item :icon="Loading" command="clearCache">{{ $t('message.user.dropdown3') }}</el-dropdown-item>
+					<el-dropdown-item :icon="CircleCloseFilled" divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
 				</el-dropdown-menu>
 			</template>
 		</el-dropdown>
@@ -85,7 +85,7 @@
 </template>
 
 <script setup lang="ts" name="layoutBreadcrumbUser">
-import { defineAsyncComponent, ref, computed, reactive, onMounted, onUnmounted } from 'vue';
+import { defineAsyncComponent, ref, computed, reactive, onMounted } from 'vue';
 import { useRouter } from 'vue-router';
 import { ElMessageBox, ElMessage, ElNotification } from 'element-plus';
 import screenfull from 'screenfull';
@@ -95,12 +95,13 @@ import { useUserInfo } from '/@/stores/userInfo';
 import { useThemeConfig } from '/@/stores/themeConfig';
 import other from '/@/utils/other';
 import mittBus from '/@/utils/mitt';
-import { Local } from '/@/utils/storage';
+import { Local, Session } from '/@/utils/storage';
+import Push from 'push.js';
+import { signalR } from '/@/views/system/onlineUser/signalR';
+import { Avatar, CircleCloseFilled, Loading } from '@element-plus/icons-vue';
 
 import { clearAccessTokens, getAPI } from '/@/utils/axios-utils';
 import { SysAuthApi, SysNoticeApi } from '/@/api-services/api';
-import Push from 'push.js';
-import { signalR } from '/@/views/system/onlineUser/signalR';
 
 // 引入组件
 const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/topBar/userNews.vue'));
@@ -153,7 +154,11 @@ const onLayoutSetingClick = () => {
 };
 // 下拉菜单点击时
 const onHandleCommandClick = (path: string) => {
-	if (path === 'logOut') {
+	if (path === 'clearCache') {
+		Local.clear();
+		Session.clear();
+		window.location.reload();
+	} else if (path === 'logOut') {
 		ElMessageBox({
 			closeOnClickModal: false,
 			closeOnPressEscape: false,

+ 12 - 9
Web/src/main.ts

@@ -5,18 +5,21 @@ import router from '/@/router';
 import { directive } from '/@/directive/index';
 import { i18n } from '/@/i18n/index';
 import other from '/@/utils/other';
-
 import ElementPlus from 'element-plus';
 import '/@/theme/index.scss';
+// 动画库
+import 'animate.css';
+// 栅格布局
 import VueGridLayout from 'vue-grid-layout';
-
-import VForm3 from 'vform3-builds'; // VForm3表单设计
-import 'vform3-builds/dist/designer.style.css'; // VForm3表单设计样式
-import VueSignaturePad from 'vue-signature-pad'; // 电子签名
-import vue3TreeOrg from 'vue3-tree-org'; // 组织架构图
-import 'vue3-tree-org/lib/vue3-tree-org.css'; // 组织架构图样式
-import 'animate.css'; // 动画库
-
+// 电子签名
+import VueSignaturePad from 'vue-signature-pad';
+// 组织架构图
+import vue3TreeOrg from 'vue3-tree-org';
+import 'vue3-tree-org/lib/vue3-tree-org.css';
+// VForm3 表单设计
+import VForm3 from 'vform3-builds';
+import 'vform3-builds/dist/designer.style.css';
+// 关闭自动打印
 import { disAutoConnect } from 'vue-plugin-hiprint';
 disAutoConnect();
 

+ 1 - 1
Web/src/stores/userInfo.ts

@@ -76,7 +76,7 @@ export const useUserInfo = defineStore('userInfo', {
 							idCardNum: d.idCardNum,
 							email: d.email,
 							accountType: d.accountType,
-							avatar: d.avatar ?? '/Upload/logo.png',
+							avatar: d.avatar ?? '/upload/logo.png',
 							address: d.address,
 							signature: d.signature,
 							orgId: d.orgId,

+ 1 - 1
Web/src/views/home/widgets/components/about.vue

@@ -21,7 +21,7 @@
 <script lang="ts">
 export default {
 	title: '关于项目',
-	icon: 'ele-Setting',
+	icon: 'ele-QuestionFilled',
 	description: '点个星星支持一下',
 };
 </script>

+ 5 - 1
Web/src/views/home/widgets/components/commit.vue

@@ -19,7 +19,7 @@
 <script lang="ts">
 export default {
 	title: '更新记录',
-	icon: 'ele-Message',
+	icon: 'ele-DocumentCopy',
 	description: '当前项目更新记录',
 };
 </script>
@@ -69,4 +69,8 @@ onMounted(() => {
 	font-size: 12px;
 	margin-top: 10px;
 }
+.commit {
+	max-height: 500px;
+	overflow: auto;
+}
 </style>

+ 19 - 23
Web/src/views/home/widgets/components/myapp.vue

@@ -1,5 +1,9 @@
 <template>
 	<el-card shadow="hover" header="快捷入口">
+		<template #header>
+			<el-icon style="display: inline; vertical-align: middle"> <ele-Guide /> </el-icon>
+			<span style=""> 快捷入口 </span>
+		</template>
 		<ul class="myMods">
 			<li v-for="mod in myMods" :key="mod.path!">
 				<router-link :to="{ path: mod.path! }">
@@ -17,25 +21,21 @@
 		<el-drawer title="添加应用" v-model="modsDrawer" :size="520" destroy-on-close :before-close="beforeClose">
 			<div class="setMods mt15">
 				<h4>我的常用 ( {{ myMods.length }} )</h4>
-				<draggable tag="ul" v-model="myMods" animation="200" item-key="id" group="app" class="draggable-box" force-fallback fallback-on-body>
-					<template #item="{ element }">
-						<li>
-							<SvgIcon :name="element.meta.icon" style="font-size: 18px" />
-							<p>{{ element.meta.title }}</p>
-						</li>
-					</template>
-				</draggable>
+				<VueDraggable tag="ul" v-model="myMods" :animation="200" group="app" class="draggable-box">
+					<li v-for="item in myMods" :key="item.id">
+						<SvgIcon :name="item.meta?.icon" style="font-size: 18px" />
+						<p>{{ item.meta?.title }}</p>
+					</li>
+				</VueDraggable>
 			</div>
 			<div class="setMods">
 				<h4>全部应用 ( {{ filterMods.length }} )</h4>
-				<draggable tag="ul" v-model="filterMods" animation="200" item-key="id" group="app" class="draggable-box-all" force-fallback fallback-on-body>
-					<template #item="{ element }">
-						<li :style="{ background: element.meta.color || '#909399' }">
-							<SvgIcon :name="element.meta.icon" style="font-size: 18px" />
-							<p>{{ element.meta.title }}</p>
-						</li>
-					</template>
-				</draggable>
+				<VueDraggable tag="ul" v-model="filterMods" :animation="200" group="app" class="draggable-box-all">
+					<li v-for="item in filterMods" :key="item.id" :style="{ background: '#909399' }">
+						<SvgIcon :name="item.meta?.icon" style="font-size: 18px" />
+						<p>{{ item.meta?.title }}</p>
+					</li>
+				</VueDraggable>
 			</div>
 			<template #footer>
 				<div style="margin: 0 20px 20px 0">
@@ -50,17 +50,17 @@
 <script lang="ts">
 export default {
 	title: '快捷入口',
-	icon: 'ele-Monitor',
+	icon: 'ele-Guide',
 	description: '可以配置的快捷入口',
 };
 </script>
 
 <script setup lang="ts" name="myapp">
-import { reactive, onMounted, ref } from 'vue';
+import { onMounted, ref } from 'vue';
 import { ElMessage } from 'element-plus';
 import { useRequestOldRoutes } from '/@/stores/requestOldRoutes';
 import { storeToRefs } from 'pinia';
-import draggable from 'vuedraggable';
+import { VueDraggable } from 'vue-draggable-plus';
 import { useUserInfo } from '/@/stores/userInfo';
 
 import { getAPI } from '/@/utils/axios-utils';
@@ -74,10 +74,6 @@ const filterMods = ref<MenuOutput[]>([]); // 过滤我的常用后的应用
 const modsDrawer = ref<boolean>(false);
 
 const { userInfos } = storeToRefs(useUserInfo());
-const state = reactive({
-	navError: '',
-	navData: [],
-});
 
 onMounted(() => {
 	getMods();

+ 8 - 11
Web/src/views/home/widgets/components/schedule.vue

@@ -8,16 +8,12 @@
 
 		<div class="custome-canlendar">
 			<el-calendar v-model="state.calendarValue" ref="calendar">
-				<template #header="{date }">
-					<span>{{ date  }}</span>
+				<template #header="{ date }">
+					<span>{{ date }}</span>
 					<el-button-group>
-						<el-button size="small" @click="selectDate('prev-month')">
-							上个月
-						</el-button>
+						<el-button size="small" @click="selectDate('prev-month')"> 上个月 </el-button>
 						<el-button size="small" @click="selectDate('today')">今天</el-button>
-						<el-button size="small" @click="selectDate('next-month')">
-							下个月
-						</el-button>
+						<el-button size="small" @click="selectDate('next-month')"> 下个月 </el-button>
 					</el-button-group>
 				</template>
 				<template #date-cell="{ data }">
@@ -30,7 +26,6 @@
 							{{ solarDate2lunar(data.day) }}
 						</div>
 					</div>
-
 				</template>
 			</el-calendar>
 		</div>
@@ -58,7 +53,7 @@
 <script lang="ts">
 export default {
 	title: '日程',
-	icon: 'ele-Odometer',
+	icon: 'ele-Calendar',
 	description: '日程演示',
 };
 </script>
@@ -82,6 +77,7 @@ const state = reactive({
 	TodayScheduleData: [] as Array<SysSchedule>, // 当天列表数据
 	calendarValue: new Date(),
 	queryParams: {
+		scheduleTime: new Date(),
 		startTime: new Date(),
 		endTime: new Date(),
 	},
@@ -164,7 +160,7 @@ const openAddSchedule = () => {
 	var timerange = GetRecentTime();
 
 	state.editTitle = '添加日程';
-	editScheduleRef.value?.openDialog({ id: undefined, status: 0, orderNo: 100, startTime: timerange.startTime, endTime: timerange.endTime });
+	editScheduleRef.value?.openDialog({ id: undefined, status: 0, orderNo: 100, scheduleTime: state.queryParams.scheduleTime, startTime: timerange.startTime, endTime: timerange.endTime });
 };
 
 // 打开编辑页面
@@ -180,6 +176,7 @@ const handleClickDate = async (data: any) => {
 		await handleQuery();
 	}
 	await handleQueryByDate(data.day);
+	state.queryParams.scheduleTime = data.day;
 };
 
 // 获取当月第一天

+ 1 - 1
Web/src/views/home/widgets/components/version.vue

@@ -19,7 +19,7 @@
 <script lang="ts">
 export default {
 	title: '版本信息',
-	icon: 'ele-Monitor',
+	icon: 'ele-InfoFilled',
 	description: '版本信息原子组件演示',
 };
 </script>

+ 1 - 1
Web/src/views/home/widgets/components/welcome.vue

@@ -45,7 +45,7 @@ const { themeConfig } = storeToRefs(storesThemeConfig);
 
 export default {
 	title: '欢迎',
-	icon: 'ele-Present',
+	icon: 'ele-Promotion',
 	description: '项目特色以及文档链接',
 };
 </script>

+ 124 - 122
Web/src/views/home/widgets/index.vue

@@ -1,150 +1,144 @@
 <template>
-	<div>
-		<NoticeBar style="margin: 4px" />
-		<div style="float: right; margin: -35px 10px 0 0">
-			<el-button v-if="customizing" type="warning" icon="ele-Check" circle plain @click="save"></el-button>
-			<el-button v-else type="warning" icon="ele-Edit" circle plain @click="custom"></el-button>
+	<div style="height: 100vh; overflow: hidden">
+		<div class="noticebar" style="display: flex">
+			<NoticeBar />
+			<div class="editlayout">
+				<el-tooltip content="编辑/保存布局" placement="bottom">
+					<el-button v-if="customizing" type="warning" icon="ele-Check" circle plain @click="save"></el-button>
+					<el-button v-else type="warning" icon="ele-Edit" circle plain @click="custom"></el-button>
+				</el-tooltip>
+			</div>
 		</div>
-	</div>
 
-	<div :class="['widgets-home', customizing ? 'customizing' : '']" ref="main">
-		<div class="widgets-content">
-			<!-- <div class="widgets-top">
+		<div :class="['widgets-home', customizing ? 'customizing' : '']" ref="main">
+			<div class="widgets-content">
+				<!-- <div class="widgets-top">
 				<div class="widgets-top-title">控制台</div>
 				<div class="widgets-top-actions">
 					<el-button v-if="customizing" type="primary" icon="ele-Check" round @click="save">完成</el-button>
 					<el-button v-else type="primary" icon="ele-Edit" round @click="custom">自定义</el-button>
 				</div>
 			</div> -->
-			<div class="widgets" ref="widgetsRef">
-				<div class="widgets-wrapper">
-					<div v-if="nowCompsList.length <= 0" class="no-widgets">
-						<el-empty description="没有部件啦" :image-size="300"></el-empty>
-					</div>
-					<el-row :gutter="8">
-						<el-col v-for="(item, index) in grid.layout" :key="index" :md="item" :xs="24">
-							<draggable
-								v-model="grid.copmsList[index]"
-								animation="200"
-								handle=".customize-overlay"
-								group="people"
-								item-key="com"
-								drag-class="aaaaa"
-								force-fallback
-								fallback-on-body
-								class="draggable-box"
-							>
-								<template #item="{ element }">
-									<div class="widgets-item mb8">
-										<component :is="allComps[element]"></component>
-										<div v-if="customizing" class="customize-overlay">
-											<el-button class="close" type="danger" plain icon="ele-Close" @click="remove(element)"></el-button>
-											<label v-if="allComps[element]">
-												<el-icon> <component :is="allComps[element].icon" /> </el-icon>{{ allComps[element].title }}
-											</label>
+				<div class="widgets" ref="widgetsRef">
+					<div class="widgets-wrapper">
+						<div v-if="nowCompsList.length <= 0" class="no-widgets">
+							<el-empty description="没有部件啦" :image-size="300"></el-empty>
+						</div>
+						<el-row :gutter="8">
+							<el-col v-for="(item, index) in grid.layout" :key="index" :md="item" :xs="24">
+								<VueDraggable v-model="grid.copmsList[index]" :animation="200" group="grid" handle=".customize-overlay" class="draggable-box">
+									<div v-for="item in grid.copmsList[index]" :key="item">
+										<div class="widgets-item mb8">
+											<component :is="allComps[item]"></component>
+											<div v-if="customizing" class="customize-overlay">
+												<el-button class="close" type="danger" plain icon="ele-Close" @click="remove(item)"></el-button>
+												<label v-if="allComps[item]">
+													<el-icon> <component :is="allComps[item].icon" /> </el-icon>{{ allComps[item].title }}
+												</label>
+											</div>
 										</div>
 									</div>
-								</template>
-							</draggable>
-						</el-col>
-					</el-row>
+								</VueDraggable>
+							</el-col>
+						</el-row>
+					</div>
 				</div>
 			</div>
-		</div>
 
-		<div v-if="customizing" class="widgets-aside">
-			<div class="widgets-top">
-				<div class="widgets-aside-title">
-					<el-icon><ele-CirclePlusFilled /></el-icon>添加部件
-				</div>
-				<div class="widgets-top-actions">
-					<div class="widgets-aside-close" @click="close">
-						<el-icon><ele-Close /></el-icon>
+			<div v-if="customizing" class="widgets-aside">
+				<div class="widgets-top">
+					<div class="widgets-aside-title">
+						<el-icon><ele-CirclePlusFilled /></el-icon>添加部件
 					</div>
-				</div>
-			</div>
-			<el-container>
-				<el-header style="height: auto">
-					<div class="selectLayout">
-						<div class="selectLayout-item item01" :class="{ active: grid.layout.join(',') === '12,6,6' }" @click="setLayout([12, 6, 6])">
-							<el-row :gutter="2">
-								<el-col :span="12"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-							</el-row>
-						</div>
-						<div class="selectLayout-item item02" :class="{ active: grid.layout.join(',') === '24,16,8' }" @click="setLayout([24, 16, 8])">
-							<el-row :gutter="2">
-								<el-col :span="24"><span></span></el-col>
-								<el-col :span="16"><span></span></el-col>
-								<el-col :span="8"><span></span></el-col>
-							</el-row>
-						</div>
-						<div class="selectLayout-item item03" :class="{ active: grid.layout.join(',') === '24' }" @click="setLayout([24])">
-							<el-row :gutter="2">
-								<el-col :span="24"><span></span></el-col>
-								<el-col :span="24"><span></span></el-col>
-								<el-col :span="24"><span></span></el-col>
-							</el-row>
-						</div>
-						<div class="selectLayout-item item01" :class="{ active: grid.layout.join(',') === '6,12,6' }" @click="setLayout([6, 12, 6])">
-							<el-row :gutter="2">
-								<el-col :span="6"><span></span></el-col>
-								<el-col :span="12"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-							</el-row>
-						</div>
-						<div class="selectLayout-item item02" :class="{ active: grid.layout.join(',') === '24,6,12,6' }" @click="setLayout([24, 6, 12, 6])">
-							<el-row :gutter="2">
-								<el-col :span="24"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-								<el-col :span="12"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-							</el-row>
-						</div>
-						<div class="selectLayout-item item05" :class="{ active: grid.layout.join(',') === '24,6,12,6,24' }" @click="setLayout([24, 6, 12, 6, 24])">
-							<el-row :gutter="2">
-								<el-col :span="24"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-								<el-col :span="12"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-								<el-col :span="24"><span></span></el-col>
-							</el-row>
+					<div class="widgets-top-actions">
+						<div class="widgets-aside-close" @click="close">
+							<el-icon><ele-Close /></el-icon>
 						</div>
 					</div>
-				</el-header>
-				<el-main class="nopadding">
-					<div class="widgets-list">
-						<div v-if="myCompsList.length <= 0" class="widgets-list-nodata">
-							<el-empty description="没有部件啦" :image-size="60"></el-empty>
-						</div>
-						<div v-for="item in myCompsList" :key="item.title" class="widgets-list-item">
-							<div class="item-logo">
-								<el-icon>
-									<component :is="item.icon" />
-								</el-icon>
+				</div>
+				<el-container>
+					<el-header style="height: auto">
+						<div class="selectLayout">
+							<div class="selectLayout-item item01" :class="{ active: grid.layout.join(',') === '12,6,6' }" @click="setLayout([12, 6, 6])">
+								<el-row :gutter="2">
+									<el-col :span="12"><span></span></el-col>
+									<el-col :span="6"><span></span></el-col>
+									<el-col :span="6"><span></span></el-col>
+								</el-row>
+							</div>
+							<div class="selectLayout-item item02" :class="{ active: grid.layout.join(',') === '24,16,8' }" @click="setLayout([24, 16, 8])">
+								<el-row :gutter="2">
+									<el-col :span="24"><span></span></el-col>
+									<el-col :span="16"><span></span></el-col>
+									<el-col :span="8"><span></span></el-col>
+								</el-row>
+							</div>
+							<div class="selectLayout-item item03" :class="{ active: grid.layout.join(',') === '24' }" @click="setLayout([24])">
+								<el-row :gutter="2">
+									<el-col :span="24"><span></span></el-col>
+									<el-col :span="24"><span></span></el-col>
+									<el-col :span="24"><span></span></el-col>
+								</el-row>
 							</div>
-							<div class="item-info">
-								<h2>{{ item.title }}</h2>
-								<p>{{ item.description }}</p>
+							<div class="selectLayout-item item01" :class="{ active: grid.layout.join(',') === '6,12,6' }" @click="setLayout([6, 12, 6])">
+								<el-row :gutter="2">
+									<el-col :span="6"><span></span></el-col>
+									<el-col :span="12"><span></span></el-col>
+									<el-col :span="6"><span></span></el-col>
+								</el-row>
 							</div>
-							<div class="item-actions">
-								<el-button type="primary" icon="ele-Plus" @click="push(item)"></el-button>
+							<div class="selectLayout-item item02" :class="{ active: grid.layout.join(',') === '24,6,12,6' }" @click="setLayout([24, 6, 12, 6])">
+								<el-row :gutter="2">
+									<el-col :span="24"><span></span></el-col>
+									<el-col :span="6"><span></span></el-col>
+									<el-col :span="12"><span></span></el-col>
+									<el-col :span="6"><span></span></el-col>
+								</el-row>
+							</div>
+							<div class="selectLayout-item item05" :class="{ active: grid.layout.join(',') === '24,6,12,6,24' }" @click="setLayout([24, 6, 12, 6, 24])">
+								<el-row :gutter="2">
+									<el-col :span="24"><span></span></el-col>
+									<el-col :span="6"><span></span></el-col>
+									<el-col :span="12"><span></span></el-col>
+									<el-col :span="6"><span></span></el-col>
+									<el-col :span="24"><span></span></el-col>
+								</el-row>
 							</div>
 						</div>
-					</div>
-				</el-main>
-				<el-footer style="height: 51px">
-					<el-button @click="backDefault">恢复默认</el-button>
-				</el-footer>
-			</el-container>
+					</el-header>
+					<el-main class="nopadding">
+						<div class="widgets-list">
+							<div v-if="myCompsList.length <= 0" class="widgets-list-nodata">
+								<el-empty description="没有部件啦" :image-size="60"></el-empty>
+							</div>
+							<div v-for="item in myCompsList" :key="item.title" class="widgets-list-item">
+								<div class="item-logo">
+									<el-icon>
+										<component :is="item.icon" />
+									</el-icon>
+								</div>
+								<div class="item-info">
+									<h2>{{ item.title }}</h2>
+									<p>{{ item.description }}</p>
+								</div>
+								<div class="item-actions">
+									<el-button type="primary" icon="ele-Plus" @click="push(item)"></el-button>
+								</div>
+							</div>
+						</div>
+					</el-main>
+					<el-footer style="height: 51px">
+						<el-button @click="backDefault">恢复默认</el-button>
+					</el-footer>
+				</el-container>
+			</div>
 		</div>
 	</div>
 </template>
 
 <script setup lang="ts">
 import { ref, computed, onMounted, nextTick } from 'vue';
-import draggable from 'vuedraggable';
+import { VueDraggable } from 'vue-draggable-plus';
 import { clone } from '/@/utils/arrayOperation';
 import allComps from './components/index';
 import { Local } from '/@/utils/storage';
@@ -157,7 +151,7 @@ interface Grid {
 const defaultGrid = {
 	layout: [12, 6, 6],
 	copmsList: [
-		['welcome', 'commit'],
+		['welcome', 'myapp', 'commit'],
 		['about', 'version'],
 		['timer', 'schedule'],
 	],
@@ -275,7 +269,7 @@ const close = () => {
 	box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
 	position: relative;
 	overflow: auto;
-	padding-top: 20px;
+	padding-top: 10px;
 }
 .widgets-aside-title {
 	margin-top: 10px;
@@ -503,4 +497,12 @@ const close = () => {
 		margin-right: 0;
 	}
 }
+.noticebar {
+	margin: 5px;
+}
+.editlayout {
+	top: 18px;
+	right: 25px;
+	position: absolute;
+}
 </style>

+ 5 - 4
Web/src/views/login/component/account.vue

@@ -84,11 +84,11 @@ import { Local, Session } from '/@/utils/storage';
 import { formatAxis } from '/@/utils/formatTime';
 import { NextLoading } from '/@/utils/loading';
 import { sm2 } from 'sm-crypto-v2';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { storeToRefs } from 'pinia';
 
 import { accessTokenKey, clearTokens, feature, getAPI } from '/@/utils/axios-utils';
 import { SysAuthApi } from '/@/api-services/api';
-import { useThemeConfig } from '/@/stores/themeConfig';
-import { storeToRefs } from 'pinia';
 
 // 旋转图片滑块组件
 // import verifyImg from '/@/assets/logo-mini.svg';
@@ -110,8 +110,8 @@ const dragRef: any = ref(null);
 const state = reactive({
 	isShowPassword: false,
 	ruleForm: {
-		account: 'superadmin',
-		password: '123456',
+		account: window.__env__.VITE_DEFAULT_USER,
+		password: window.__env__.VITE_DEFAULT_USER_PASSWORD,
 		code: '',
 		codeId: 0,
 	},
@@ -144,6 +144,7 @@ onMounted(async () => {
 	if (accessToken != null && accessToken != undefined) {
 		await saveTokenAndInitRoutes(accessToken);
 	}
+
 	// 获取登录配置
 	state.secondVerEnabled = themeConfig.value.secondVer ?? true;
 	state.captchaEnabled = themeConfig.value.captcha ?? true;

+ 25 - 4
Web/src/views/system/cache/index.vue

@@ -10,12 +10,16 @@
 					<template #header>
 						<div class="card-header">
 							<span>缓存列表</span>
-							<el-button icon="ele-Refresh" size="small" circle @click="handleQuery" v-auth="'sysCache:keyList'" />
+							<div>
+								<el-button icon="ele-Refresh" size="small" type="success" circle plain @click="handleQuery" v-auth="'sysCache:keyList'" />
+								<el-button icon="ele-DeleteFilled" size="small" type="danger" circle plain @click="clearCache" v-auth="'sysCache:clear'"> </el-button>
+							</div>
 						</div>
 					</template>
 					<el-tree
 						ref="treeRef"
 						class="filter-tree"
+						style="padding-bottom: 60px"
 						:data="state.cacheData"
 						node-key="id"
 						:props="{ children: 'children', label: 'name' }"
@@ -36,7 +40,7 @@
 							<el-button icon="ele-Delete" size="small" type="danger" @click="delCache" v-auth="'sysCache:delete'"> 删除缓存 </el-button>
 						</div>
 					</template>
-					<vue-json-pretty :data="state.cacheValue" showLength showIcon showLineNumber showSelectController />
+					<vue-json-pretty :data="state.cacheValue" showLength showIcon showLineNumber showSelectController style="padding-bottom: 60px" />
 				</el-card>
 			</pane>
 		</splitpanes>
@@ -66,7 +70,7 @@ const state = reactive({
 });
 
 onMounted(async () => {
-	handleQuery();
+	await handleQuery();
 });
 
 // 查询操作
@@ -114,7 +118,7 @@ const delCache = () => {
 	})
 		.then(async () => {
 			await getAPI(SysCacheApi).apiSysCacheDeleteKeyPost(currentNode.value.id);
-			handleQuery();
+			await handleQuery();
 			state.cacheValue = undefined;
 			state.cacheKey = undefined;
 			ElMessage.success('删除成功');
@@ -122,6 +126,23 @@ const delCache = () => {
 		.catch(() => {});
 };
 
+// 清空
+const clearCache = () => {
+	ElMessageBox.confirm(`确认清空所有缓存?`, '提示', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	})
+		.then(async () => {
+			await getAPI(SysCacheApi).apiSysCacheClearDelete();
+			await handleQuery();
+			state.cacheValue = undefined;
+			state.cacheKey = undefined;
+			ElMessage.success('清空成功');
+		})
+		.catch(() => {});
+};
+
 // 树点击
 const nodeClick = async (node: any) => {
 	if (node.id == 0) return;

+ 13 - 2
Web/src/views/system/dict/index.vue

@@ -118,8 +118,11 @@
 								<ModifyRecord :data="scope.row" />
 							</template>
 						</el-table-column>
-						<el-table-column label="操作" width="80" fixed="right" align="center" show-overflow-tooltip>
+						<el-table-column label="操作" width="120" fixed="right" align="center" show-overflow-tooltip>
 							<template #default="scope">
+								<el-tooltip content="复制">
+									<el-button icon="ele-CopyDocument" size="small" text type="primary" @click="openCopyDictData(scope.row)"> </el-button>
+								</el-tooltip>
 								<el-tooltip content="编辑">
 									<el-button icon="ele-Edit" size="small" text type="primary" @click="openEditDictData(scope.row)"> </el-button>
 								</el-tooltip>
@@ -160,7 +163,7 @@ import { getAPI } from '/@/utils/axios-utils';
 import { Session } from '/@/utils/storage';
 import { useUserInfo } from '/@/stores/userInfo';
 import { SysDictTypeApi, SysDictDataApi } from '/@/api-services/api';
-import { SysDictType, SysDictData } from '/@/api-services/models';
+import { SysDictType, SysDictData, UpdateDictDataInput } from '/@/api-services/models';
 
 const editDictTypeRef = ref<InstanceType<typeof EditDictType>>();
 const editDictDataRef = ref<InstanceType<typeof EditDictData>>();
@@ -259,6 +262,14 @@ const openEditDictType = (row: any) => {
 	editDictTypeRef.value?.openDialog(row);
 };
 
+// 打开复制字典值页面
+const openCopyDictData = (row: any) => {
+	state.editDictDataTitle = '复制字典值';
+	var copyRow = JSON.parse(JSON.stringify(row)) as UpdateDictDataInput;
+	copyRow.id = 0;
+	editDictDataRef.value?.openDialog(copyRow);
+};
+
 // 打开编辑字典值页面
 const openEditDictData = (row: any) => {
 	state.editDictDataTitle = '编辑字典值';

+ 9 - 1
Web/src/views/system/file/component/editSysfile.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="sys-file-container">
-		<el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" width="500px">
+		<el-dialog v-model="state.isShowDialog" draggable overflow destroy-on-close width="500px">
 			<template #header>
 				<div style="color: #fff">
 					<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Edit /> </el-icon>
@@ -24,6 +24,14 @@
 							</el-select>
 						</el-form-item>
 					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="是否公开">
+							<el-radio-group v-model="state.ruleForm.isPublic">
+								<el-radio :value="false">否</el-radio>
+								<el-radio :value="true">是</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
 				</el-row>
 			</el-form>
 			<template #footer>

+ 14 - 2
Web/src/views/system/file/index.vue

@@ -53,6 +53,12 @@
 				<el-table-column prop="bucketName" label="存储位置" align="center" show-overflow-tooltip />
 				<el-table-column prop="id" label="存储标识" align="center" show-overflow-tooltip />
 				<el-table-column prop="fileType" label="文件类型" min-width="100" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="isPublic" label="是否公开" min-width="100" header-align="center" show-overflow-tooltip>
+					<template #default="scope">
+						<el-tag v-if="scope.row.isPublic === true" type="success">是</el-tag>
+						<el-tag v-else type="danger">否</el-tag>
+					</template>
+				</el-table-column>
 				<el-table-column type="relationName" label="关联对象名称" min-width="150" align="center" />
 				<el-table-column type="relationId" label="关联对象Id" align="center" />
 				<el-table-column type="belongId" label="所属Id" align="center" />
@@ -95,7 +101,11 @@
 					<el-option label="相关文件" value="相关文件" />
 					<el-option label="归档文件" value="归档文件" />
 				</el-select>
-
+				是否公开:
+				<el-radio-group v-model="state.isPublic">
+					<el-radio :value="false">否</el-radio>
+					<el-radio :value="true">是</el-radio>
+				</el-radio-group>
 				<el-upload ref="uploadRef" drag :auto-upload="false" :limit="1" :file-list="state.fileList" action="" :on-change="handleChange" accept=".jpg,.png,.bmp,.gif,.txt,.pdf,.xlsx,.docx">
 					<el-icon class="el-icon--upload">
 						<ele-UploadFilled />
@@ -173,6 +183,7 @@ const state = reactive({
 	pdfUrl: '',
 	fileName: '',
 	fileType: '',
+	isPublic: false,
 	previewList: [] as string[],
 });
 
@@ -205,6 +216,7 @@ const resetQuery = () => {
 const openUploadDialog = () => {
 	state.fileList = [];
 	state.dialogUploadVisible = true;
+	state.isPublic = false;
 };
 
 // 通过onChanne方法获得文件列表
@@ -215,7 +227,7 @@ const handleChange = (file: any, fileList: []) => {
 // 上传
 const uploadFile = async () => {
 	if (state.fileList.length < 1) return;
-	await getAPI(SysFileApi).apiSysFileUploadFilePostForm(state.fileList[0].raw, state.fileType, undefined);
+	await getAPI(SysFileApi).apiSysFileUploadFilePostForm(state.fileList[0].raw, state.fileType, state.isPublic, undefined);
 	handleQuery();
 	ElMessage.success('上传成功');
 	state.dialogUploadVisible = false;

+ 12 - 2
Web/src/views/system/menu/index.vue

@@ -54,8 +54,9 @@
 						<ModifyRecord :data="scope.row" />
 					</template>
 				</el-table-column>
-				<el-table-column label="操作" width="140" fixed="right" align="center" show-overflow-tooltip>
+				<el-table-column label="操作" width="210" fixed="right" align="center" show-overflow-tooltip>
 					<template #default="scope">
+						<el-button icon="ele-CopyDocument" size="small" text type="primary" @click="openCopyMenu(scope.row)" v-auth="'sysMenu:add'"> 复制 </el-button>
 						<el-button icon="ele-Edit" size="small" text type="primary" @click="openEditMenu(scope.row)" v-auth="'sysMenu:update'"> 编辑 </el-button>
 						<el-button icon="ele-Delete" size="small" text type="danger" @click="delMenu(scope.row)" v-auth="'sysMenu:delete'"> 删除 </el-button>
 					</template>
@@ -75,7 +76,7 @@ import ModifyRecord from '/@/components/table/modifyRecord.vue';
 
 import { getAPI } from '/@/utils/axios-utils';
 import { SysMenuApi } from '/@/api-services/api';
-import { SysMenu } from '/@/api-services/models';
+import { SysMenu, UpdateMenuInput } from '/@/api-services/models';
 
 const editMenuRef = ref<InstanceType<typeof EditMenu>>();
 const state = reactive({
@@ -113,6 +114,15 @@ const openAddMenu = () => {
 	editMenuRef.value?.openDialog({ type: 2, isHide: false, isKeepAlive: true, isAffix: false, isIframe: false, status: 1, orderNo: 100 });
 };
 
+// 打开复制页面
+const openCopyMenu = (row: any) => {
+	state.editMenuTitle = '复制菜单';
+	var copyRow = JSON.parse(JSON.stringify(row)) as UpdateMenuInput;
+	copyRow.id = 0;
+	copyRow.title = '';
+	editMenuRef.value?.openDialog(copyRow);
+};
+
 // 打开编辑页面
 const openEditMenu = (row: any) => {
 	state.editMenuTitle = '编辑菜单';

+ 12 - 2
Web/src/views/system/org/index.vue

@@ -48,8 +48,9 @@
 								<ModifyRecord :data="scope.row" />
 							</template>
 						</el-table-column>
-						<el-table-column label="操作" width="140" fixed="right" align="center" show-overflow-tooltip>
+						<el-table-column label="操作" width="210" fixed="right" align="center" show-overflow-tooltip>
 							<template #default="scope">
+								<el-button icon="ele-CopyDocument" size="small" text type="primary" @click="openCopyOrg(scope.row)" v-auth="'sysOrg:add'"> 复制 </el-button>
 								<el-button icon="ele-Edit" size="small" text type="primary" @click="openEditOrg(scope.row)" v-auth="'sysOrg:update'"> 编辑 </el-button>
 								<el-button icon="ele-Delete" size="small" text type="danger" @click="delOrg(scope.row)" v-auth="'sysOrg:delete'"> 删除 </el-button>
 							</template>
@@ -75,7 +76,7 @@ import ModifyRecord from '/@/components/table/modifyRecord.vue';
 
 import { getAPI } from '/@/utils/axios-utils';
 import { SysOrgApi, SysDictDataApi } from '/@/api-services/api';
-import { SysOrg } from '/@/api-services/models';
+import { SysOrg, UpdateOrgInput } from '/@/api-services/models';
 
 const editOrgRef = ref<InstanceType<typeof EditOrg>>();
 const orgTreeRef = ref<InstanceType<typeof OrgTree>>();
@@ -134,6 +135,15 @@ const openAddOrg = () => {
 	editOrgRef.value?.openDialog({ status: 1, orderNo: 100 });
 };
 
+// 打开复制页面
+const openCopyOrg = (row: any) => {
+	state.editOrgTitle = '复制菜单';
+	var copyRow = JSON.parse(JSON.stringify(row)) as UpdateOrgInput;
+	copyRow.id = 0;
+	copyRow.name = '';
+	editOrgRef.value?.openDialog(copyRow);
+};
+
 // 打开编辑页面
 const openEditOrg = (row: any) => {
 	state.editOrgTitle = '编辑机构';