Bläddra i källkod

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

Ir0nMax 2 år sedan
förälder
incheckning
c95c031e8e
27 ändrade filer med 1149 tillägg och 849 borttagningar
  1. 1 1
      Admin.NET/Admin.NET.Application/Configuration/CodeGen.json
  2. 1 1
      Admin.NET/Admin.NET.Application/Configuration/Enum.json
  3. 22 4
      Admin.NET/Admin.NET.Application/Configuration/SMS.json
  4. 2 2
      Admin.NET/Admin.NET.Application/Configuration/Upload.json
  5. 2 2
      Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
  6. 5 0
      Admin.NET/Admin.NET.Core/Const/CommonConst.cs
  7. 0 1
      Admin.NET/Admin.NET.Core/Job/EnumToDictJob.cs
  8. 3 2
      Admin.NET/Admin.NET.Core/Job/LogJob.cs
  9. 18 3
      Admin.NET/Admin.NET.Core/Option/SMSOptions.cs
  10. 18 17
      Admin.NET/Admin.NET.Core/SeedData/SysConfigSeedData.cs
  11. 9 4
      Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenService.cs
  12. 4 1
      Admin.NET/Admin.NET.Core/Service/Enum/SysEnumService.cs
  13. 10 6
      Admin.NET/Admin.NET.Core/Service/Message/SysSmsService.cs
  14. 2 2
      Admin.NET/Admin.NET.Web.Entry/Program.cs
  15. 4 4
      Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/editDialog.vue.vm
  16. 13 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Const/DingTalkConst.cs
  17. 19 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkUser.cs
  18. 13 1
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Job/SyncDingTalkUserJob.cs
  19. 2 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkCurrentEmployeesRosterListInput.cs
  20. 9 9
      Web/package.json
  21. 1 1
      Web/src/components/cropper/index.vue
  22. 6 1
      Web/src/theme/element.scss
  23. 10 0
      Web/src/theme/media/form.scss
  24. 296 188
      Web/src/views/elive/index.vue
  25. 674 594
      Web/src/views/mqttx/index.vue
  26. 1 1
      Web/src/views/system/database/index.vue
  27. 4 4
      Web/src/views/system/dict/index.vue

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

@@ -3,7 +3,7 @@
 
   // 代码生成配置项-程序集名称集合
   "CodeGen": {
-    "EntityAssemblyNames": [ "Admin.NET.Core", "Admin.NET.Application" ],
+    "EntityAssemblyNames": [ "Admin." ],
     "BaseEntityNames": [ "EntityTenantId", "EntityTenant", "EntityTenantBaseData", "EntityBaseData", "EntityBase", "EntityBaseId" ],
     "EntityBaseColumn": {
       "EntityTenantId": [ "Id", "TenantId" ],

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

@@ -3,6 +3,6 @@
 
   // 枚举实体所在程序集名称集合
   "Enum": {
-    "EntityAssemblyNames": [ "Admin.NET.Core", "Admin.NET.Application", "Admin.NET.AppCMS" ]
+    "EntityAssemblyNames": [ "Admin." ]
   }
 }

+ 22 - 4
Admin.NET/Admin.NET.Application/Configuration/SMS.json

@@ -5,15 +5,33 @@
     "Aliyun": {
       "AccessKeyId": "",
       "AccessKeySecret": "",
-      "SignName": "AdminNET 平台", // 短信签名
-      "TemplateCode": "" // 短信模板
+      "Templates": [
+        {
+          "Id": "0",
+          "SignName": "AdminNET 平台",
+          "TemplateCode": "SMS_291005708",
+          "Content": "您的验证码为:${code},请勿泄露于他人!"
+        },
+        {
+          "Id": "1",
+          "SignName": "AdminNET 平台",
+          "TemplateCode": "SMS_462801755",
+          "Content": "注册成功,感谢您的注册,请妥善保管您的账户信息"
+        }
+      ]
     },
     "Tencentyun": {
       "SdkAppId": "",
       "AccessKeyId": "",
       "AccessKeySecret": "",
-      "SignName": "AdminNET 平台", // 短信签名
-      "TemplateCode": "" // 短信模板
+      "Templates": [
+        {
+          "Id": "0",
+          "SignName": "AdminNET 平台",
+          "TemplateCode": "",
+          "Content": ""
+        }
+      ]
     }
   }
 }

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

@@ -3,8 +3,8 @@
 
   "Upload": {
     "Path": "Upload/{yyyy}/{MM}/{dd}", // 文件上传目录
-    "MaxSize": 20480, // 文件最大限制KB:1024*20
-    "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.spreadsheetml.sheet", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "video/mp4", "application/wps-office.docx", "application/wps-office.xlsx" ],
+    "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", "video/mp4", "application/wps-office.docx", "application/wps-office.xlsx", "application/wps-office.pptx" ],
     "EnableMd5": false // 启用文件MDF5验证-防止重复上传
   },
   "OSSProvider": {

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

@@ -34,10 +34,10 @@
     <PackageReference Include="SixLabors.ImageSharp.Web" Version="3.1.2" />
     <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.3.0" />
     <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.5.0" />
-    <PackageReference Include="SqlSugarCore" Version="5.1.4.159" />
+    <PackageReference Include="SqlSugarCore" Version="5.1.4.160" />
     <PackageReference Include="SSH.NET" Version="2024.0.0" />
     <PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.2" />
-    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1034" />
+    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1036" />
     <PackageReference Include="UAParser" Version="3.1.47" />
     <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
   </ItemGroup>

+ 5 - 0
Admin.NET/Admin.NET.Core/Const/CommonConst.cs

@@ -87,6 +87,11 @@ public class CommonConst
     /// </summary>
     public const string SysValidationLog = "sys_validation_log";
 
+    /// <summary>
+    /// 日志保留天数
+    /// </summary>
+    public const string SysLogRetentionDays = "sys_log_retention_days";
+
     /// <summary>
     /// 日志分组名称
     /// </summary>

+ 0 - 1
Admin.NET/Admin.NET.Core/Job/EnumToDictJob.cs

@@ -59,7 +59,6 @@ public class EnumToDictJob : IJob
                                 dictData.Value = enumData.Value.ToString();
                                 dictData.Code = enumData.Name;
                                 dictData.OrderNo = enumData.Value + 10;
-								dictData.Name = enumData.Describe;
                                 dictData.Name = enumData.Describe;
                                 uSysDictData.Add(dictData);
                             }

+ 3 - 2
Admin.NET/Admin.NET.Core/Job/LogJob.cs

@@ -29,15 +29,16 @@ public class LogJob : IJob
         var logVisRep = serviceScope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysLogVis>>();
         var logOpRep = serviceScope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysLogOp>>();
         var logDiffRep = serviceScope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysLogDiff>>();
+        var sysConfigService = serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
 
-        var daysAgo = 30; // 删除30天以前
+        var daysAgo = await sysConfigService.GetConfigValue<int>(CommonConst.SysLogRetentionDays); // 日志保留天数
         await logVisRep.CopyNew().AsDeleteable().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除访问日志
         await logOpRep.CopyNew().AsDeleteable().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除操作日志
         await logDiffRep.CopyNew().AsDeleteable().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除差异日志
 
         var originColor = Console.ForegroundColor;
         Console.ForegroundColor = ConsoleColor.Yellow;
-        Console.WriteLine($"【{DateTime.Now}】清理系统日志(30天前)");
+        Console.WriteLine($"【{DateTime.Now}】清理系统日志({daysAgo}天前)");
         Console.ForegroundColor = originColor;
 
         // 自定义日志

+ 18 - 3
Admin.NET/Admin.NET.Core/Option/SMSOptions.cs

@@ -40,12 +40,27 @@ public sealed class SMSSettings
     public string AccessKeySecret { get; set; }
 
     /// <summary>
-    /// 短信签名
+    /// Templates
     /// </summary>
-    public string SignName { get; set; }
+    public List<SmsTemplate> Templates { get; set; }
 
     /// <summary>
-    /// 短信模板
+    /// GetTemplate
     /// </summary>
+    public SmsTemplate GetTemplate(string id = "0")
+    {
+        foreach (var template in Templates)
+        {
+            if (template.Id == id) { return template; }
+        }
+        return null;
+    }
+}
+
+public class SmsTemplate
+{
+    public string Id { get; set; } = string.Empty;
+    public string SignName { get; set; }
     public string TemplateCode { get; set; }
+    public string Content { get; set; }
 }

+ 18 - 17
Admin.NET/Admin.NET.Core/SeedData/SysConfigSeedData.cs

@@ -22,23 +22,24 @@ public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
             new SysConfig{ Id=1300000000101, Name="演示环境", Code="sys_demo", Value="False", SysFlag=YesNoEnum.Y, Remark="演示环境", OrderNo=10, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000111, Name="默认密码", Code="sys_password", Value="123456", SysFlag=YesNoEnum.Y, Remark="默认密码", OrderNo=20, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000121, Name="密码最大错误次数", Code="sys_password_max_error_times", Value="5", SysFlag=YesNoEnum.Y, Remark="允许密码最大输入错误次数", OrderNo=30, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000131, Name="记录操作日志", Code="sys_oplog", Value="True", SysFlag=YesNoEnum.Y, Remark="是否记录操作日志", OrderNo=40, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000141, Name="开启单设备登录", Code="sys_single_login", Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启单设备登录", OrderNo=50, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000151, Name="开启登录二次验证", Code="sys_second_ver", Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启登录二次验证", OrderNo=60, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000161, Name="开启图形验证码", Code="sys_captcha", Value="True", SysFlag=YesNoEnum.Y, Remark="是否开启图形验证码", OrderNo=+0, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000171, Name="Token过期时间", Code="sys_token_expire", Value="10080", SysFlag=YesNoEnum.Y, Remark="Token过期时间(分钟)", OrderNo=80, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000181, Name="刷新Token过期时间", Code="sys_refresh_token_expire", Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间(分钟)(一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=90, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000191, Name="发送异常日志邮件", Code="sys_error_mail", Value="True", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=100, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000201, Name="开启域登录验证", Code="sys_domain_login", 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="sys_validation_log", Value="True", SysFlag=YesNoEnum.Y, Remark="是否数据校验日志", OrderNo=120, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000221, Name="系统主标题", Code="sys_web_title", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="系统主标题", OrderNo=130, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000231, Name="系统副标题", Code="sys_web_viceTitle", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="系统副标题", OrderNo=140, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000241, Name="系统描述", Code="sys_web_viceDesc", Value="站在巨人肩膀上的 .NET 通用权限开发框架", SysFlag=YesNoEnum.Y, Remark="系统描述", OrderNo=150, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000251, Name="水印内容", Code="sys_web_watermark", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="水印内容", OrderNo=160, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000261, Name="版权说明", Code="sys_web_copyright", Value="Copyright © 2021-present Admin.NET All rights reserved.", SysFlag=YesNoEnum.Y, Remark="版权说明", OrderNo=170, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000271, Name="系统图标", Code="sys_web_logo", Value="/Upload/logo.png", SysFlag=YesNoEnum.Y, Remark="系统图标", OrderNo=180, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000281, Name="ICP备案号", Code="sys_web_icp", Value="省ICP备12345678号", SysFlag=YesNoEnum.Y, Remark="ICP备案号", OrderNo=190, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000291, Name="ICP地址", Code="sys_web_icpUrl", Value="https://beian.miit.gov.cn", SysFlag=YesNoEnum.Y, Remark="ICP地址", OrderNo=200, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000131, Name="日志保留天数", Code="sys_log_retention_days", Value="180", SysFlag=YesNoEnum.Y, Remark="日志保留天数(天)", OrderNo=40, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000141, Name="记录操作日志", Code="sys_oplog", Value="True", SysFlag=YesNoEnum.Y, Remark="是否记录操作日志", OrderNo=50, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000151, Name="开启单设备登录", Code="sys_single_login", Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启单设备登录", OrderNo=60, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000161, Name="开启登录二次验证", Code="sys_second_ver", Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启登录二次验证", OrderNo=70, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000171, Name="开启图形验证码", Code="sys_captcha", Value="True", SysFlag=YesNoEnum.Y, Remark="是否开启图形验证码", OrderNo=80, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000181, Name="Token过期时间", Code="sys_token_expire", 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="刷新Token过期时间", Code="sys_refresh_token_expire", 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="sys_error_mail", Value="True", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=110, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000211, Name="开启域登录验证", Code="sys_domain_login", 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="sys_validation_log", Value="True", SysFlag=YesNoEnum.Y, Remark="是否数据校验日志", OrderNo=130, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000301, Name="系统主标题", Code="sys_web_title", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="系统主标题", OrderNo=300, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000311, Name="系统副标题", Code="sys_web_viceTitle", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="系统副标题", OrderNo=310, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000321, Name="系统描述", Code="sys_web_viceDesc", 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="sys_web_watermark", 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="sys_web_copyright", 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="sys_web_logo", 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="sys_web_icp", 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="sys_web_icpUrl", Value="https://beian.miit.gov.cn", SysFlag=YesNoEnum.Y, Remark="ICP地址", OrderNo=370, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
         };
     }
 }

+ 9 - 4
Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenService.cs

@@ -248,10 +248,15 @@ public class SysCodeGenService : IDynamicApiController, ITransient
         var types = new List<Type>();
         if (_codeGenOptions.EntityAssemblyNames != null)
         {
-            foreach (var assemblyName in _codeGenOptions.EntityAssemblyNames)
+            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+            foreach (var assembly in assemblies)
             {
-                Assembly asm = Assembly.Load(assemblyName);
-                types.AddRange(asm.GetExportedTypes().ToList());
+                var assemblyName = assembly.GetName().Name;
+                if (_codeGenOptions.EntityAssemblyNames.Contains(assemblyName) || _codeGenOptions.EntityAssemblyNames.Any(name => assemblyName.Contains(name)))
+                {
+                    Assembly asm = Assembly.Load(assemblyName);
+                    types.AddRange(asm.GetExportedTypes().ToList());
+                }
             }
         }
         bool IsMyAttribute(Attribute[] o)
@@ -343,7 +348,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
             PrintType = input.PrintType,
             PrintName = input.PrintName,
         };
-        //模板目录
+        // 模板目录
         var templatePathList = GetTemplatePathList(input);
         var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "Template");
 

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

@@ -27,7 +27,10 @@ public class SysEnumService : IDynamicApiController, ITransient
     [DisplayName("获取所有枚举类型")]
     public List<EnumTypeOutput> GetEnumTypeList()
     {
-        var enumTypeList = App.EffectiveTypes.Where(u => u.IsEnum && _enumOptions.EntityAssemblyNames.Contains(u.Assembly.GetName().Name)).OrderBy(u => u.Name).OrderBy(u => u.FullName).ToList();
+        var enumTypeList = App.EffectiveTypes.Where(t => t.IsEnum)
+            .Where(t => _enumOptions.EntityAssemblyNames.Contains(t.Assembly.GetName().Name) || _enumOptions.EntityAssemblyNames.Any(name => t.Assembly.GetName().Name.Contains(name)))
+            .OrderBy(u => u.Name).OrderBy(u => u.FullName)
+            .ToList();
 
         var result = new List<EnumTypeOutput>();
         foreach (var item in enumTypeList)

+ 10 - 6
Admin.NET/Admin.NET.Core/Service/Message/SysSmsService.cs

@@ -8,6 +8,7 @@ using AlibabaCloud.SDK.Dysmsapi20170525.Models;
 using TencentCloud.Common;
 using TencentCloud.Common.Profile;
 using TencentCloud.Sms.V20190711;
+using static SKIT.FlurlHttpClient.Wechat.Api.Models.ComponentTCBBatchCreateContainerServiceVersionRequest.Types;
 
 namespace Admin.NET.Core.Service;
 
@@ -65,11 +66,12 @@ public class SysSmsService : IDynamicApiController, ITransient
         });
 
         var client = CreateAliyunClient();
+        var template = _smsOptions.Aliyun.GetTemplate();
         var sendSmsRequest = new SendSmsRequest
         {
             PhoneNumbers = phoneNumber, // 待发送手机号, 多个以逗号分隔
-            SignName = _smsOptions.Aliyun.SignName, // 短信签名
-            TemplateCode = _smsOptions.Aliyun.TemplateCode, // 短信模板
+            SignName = template.SignName, // 短信签名
+            TemplateCode = template.TemplateCode, // 短信模板
             TemplateParam = templateParam.ToString(), // 模板中的变量替换JSON串
             OutId = YitIdHelper.NextId().ToString()
         };
@@ -104,11 +106,12 @@ public class SysSmsService : IDynamicApiController, ITransient
             throw Oops.Oh("短信内容不能为空");
 
         var client = CreateAliyunClient();
+        var template = _smsOptions.Aliyun.GetTemplate();
         var sendSmsRequest = new SendSmsRequest
         {
             PhoneNumbers = phoneNumber, // 待发送手机号, 多个以逗号分隔
-            SignName = _smsOptions.Aliyun.SignName, // 短信签名
-            TemplateCode = _smsOptions.Aliyun.TemplateCode, // 短信模板
+            SignName = template.SignName, // 短信签名
+            TemplateCode = template.TemplateCode, // 短信模板
             TemplateParam = templateParam.ToString(), // 模板中的变量替换JSON串
             OutId = YitIdHelper.NextId().ToString()
         };
@@ -142,13 +145,14 @@ public class SysSmsService : IDynamicApiController, ITransient
 
         // 实例化要请求产品的client对象,clientProfile是可选的
         var client = new SmsClient(CreateTencentClient(), "ap-guangzhou", new ClientProfile() { HttpProfile = new HttpProfile() { Endpoint = ("sms.tencentcloudapi.com") } });
+        var template = _smsOptions.Tencentyun.GetTemplate();
         // 实例化一个请求对象,每个接口都会对应一个request对象
         var req = new TencentCloud.Sms.V20190711.Models.SendSmsRequest
         {
             PhoneNumberSet = new string[] { "+86" + phoneNumber.Trim(',') },
             SmsSdkAppid = _smsOptions.Tencentyun.SdkAppId,
-            Sign = _smsOptions.Tencentyun.SignName,
-            TemplateID = _smsOptions.Tencentyun.TemplateCode,
+            Sign = template.SignName,
+            TemplateID = template.TemplateCode,
             TemplateParamSet = new string[] { verifyCode.ToString() }
         };
 

+ 2 - 2
Admin.NET/Admin.NET.Web.Entry/Program.cs

@@ -16,8 +16,8 @@ public class WebComponent : IWebComponent
             return !new[] { "Microsoft.Hosting", "Microsoft.AspNetCore" }.Any(u => category.StartsWith(u)) && logLevel >= LogLevel.Information;
         });
 
-        // 设置接口超时时间和上传大小
-        builder.Configuration.Get<WebHostBuilder>().ConfigureKestrel(u =>
+        // 设置接口超时时间和上传大小-Kestrel
+        builder.WebHost.ConfigureKestrel(u =>
         {
             u.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(30);
             u.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(30);

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

@@ -78,7 +78,7 @@
 					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
 							@:<el-select clearable v-model="ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
-								@:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')"  :key="index" :value="item.code" :label="`[${item.code}] ${item.value}`"></el-option>
+								@:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')"  :key="index" :value="@(@column.NetType.StartsWith("string") ? "item.code" : "Number(item.code)")" :label="`[${item.code}] ${item.value}`"></el-option>
 								@:
 							</el-select>
 							@:
@@ -89,7 +89,7 @@
 					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
 							@:<el-select clearable v-model="ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
-								@:<el-option v-for="(item,index) in getConstType('@column.DictTypeCode')" :key="index" :label="item.name" :value="item.code">{{ item.name }}</el-option>
+								@:<el-option v-for="(item,index) in getConstType('@column.DictTypeCode')" :key="index" :label="item.name" :value="@(@column.NetType.StartsWith("string") ? "item.code" : "Number(item.code)")">{{ item.name }}</el-option>
 								@:
 							</el-select>
 							@:
@@ -135,7 +135,7 @@
 					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
 							@:<el-select clearable v-model="ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
-								@:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')" :key="index" :value="@(@column.NetType == "string?" ? "item.value" : "Number(item.value)")" :label="`${item.name} (${item.code}) [${item.value}]`"></el-option>
+								@:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')" :key="index" :value="@(@column.NetType.StartsWith("string") ? "item.code" : "Number(item.code)")" :label="`${item.name} (${item.code}) [${item.value}]`"></el-option>
 								@:
 							</el-select>
 							@:
@@ -159,7 +159,7 @@
 		</el-dialog>
 	</div>
 </template>
-<style scoped>
+<style lang="scss" scoped>
 :deep(.el-select),
 :deep(.el-input-number) {
 	width: 100%;

+ 13 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Const/DingTalkConst.cs

@@ -31,4 +31,17 @@ public class DingTalkConst
     /// 工号
     /// </summary>
     public const string JobNumberField = "sys00-jobNumber";
+
+    /// <summary>
+    /// 主部门Id
+    /// </summary>
+    public const string DeptId = "sys00-mainDeptId";
+    /// <summary>
+    /// 主部门
+    /// </summary>
+    public const string Dept = "sys00-mainDept";
+    /// <summary>
+    /// 职位
+    /// </summary>
+    public const string Position = "sys00-position";
 }

+ 19 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkUser.cs

@@ -73,4 +73,23 @@ public class DingTalkUser : EntityBase
     [SugarColumn(ColumnDescription = "工号", Length = 16)]
     [MaxLength(16)]
     public string? JobNumber { get; set; }
+    /// <summary>
+    /// 主部门Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "主部门Id", Length = 16)]
+    [MaxLength(16)]
+    public string? DeptId { get; set; }
+    /// <summary>
+    /// 主部门
+    /// </summary>
+    [SugarColumn(ColumnDescription = "主部门", Length = 16)]
+    [MaxLength(16)]
+    public string? Dept { get; set; }
+    /// <summary>
+    /// 职位
+    /// </summary>
+    [SugarColumn(ColumnDescription = "职位", Length = 16)]
+    [MaxLength(16)]
+    public string? Position { get; set; }
+
 }

+ 13 - 1
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Job/SyncDingTalkUserJob.cs

@@ -95,6 +95,9 @@ public class SyncDingTalkUserJob : IJob
             Name = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.NameField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
             Mobile = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.MobileField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
             JobNumber = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.JobNumberField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            DeptId = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.DeptId).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            Dept = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.Dept).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            Position = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.Position).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
         }).ToList();
         if (iUser.Count > 0)
         {
@@ -109,6 +112,9 @@ public class SyncDingTalkUserJob : IJob
             Name = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.NameField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
             Mobile = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.MobileField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
             JobNumber = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.JobNumberField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            DeptId = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.DeptId).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            Dept = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.Dept).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            Position = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.Position).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
         }).ToList();
         if (uUser.Count > 0)
         {
@@ -118,6 +124,9 @@ public class SyncDingTalkUserJob : IJob
                 u.Name,
                 u.Mobile,
                 u.JobNumber,
+                u.DeptId,
+                u.Dept,
+                u.Position,
                 u.UpdateTime,
                 u.UpdateUserName,
                 u.UpdateUserId,
@@ -137,7 +146,10 @@ public class SyncDingTalkUserJob : IJob
             {
                 u.Id,
                 u.JobNumber,
-                u.Mobile
+                u.Mobile,
+                u.DeptId,
+                u.Dept,
+                u.Position
             }).ToListAsync();
         var uSysDingTalkUser = sysDingTalkUser.Select(u => new DingTalkUser
         {

+ 2 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkCurrentEmployeesRosterListInput.cs

@@ -25,5 +25,7 @@ public class GetDingTalkCurrentEmployeesRosterListInput
     /// <summary>
     /// 应用的AgentId
     /// </summary>
+    [Newtonsoft.Json.JsonProperty("agentid")]
+    [System.Text.Json.Serialization.JsonPropertyName("agentid")]
     public string AgentId { get; set; }
 }

+ 9 - 9
Web/package.json

@@ -2,7 +2,7 @@
 	"name": "admin.net",
 	"type": "module",
 	"version": "2.4.33",
-	"lastBuildTime": "2024.6.25",
+	"lastBuildTime": "2024.6.28",
 	"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
 	"author": "zuohuaijun",
 	"license": "MIT",
@@ -14,8 +14,8 @@
 	},
 	"dependencies": {
 		"@element-plus/icons-vue": "^2.3.1",
-		"@logicflow/core": "^2.0.0-beta.0",
-		"@logicflow/extension": "^2.0.0-beta.0",
+		"@logicflow/core": "^1.2.27",
+		"@logicflow/extension": "^1.2.27",
 		"@microsoft/signalr": "^8.0.0",
 		"@vue-office/docx": "^1.6.2",
 		"@vue-office/excel": "^1.7.8",
@@ -28,19 +28,19 @@
 		"axios": "^1.7.2",
 		"countup.js": "^2.8.0",
 		"cropperjs": "^1.6.2",
-		"echarts": "^5.5.0",
+		"echarts": "^5.5.1",
 		"echarts-gl": "^2.0.9",
 		"echarts-wordcloud": "^2.1.0",
 		"element-plus": "^2.7.6",
 		"ezuikit": "^1.0.0",
-		"ezuikit-js": "^8.0.5",		
+		"ezuikit-js": "^8.0.5",
 		"js-cookie": "^3.0.5",
 		"js-table2excel": "^1.1.2",
 		"jsplumb": "^2.15.6",
 		"lodash-es": "^4.17.21",
-		"mqtt": "4.3.7",
 		"mitt": "^3.0.1",
 		"monaco-editor": "^0.50.0",
+		"mqtt": "^4.3.8",
 		"nprogress": "^0.2.0",
 		"pinia": "^2.1.7",
 		"print-js": "^1.6.0",
@@ -54,7 +54,7 @@
 		"splitpanes": "^3.1.5",
 		"vcrontab-3": "^3.3.22",
 		"vform3-builds": "^3.0.10",
-		"vue": "^3.4.30",
+		"vue": "^3.4.31",
 		"vue-clipboard3": "^2.0.0",
 		"vue-demi": "^0.14.8",
 		"vue-grid-layout": "3.0.0-beta1",
@@ -77,7 +77,7 @@
 		"@typescript-eslint/parser": "^7.14.1",
 		"@vitejs/plugin-vue": "^5.0.5",
 		"@vitejs/plugin-vue-jsx": "^4.0.0",
-		"@vue/compiler-sfc": "^3.4.30",
+		"@vue/compiler-sfc": "^3.4.31",
 		"code-inspector-plugin": "^0.14.2",
 		"eslint": "^9.5.0",
 		"eslint-plugin-vue": "^9.26.0",
@@ -87,7 +87,7 @@
 		"sass": "^1.77.6",
 		"terser": "^5.31.1",
 		"typescript": "^5.5.2",
-		"vite": "^5.3.1",
+		"vite": "^5.3.2",
 		"vite-plugin-cdn-import": "^1.0.1",
 		"vite-plugin-compression2": "^1.1.1",
 		"vite-plugin-vue-setup-extend": "^0.4.0",

+ 1 - 1
Web/src/components/cropper/index.vue

@@ -37,7 +37,7 @@
 						:auto-upload="false"
 						:on-change="selectPicture"
 						:on-exceed="selectPictureExceed"
-						style="display: inline-block; position: absolute; right: 172px"
+						style="display: inline-block; position: absolute; right: 182px"
 					>
 						<el-button icon="ele-Picture">选择图片</el-button>
 					</el-upload>

+ 6 - 1
Web/src/theme/element.scss

@@ -403,7 +403,12 @@
 	float: right;
 	margin: 10px 0 -10px 0 !important;
 }
-
+//由于index.vue模板中加入了el-select宽度为100%,导致分页组件的下拉框无法正确显示,这里强制给个宽度分页组件可正常显示
+.el-pagination--small {
+	.el-select {
+		width: 100px !important;
+	}
+}
 /* scrollbar
 ------------------------------- */
 .el-scrollbar__bar {

+ 10 - 0
Web/src/theme/media/form.scss

@@ -28,4 +28,14 @@
 		max-height: 160px;
 		overflow: auto;
 	}
+	// 在小屏幕中,取消表单左边距
+	.el-form-item__label-wrap{
+		margin-left: 0px !important;
+	}
+	// 列表页查询表单内的输入框宽度适配
+    .el-form--inline .el-form-item .el-select,
+    .el-form--inline .el-form-item .el-input
+    {
+        width: 100% !important;
+    }
 }

+ 296 - 188
Web/src/views/elive/index.vue

@@ -1,37 +1,57 @@
 <template>
-  <div class="sys-video-container">
-    <el-container>
-      <el-header>视频监控(萤石云云直播)</el-header>
-      <el-container>
-        <el-aside width="200px">
-			  <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" />
-		</el-aside>
-		
-        <el-main>
-			<div class="updateToken" props="ezviz_video">
-				<ul>
-					<li>
-						<el-span>密匙串:</el-span><el-input placeholder="密匙" show-word-limit type="text" id="txt_token" title="每周更新(开放平台,云直播,轻应用,代码示例)" v-model="ezviz_video.ezvizToken" @keyup.enter="update_Token" class="token_input" />
-					</li>
-					<li>
-						<el-span>视频流:</el-span><el-input placeholder="萤石云视频流地址" show-word-limit type="text" id="txt_url" title="密匙对应的视频流地址(高清后缀.h.live)" v-model="ezviz_video.ezvizUrl" @keyup.enter="update_Token" class="token_input" />
-					</li>
-				</ul>
-			</div>	
-			
-			<div class="video">
-				<div class="video-item">
-					<div class="item">
-						<div class="home" ref="viewtoolOne">
-							<div id="video-container">等待加载...</div>
+	<div class="sys-video-container">
+		<el-container>
+			<el-header>视频监控(萤石云云直播)</el-header>
+			<el-container>
+				<el-aside width="200px">
+					<el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" />
+				</el-aside>
+
+				<el-main>
+					<div class="updateToken" props="ezviz_video">
+						<ul>
+							<li>
+								<el-span>密匙串:</el-span>
+								<el-input
+									placeholder="密匙"
+									show-word-limit
+									type="text"
+									id="txt_token"
+									title="每周更新(开放平台,云直播,轻应用,代码示例)"
+									v-model="ezviz_video.ezvizToken"
+									@keyup.enter="update_Token"
+									class="token_input"
+								/>
+							</li>
+							<li>
+								<el-span>视频流:</el-span>
+								<el-input
+									placeholder="萤石云视频流地址"
+									show-word-limit
+									type="text"
+									id="txt_url"
+									title="密匙对应的视频流地址(高清后缀.h.live)"
+									v-model="ezviz_video.ezvizUrl"
+									@keyup.enter="update_Token"
+									class="token_input"
+								/>
+							</li>
+						</ul>
+					</div>
+
+					<div class="video">
+						<div class="video-item">
+							<div class="item">
+								<div class="home" ref="viewtoolOne">
+									<div id="video-container">等待加载...</div>
+								</div>
+							</div>
 						</div>
 					</div>
-				</div>
-			</div>			
-		</el-main>
-      </el-container>
-    </el-container>
-  </div>
+				</el-main>
+			</el-container>
+		</el-container>
+	</div>
 </template>
 
 <!-- 
@@ -39,12 +59,11 @@
 安装:npm install ezuikit-js 或 pnpm add ezuikit-js
 -->
 <script lang="ts" setup name="video">
-import { reactive, ref,onMounted,nextTick,beforeDestroy } from "vue";
-import EZUIKit from 'ezuikit-js';//页面引用
+import { reactive, ref, onMounted, nextTick, beforeDestroy } from 'vue';
+import EZUIKit from 'ezuikit-js'; //页面引用
 //import { ElNotification } from 'element-plus';
 //import { Search,ChatDotSquare,TopRight,Star,Operation,Setting,Connection,Discount,Open,Delete,Position,View,CopyDocument,DocumentChecked,VideoCamera} from '@element-plus/icons-vue';
-import mittBus from '/@/utils/mitt';//事件总线mitt 解决打包后错误Uncaught (in promise) ReferenceError: Cannot access 'oe' before initialization
-
+import mittBus from '/@/utils/mitt'; //事件总线mitt 解决打包后错误Uncaught (in promise) ReferenceError: Cannot access 'oe' before initialization
 
 let ezvizPlayOne = ref(null);
 let ezvizPlayTwo = ref(null);
@@ -56,40 +75,37 @@ let viewtoolThree = ref();
 let viewtoolFour = ref();
 
 interface Tree {
-  label: string
-  children?: Tree[]
+	label: string;
+	children?: Tree[];
 }
 
 const defaultProps = {
-  children: 'children',
-  label: 'label',
-}
+	children: 'children',
+	label: 'label',
+};
 
 const handleNodeClick = (data: Tree) => {
-  console.log(data)
-}
-
-
+	console.log(data);
+};
 
 // 更新token
-function update_Token(e){
+function update_Token(e) {
 	//ezviz_video.ezvizToken=e.target.value;
 	console.log(e.target.value);
-	autoVideoOne("video-container");
-	
+	autoVideoOne('video-container');
 }
 
-onMounted(async()=>{
-	autoVideoOne("video-container");
+onMounted(async () => {
+	autoVideoOne('video-container');
 	//console.log('https://open.ys7.com/console/ezuikit/template/detail.html?themeId=pcLive&editing=false');
 });
 
-//测试 ezopen://open.ys7.com/G39444019/1.live 和 at.3bvmj4ycamlgdwgw1ig1jruma0wpohl6-48zifyb39c-13t5am6-yukyi86mz
-//备用 ezopen://open.ys7.com/AA2615287/1.live 和 ra.5k88qgc34vgr9yva7rlub985blo9ph7k-92q0bl2r4r-0aygaog-5cofhebpm
+// 测试 ezopen://open.ys7.com/G39444019/1.live 和 at.3bvmj4ycamlgdwgw1ig1jruma0wpohl6-48zifyb39c-13t5am6-yukyi86mz
+// 备用 ezopen://open.ys7.com/AA2615287/1.live 和 ra.5k88qgc34vgr9yva7rlub985blo9ph7k-92q0bl2r4r-0aygaog-5cofhebpm
 const ezviz_video = reactive({
-	ezvizToken: "ra.5k88qgc34vgr9yva7rlub985blo9ph7k-92q0bl2r4r-0aygaog-5cofhebpm",//需要修改每周(开放平台,云直播,轻应用,代码示例中找)演示设备
-	ezvizUrl: "ezopen://open.ys7.com/AA2615287/1.live",//高清直播拼接字符串   cosnt url = `ezopen://${item.identifyingCode}@open.ys7.com/${item.imei}/${item.channelNo}.hd.live`
-	//回放地址ezopen://open.ys7.com/AA2615287/1.rec
+	ezvizToken: 'ra.5k88qgc34vgr9yva7rlub985blo9ph7k-92q0bl2r4r-0aygaog-5cofhebpm', //需要修改每周(开放平台,云直播,轻应用,代码示例中找)演示设备
+	ezvizUrl: 'ezopen://open.ys7.com/AA2615287/1.live', //高清直播拼接字符串   cosnt url = `ezopen://${item.identifyingCode}@open.ys7.com/${item.imei}/${item.channelNo}.hd.live`
+	// 回放地址ezopen://open.ys7.com/AA2615287/1.rec
 });
 
 // beforeDestroy(()=>{
@@ -97,25 +113,25 @@ const ezviz_video = reactive({
 // 	console.log('beforeDestroy');
 // });
 
-//监控1,参数https://blog.csdn.net/weixin_53791978/article/details/126489296
+// 监控1,参数https://blog.csdn.net/weixin_53791978/article/details/126489296
 function autoVideoOne(params) {
-    // 获取父节点的宽高
-	let divW = viewtoolOne.value.clientWidth
-	let divH = viewtoolOne.value.clientHeight
+	// 获取父节点的宽高
+	let divW = viewtoolOne.value.clientWidth;
+	let divH = viewtoolOne.value.clientHeight;
 	if (ezvizPlayOne.value != null) {
-		return
+		return;
 	}
-	
-	//获取萤石token
+
+	// 获取萤石token
 	ezvizPlayOne.value = new EZUIKit.EZUIKitPlayer({
-		autoplay: true,  // 默认播放
-		//视频播放包括元素
-		id: "video-container",//DIV容器
-		//萤石token,https://open.ys7.com/console/ezuikit/template/detail.html?themeId=pcLive&editing=false中查询实例代码
-		accessToken: ezviz_video.ezvizToken,//"ra.bl9n4hmb3c7w4fk6bbuumtmdcbbo66w0-3k7nal0q6y-0lp00m5-fi61isesz",
+		autoplay: true, // 默认播放
+		// 视频播放包括元素
+		id: 'video-container', //DIV容器
+		// 萤石token,https://open.ys7.com/console/ezuikit/template/detail.html?themeId=pcLive&editing=false中查询实例代码
+		accessToken: ezviz_video.ezvizToken, //"ra.bl9n4hmb3c7w4fk6bbuumtmdcbbo66w0-3k7nal0q6y-0lp00m5-fi61isesz",
 		// ezopen://open.ys7.com/${设备序列号}/{通道号}.live
-		url: ezviz_video.ezvizUrl,//"ezopen://open.ys7.com/AA2615287/1.live", // 播放地址
-		template: "standard", // pcLive,simple - 极简版;standard-标准版;security - 安防版(预览回放);voice-语音版;theme-可配置主题;
+		url: ezviz_video.ezvizUrl, //"ezopen://open.ys7.com/AA2615287/1.live", // 播放地址
+		template: 'standard', // pcLive,simple - 极简版;standard-标准版;security - 安防版(预览回放);voice-语音版;theme-可配置主题;
 		useHardDev: true, // 开启高性能模式 依赖需高于7.7.x 截止到2023.11.7 建议保持最新版本为7.7.6
 		// header: ['capturePicture', 'zoom'], // 如果templete参数不为simple,该字段将被覆盖
 		//plugin: ['talk'], // 加载插件,talk-对讲
@@ -132,184 +148,276 @@ function autoVideoOne(params) {
 		// getOSDTimeCallBack: data => console.log("获取OSDTime回调", data),
 		width: divW,
 		height: divH,
-		handleError: (err) => {
-			if (err.type === "handleRunTimeInfoError" && err.data.nErrorCode === 5) {
-			  console.log("加密设备密码错误");
+		handleError: (err: any) => {
+			if (err.type === 'handleRunTimeInfoError' && err.data.nErrorCode === 5) {
+				console.log('加密设备密码错误');
 			}
-		},		
+		},
 	});
 }
 
-
-
 const data: Tree[] = [
-  {
-    label: '节点A',
-    children: [
-      {
-        label: '菜单A-1',
-        children: [
-          {
-            label: '菜单A-1-1',
-          },
-        ],
-      },
-    ],
-  },
-  {
-    label: '节点B',
-    children: [
-      {
-        label: '菜单B-1',
-        children: [
-          {
-            label: '菜单B-1-1',
-          },
-        ],
-      },
-      {
-        label: '菜单B-2',
-        children: [
-          {
-            label: '菜单B-2-1',
-          },
-        ],
-      },
-    ],
-  },
-  {
-    label: '节点C',
-    children: [
-      {
-        label: '菜单C-1',
-        children: [
-          {
-            label: '菜单C-1-1',
-          },
-        ],
-      },
-      {
-        label: '菜单C-2',
-        children: [
-          {
-            label: '菜单C-2-1',
-          },
-        ],
-      },
-    ],
-  },
-]
+	{
+		label: '节点A',
+		children: [
+			{
+				label: '菜单A-1',
+				children: [
+					{
+						label: '菜单A-1-1',
+					},
+				],
+			},
+		],
+	},
+	{
+		label: '节点B',
+		children: [
+			{
+				label: '菜单B-1',
+				children: [
+					{
+						label: '菜单B-1-1',
+					},
+				],
+			},
+			{
+				label: '菜单B-2',
+				children: [
+					{
+						label: '菜单B-2-1',
+					},
+				],
+			},
+		],
+	},
+	{
+		label: '节点C',
+		children: [
+			{
+				label: '菜单C-1',
+				children: [
+					{
+						label: '菜单C-1-1',
+					},
+				],
+			},
+			{
+				label: '菜单C-2',
+				children: [
+					{
+						label: '菜单C-2-1',
+					},
+				],
+			},
+		],
+	},
+];
 </script>
 
 <style lang="scss" scoped>
-.sys-video-container{
-overflow:hidden;
-height:100vh;
-
+.sys-video-container {
+	overflow: hidden;
+	height: 100vh;
+}
+.common-layout {
+	background-color: #ecf5ff;
 }
-.common-layout{background-color: #ECF5FF;}
 
-.el-header{text-align: center;height: 45px;line-height: 45px;font-size: 22px;background-color: #eee;padding: 5px auto;}
-.el-aside{text-align: center;padding: 4px auto;overflow: hidden;}
-.el-aside .el-form{text-align: center;padding: 2px auto;margin: 4px;}
-.el-aside .el-button{margin: 2px;}
-.el-aside .el-input{font-size: 14px;padding: 2px;}
+.el-header {
+	text-align: center;
+	height: 45px;
+	line-height: 45px;
+	font-size: 22px;
+	background-color: #eee;
+	padding: 5px auto;
+}
+.el-aside {
+	text-align: center;
+	padding: 4px auto;
+	overflow: hidden;
+}
+.el-aside .el-form {
+	text-align: center;
+	padding: 2px auto;
+	margin: 4px;
+}
+.el-aside .el-button {
+	margin: 2px;
+}
+.el-aside .el-input {
+	font-size: 14px;
+	padding: 2px;
+}
 .el-aside .el-card {
-  margin: 10px 0 10px auto;
+	margin: 10px 0 10px auto;
+}
+.el-aside .el-card .el-button {
+	width: 90px;
+}
+.el-form-item {
+	font-weight: bold;
 }
-.el-aside .el-card .el-button{width: 90px;}
-.el-form-item{font-weight:bold;}
 
-.el-main{background-color: #111;padding: 4px;width: 100%;overflow:hidden;color:#fff}
+.el-main {
+	background-color: #111;
+	padding: 4px;
+	width: 100%;
+	overflow: hidden;
+	color: #fff;
+}
 
-.recvs {overflow-y: auto;overflow-x: hidden;width: 100%;height: 800px;}
-.rev_title{width: 100%;display: block;font-style:italic;color: #999;font-size: 14px;background-color: #fafafa;padding: 2px 4px;line-height: 25px;}
-.rev_conts{width: 100%;word-wrap: break-word;line-height: 1.5em;padding: 4px;line-height: 30px;}/*缩进text-indent:2em;*/
-.recvfontsize{text-align: center;display:block;padding-top: 4px;}
-.el-color-picker{margin-left: 4px;}
-.recv_count{text-align: left;}
-.recv_count p{line-height: 30px;}
+.recvs {
+	overflow-y: auto;
+	overflow-x: hidden;
+	width: 100%;
+	height: 800px;
+}
+.rev_title {
+	width: 100%;
+	display: block;
+	font-style: italic;
+	color: #999;
+	font-size: 14px;
+	background-color: #fafafa;
+	padding: 2px 4px;
+	line-height: 25px;
+}
+.rev_conts {
+	width: 100%;
+	word-wrap: break-word;
+	line-height: 1.5em;
+	padding: 4px;
+	line-height: 30px;
+} /*缩进text-indent:2em;*/
+.recvfontsize {
+	text-align: center;
+	display: block;
+	padding-top: 4px;
+}
+.el-color-picker {
+	margin-left: 4px;
+}
+.recv_count {
+	text-align: left;
+}
+.recv_count p {
+	line-height: 30px;
+}
 
-.header{
- font-size:24px;
- font-weight:bold;
- margin:-10px auto 10px auto;
+.header {
+	font-size: 24px;
+	font-weight: bold;
+	margin: -10px auto 10px auto;
 }
 
 h1 {
-  font-size: 16px;
-  margin-top: 10px auto 20px auto;
-  padding:5px 0px 5px 0;
+	font-size: 16px;
+	margin-top: 10px auto 20px auto;
+	padding: 5px 0px 5px 0;
 }
 
-.el-col{
- padding:4px;
+.el-col {
+	padding: 4px;
 }
 
-.el-input{font-size: 13px;}
+.el-input {
+	font-size: 13px;
+}
 .el-card {
-  margin-bottom: 12px;
+	margin-bottom: 12px;
 }
 .el-card__body {
-  padding: 24px;
+	padding: 24px;
 }
 
 .el-select {
-  width: 100%;
+	width: 100%;
 }
 
 .text-right {
-  text-align: right;
+	text-align: right;
 }
 
 .sub-btn {
-  margin-top: 30px;
+	margin-top: 30px;
 }
 
-.updateToken{display:block;line-height:30px;align:left;background:rgb(250,250,250,0.2);padding:5px;}
+.updateToken {
+	display: block;
+	line-height: 30px;
+	align: left;
+	background: rgb(250, 250, 250, 0.2);
+	padding: 5px;
+}
 
-.token_input{width:90%;}
-.hidden{display: none;}
-.w80{width: 80px;}
-.w100{width: 100px;}
-.log{font-size: 14px;color:#fff;background-color: black;}
-.center{text-align: center;}
-#ch1,#ch2,#ch3,#ch4,#ch5{width: 120px;}
-.el-tag{padding:auto 4px; margin: 5px;min-width: 60px;}
+.token_input {
+	width: 90%;
+}
+.hidden {
+	display: none;
+}
+.w80 {
+	width: 80px;
+}
+.w100 {
+	width: 100px;
+}
+.log {
+	font-size: 14px;
+	color: #fff;
+	background-color: black;
+}
+.center {
+	text-align: center;
+}
+#ch1,
+#ch2,
+#ch3,
+#ch4,
+#ch5 {
+	width: 120px;
+}
+.el-tag {
+	padding: auto 4px;
+	margin: 5px;
+	min-width: 60px;
+}
 
-el-tree span{line-height: 50px;}
+el-tree span {
+	line-height: 50px;
+}
 
 .video {
 	width: 100%;
-	height:100%;
-	overflow:hidden;
-	
+	height: 100%;
+	overflow: hidden;
+
 	.video-item {
 		display: flex;
 		padding: 5px;
-		overflow:hidden;
-		
+		overflow: hidden;
+
 		.item {
 			flex: 1;
-			
+
 			min-height: 40%;
 			width: 100%;
 			margin: 0 5px;
 			background-color: #000000;
-			color:#fff;
-			border-radius: 2px;			
+			color: #fff;
+			border-radius: 2px;
 
 			.home {
 				width: 100%;
 				height: 70vh;
-				overflow:hidden;
-				padding:0;
-				marigin:0;
+				overflow: hidden;
+				padding: 0;
+				marigin: 0;
 				aspect-ratio: 16/9; /* 设置任意宽高任意一项即可 然后使用aspect-ratio元素 动态设置比例 */
 				text-align: center;
 				justify-content: center;
 			}
-
 		}
 	}
 }

+ 674 - 594
Web/src/views/mqttx/index.vue

@@ -1,277 +1,341 @@
 <template>
-  <div class="mqtt-box">
-	<h1 class="header">MQTTX在线测试客户端</h1>
-    <el-card :model="connection">
-      <h1>连接参数(Configuration)</h1>
-      <el-form label-position="top" :model="connection">
-        <el-row :gutter="20">
-          <el-col :span="8">
-			  <el-form-item prop="host" label="协议|主机|端口">
-			  <el-input v-model="connection.host" :disabled="connSuccess" type="password"  show-password>
-			  <template #prepend>
-				<el-select
-				  v-model="connection.protocol"
-				  class="w80"
-				  :disabled="connSuccess"
-				  @change="handleProtocolChange"
-				>
-				  <el-option label="ws://" value="ws"></el-option>
-				  <el-option label="wss://" value="wss"></el-option>
-				</el-select>
-			  </template>              
-			  <template #append>
-				<el-input
-				  v-model.number="connection.port"
-				  type="number"
-				  class="w80"
-				  :disabled="connSuccess"
-				  placeholder="8083/8084"
-				></el-input>
-			  </template>
-			  </el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="0">
-            <el-form-item prop="clientId" label="标识(Client ID)唯一性">
-              <el-input v-model="connection.clientId"> </el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="0">
-            <el-form-item prop="username" label="账号(Username)">
-              <el-input v-model="connection.username"></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="0">
-            <el-form-item prop="password" label="密码(Password)">
-              <el-input v-model="connection.password" type="password" show-password></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item prop="regpacket" label="设备包名(Regpacket)">
-              <el-input v-model="connection.repacket" :disabled="connSuccess" @input="syncdhtreg"  @change="init_topic"></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item prop="dhtRegpack" label="共享传感器(dhtRegpacket)">
-              <el-input v-model="connection.dhtRegpack" :disabled="connSuccess"  @change="init_topic"></el-input>
-            </el-form-item>
-          </el-col>		  
-          <el-col :span="8" class="text-right">
-            <el-button
-              type="primary"
-			  :icon="Setting"
-			  size="default"
-			  class="sub-btn"
-              :disabled="client.connected"
-              @click="createConnection"
-              :loading="btnLoadingType === 'connect'"
-			  :style="{ display: client.connected?'none':'' }"
-            >
-              {{ client.connected ? "已连接(Connected)" : "连接(Connect)" }}
-            </el-button>
-            <el-button
-              v-if="client.connected"
-              type="warning"
-			  size="default"
-			  :icon="Discount"
-              @click="destroyConnection"
-              :loading="btnLoadingType === 'disconnect'"
-            >
-              断开(Disconnect)
-            </el-button>
-          </el-col>
-        </el-row>
-      </el-form>
-    </el-card>
-	
-    <el-card shadow="hover">
-      <h1>订阅(Subscribe)</h1>
-      <el-form label-position="top" :model="subscription">
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item prop="topic" label="订阅主题(Topic)">
-              <el-input
-                v-model="connection.subTopics"
-                :disabled="subscribedSuccess"
-				type="password"  show-password
-              ></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item prop="qos" label="订阅质量(QoS)">
-              <el-select
-                v-model="subscription.qos"
-                :disabled="subscribedSuccess"
-              >
-                <el-option
-                  v-for="qos in qosList"
-                  :key="qos"
-                  :label="qos==0?'0 至多一次':qos==1?'1 至少一次':'2 仅仅一次'"
-                  :value="qos"
-                ></el-option>
-              </el-select>
-            </el-form-item>
-          </el-col>
-		  <el-col :span="8" class="text-right">
-		    <el-button
-		      type="primary"
-		  	:icon="Connection"
-		  	size="default"
-		      class="sub-btn"
-			  :style="{ display: subscribedSuccess?'none':'' }"
-		      :loading="btnLoadingType === 'subscribe'"
-		      :disabled="!client.connected || subscribedSuccess"
-		      @click="doSubscribe"
-		    >
-		      {{ subscribedSuccess ? "已订阅(Subscribed)" : "订阅(Subscribe)" }}
-		    </el-button>
-		    <el-button
-		      v-if="subscribedSuccess"
-		      type="warning"
-		  	:icon="Discount"
-		  	size="default"
-		      class="sub-btn"
-		      :loading="btnLoadingType === 'unsubscribe'"
-		      :disabled="!client.connected"
-		      @click="doUnSubscribe"
-		    >
-		      取消(Unsubscribe)
-		    </el-button>
-		  </el-col>
-        </el-row>
-		
-      </el-form>
-    </el-card>
-	
-    <el-card shadow="hover">
-      <h1>发布(Publish)</h1>
-      <el-form label-position="top" :model="publish">
-        <el-row :gutter="30">
-          <el-col :span="8">
-            <el-form-item prop="topic" label="发布主题(Topic)">
-              <el-input v-model="connection.pubTopic" type="password"  show-password></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item prop="qos" label="发布质量(QoS)">
-              <el-select v-model="publish.qos">
-                <el-option
-                  v-for="qos in qosList"
-                  :key="qos"
-                  :label="qos==0?'0 至多一次':qos==1?'1 至少一次':'2 仅仅一次'"
-                  :value="qos"
-                ></el-option>
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item prop="retain" label="发布保留(Retain)">
-              <el-select v-model="publish.retain">
-                <el-option value="false" label="false 不保留"></el-option>
-				<el-option value="true" label="true 不保留"></el-option>
-              </el-select>
-            </el-form-item>
-          </el-col>		  
-        </el-row>
-		
-		<el-row :gutter="30">
-			<el-col :span="16">
-			  <el-form-item prop="payload" label="操作指令(Payload)">
-				<el-input v-model="publish.payload" clearable maxlength="64" show-word-limit>
-						  <!--<template #prepend>
+	<div class="mqtt-box">
+		<h1 class="header">MQTTX在线测试客户端</h1>
+		<el-card :model="connection">
+			<h1>连接参数(Configuration)</h1>
+			<el-form label-position="top" :model="connection">
+				<el-row :gutter="20">
+					<el-col :span="8">
+						<el-form-item prop="host" label="协议|主机|端口">
+							<el-input v-model="connection.host" :disabled="connSuccess" type="password" show-password>
+								<template #prepend>
+									<el-select v-model="connection.protocol" class="w80" :disabled="connSuccess" @change="handleProtocolChange">
+										<el-option label="ws://" value="ws"></el-option>
+										<el-option label="wss://" value="wss"></el-option>
+									</el-select>
+								</template>
+								<template #append>
+									<el-input v-model.number="connection.port" type="number" class="w80" :disabled="connSuccess" placeholder="8083/8084"></el-input>
+								</template>
+							</el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :span="0">
+						<el-form-item prop="clientId" label="标识(Client ID)唯一性">
+							<el-input v-model="connection.clientId"> </el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :span="0">
+						<el-form-item prop="username" label="账号(Username)">
+							<el-input v-model="connection.username"></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :span="0">
+						<el-form-item prop="password" label="密码(Password)">
+							<el-input v-model="connection.password" type="password" show-password></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :span="4">
+						<el-form-item prop="regpacket" label="设备包名(Regpacket)">
+							<el-input v-model="connection.repacket" :disabled="connSuccess" @input="syncdhtreg" @change="init_topic"></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :span="4">
+						<el-form-item prop="dhtRegpack" label="共享传感器(dhtRegpacket)">
+							<el-input v-model="connection.dhtRegpack" :disabled="connSuccess" @change="init_topic"></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :span="8" class="text-right">
+						<el-button
+							type="primary"
+							:icon="Setting"
+							size="default"
+							class="sub-btn"
+							:disabled="client.connected"
+							@click="createConnection"
+							:loading="btnLoadingType === 'connect'"
+							:style="{ display: client.connected ? 'none' : '' }"
+						>
+							{{ client.connected ? '已连接(Connected)' : '连接(Connect)' }}
+						</el-button>
+						<el-button v-if="client.connected" type="warning" size="default" :icon="Discount" @click="destroyConnection" :loading="btnLoadingType === 'disconnect'"> 断开(Disconnect) </el-button>
+					</el-col>
+				</el-row>
+			</el-form>
+		</el-card>
+
+		<el-card shadow="hover">
+			<h1>订阅(Subscribe)</h1>
+			<el-form label-position="top" :model="subscription">
+				<el-row :gutter="20">
+					<el-col :span="12">
+						<el-form-item prop="topic" label="订阅主题(Topic)">
+							<el-input v-model="connection.subTopics" :disabled="subscribedSuccess" type="password" show-password></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :span="4">
+						<el-form-item prop="qos" label="订阅质量(QoS)">
+							<el-select v-model="subscription.qos" :disabled="subscribedSuccess">
+								<el-option v-for="qos in qosList" :key="qos" :label="qos == 0 ? '0 至多一次' : qos == 1 ? '1 至少一次' : '2 仅仅一次'" :value="qos"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="8" class="text-right">
+						<el-button
+							type="primary"
+							:icon="Connection"
+							size="default"
+							class="sub-btn"
+							:style="{ display: subscribedSuccess ? 'none' : '' }"
+							:loading="btnLoadingType === 'subscribe'"
+							:disabled="!client.connected || subscribedSuccess"
+							@click="doSubscribe"
+						>
+							{{ subscribedSuccess ? '已订阅(Subscribed)' : '订阅(Subscribe)' }}
+						</el-button>
+						<el-button
+							v-if="subscribedSuccess"
+							type="warning"
+							:icon="Discount"
+							size="default"
+							class="sub-btn"
+							:loading="btnLoadingType === 'unsubscribe'"
+							:disabled="!client.connected"
+							@click="doUnSubscribe"
+						>
+							取消(Unsubscribe)
+						</el-button>
+					</el-col>
+				</el-row>
+			</el-form>
+		</el-card>
+
+		<el-card shadow="hover">
+			<h1>发布(Publish)</h1>
+			<el-form label-position="top" :model="publish">
+				<el-row :gutter="30">
+					<el-col :span="8">
+						<el-form-item prop="topic" label="发布主题(Topic)">
+							<el-input v-model="connection.pubTopic" type="password" show-password></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :span="4">
+						<el-form-item prop="qos" label="发布质量(QoS)">
+							<el-select v-model="publish.qos">
+								<el-option v-for="qos in qosList" :key="qos" :label="qos == 0 ? '0 至多一次' : qos == 1 ? '1 至少一次' : '2 仅仅一次'" :value="qos"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="4">
+						<el-form-item prop="retain" label="发布保留(Retain)">
+							<el-select v-model="publish.retain">
+								<el-option value="false" label="false 不保留"></el-option>
+								<el-option value="true" label="true 不保留"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+				</el-row>
+
+				<el-row :gutter="30">
+					<el-col :span="16">
+						<el-form-item prop="payload" label="操作指令(Payload)">
+							<el-input v-model="publish.payload" clearable maxlength="64" show-word-limit>
+								<!--<template #prepend>
 								<el-button :icon="Operation" />
 							  </template> -->
-							  <template #append>
-								<el-select v-model="publish.payload" placeholder="选择指令" style="width: 115px">
-								  <el-option label="状态查询" value="55 AA AA AA AA 91 CF" />
-								  <el-option label="全部打开" value="55 AA AA AA AA 81 A4 01" />
-								  <el-option label="全部关闭" value="55 AA AA AA AA 81 A4 00" />
-								  <el-option label="一路开关" value="55 AA AA AA AA 81 BA 01" />
-								  <el-option label="二路开关" value="55 AA AA AA AA 81 BA 02" />
-								  <el-option label="三路开关" value="55 AA AA AA AA 81 BA 03" />
-								  <el-option label="四路开关" value="55 AA AA AA AA 81 BA 04" />
-								</el-select>
-							  </template>
-					</el-input>
-			  </el-form-item>
-			</el-col>
-			<el-col :span="8" class="text-right">
-			  <el-button
-				type="success"
-				:icon="Position"
-				size="default"
-				class="sub-btn"
-				:loading="btnLoadingType === 'publish'"
-				:disabled="!client.connected"		  
-				@click="doPublish(publish.payload,connection.pubTopic)">
-				发布(Publish)
-			  </el-button>
+								<template #append>
+									<el-select v-model="publish.payload" placeholder="选择指令" style="width: 115px">
+										<el-option label="状态查询" value="55 AA AA AA AA 91 CF" />
+										<el-option label="全部打开" value="55 AA AA AA AA 81 A4 01" />
+										<el-option label="全部关闭" value="55 AA AA AA AA 81 A4 00" />
+										<el-option label="一路开关" value="55 AA AA AA AA 81 BA 01" />
+										<el-option label="二路开关" value="55 AA AA AA AA 81 BA 02" />
+										<el-option label="三路开关" value="55 AA AA AA AA 81 BA 03" />
+										<el-option label="四路开关" value="55 AA AA AA AA 81 BA 04" />
+									</el-select>
+								</template>
+							</el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :span="8" class="text-right">
+						<el-button
+							type="success"
+							:icon="Position"
+							size="default"
+							class="sub-btn"
+							:loading="btnLoadingType === 'publish'"
+							:disabled="!client.connected"
+							@click="doPublish(publish.payload, connection.pubTopic)"
+						>
+							发布(Publish)
+						</el-button>
+					</el-col>
+				</el-row>
+			</el-form>
+		</el-card>
+
+		<el-card shadow="hover">
+			<h1>
+				<el-button @click="clsmsg" type="Success" size="default" :icon="Delete" title="点击清空历史记录">接收(Receive)</el-button>
+				<el-tag size="default" title="接收次数">收 {{ recvnum }}</el-tag>
+				<el-tag size="default" :title="dht_tm">{{ dht_wsd }}</el-tag>
+				<el-tag size="default" title="设备已工作时长">{{ parseInt(runSeconds) }} 秒</el-tag>
+				<el-button
+					type="success"
+					title="关闭一路"
+					:disabled="!connection.onlineStatus | !client.connected"
+					v-if="connection.ch1_Status"
+					icon="ele-Check"
+					size="default"
+					id="ch1"
+					v-preventReClick="2000"
+					@click="switchLight('55 AA AA AA AA 81 01 00')"
+					>关闭</el-button
+				>
+				<el-button
+					type="warning"
+					title="打开一路"
+					:disabled="!connection.onlineStatus | !client.connected"
+					v-else="!connection.ch1_Status"
+					icon="ele-CloseBold"
+					size="default"
+					id="ch1"
+					v-preventReClick="2000"
+					@click="switchLight('55 AA AA AA AA 81 01 01')"
+					>打开</el-button
+				>
+				<el-button
+					type="success"
+					title="关闭二路"
+					:disabled="!connection.onlineStatus | !client.connected"
+					v-if="connection.ch2_Status"
+					icon="ele-Check"
+					size="default"
+					id="ch2"
+					v-preventReClick="2000"
+					@click="switchLight('55 AA AA AA AA 81 02 00')"
+					>关闭</el-button
+				>
+				<el-button
+					type="warning"
+					title="打开二路"
+					:disabled="!connection.onlineStatus | !client.connected"
+					v-else="!connection.ch2_Status"
+					icon="ele-CloseBold"
+					size="default"
+					id="ch2"
+					v-preventReClick="2000"
+					@click="switchLight('55 AA AA AA AA 81 02 01')"
+					>打开</el-button
+				>
+				<el-button
+					type="success"
+					title="关闭三路"
+					:disabled="!connection.onlineStatus | !client.connected"
+					v-if="connection.ch3_Status"
+					icon="ele-Check"
+					size="default"
+					id="ch3"
+					v-preventReClick="2000"
+					@click="switchLight('55 AA AA AA AA 81 03 00')"
+					>关闭</el-button
+				>
+				<el-button
+					type="warning"
+					title="打开三路"
+					:disabled="!connection.onlineStatus | !client.connected"
+					v-else="!connection.ch3_Status"
+					icon="ele-CloseBold"
+					size="default"
+					id="ch3"
+					v-preventReClick="2000"
+					@click="switchLight('55 AA AA AA AA 81 03 01')"
+					>打开</el-button
+				>
+				<el-button
+					type="success"
+					title="关闭四路"
+					:disabled="!connection.onlineStatus | !client.connected"
+					v-if="connection.ch4_Status"
+					icon="ele-Check"
+					size="default"
+					id="ch4"
+					v-preventReClick="2000"
+					@click="switchLight('55 AA AA AA AA 81 04 00')"
+					>关闭</el-button
+				>
+				<el-button
+					type="warning"
+					title="打开四路"
+					:disabled="!connection.onlineStatus | !client.connected"
+					v-else="!connection.ch4_Status"
+					icon="ele-CloseBold"
+					size="default"
+					id="ch4"
+					v-preventReClick="2000"
+					@click="switchLight('55 AA AA AA AA 81 04 01')"
+					>打开</el-button
+				>
+				<el-button
+					type="danger"
+					title="四路全部关闭"
+					:disabled="!connection.onlineStatus | !client.connected"
+					v-if="connection.all_Status"
+					icon="ele-SwitchButton"
+					size="default"
+					id="ch5"
+					@click="switchLight('55 AA AA AA AA 81 A4 00')"
+					>全关</el-button
+				>
+				<el-button
+					type="success"
+					title="四路全部打开"
+					:disabled="!connection.onlineStatus | !client.connected"
+					v-else="!connection.all_Status"
+					icon="ele-Switch"
+					size="default"
+					id="ch5"
+					@click="switchLight('55 AA AA AA AA 81 A4 01')"
+					>全开</el-button
+				>
+
+				<el-alert v-if="!client.connected || !connection.onlineStatus" title="网络服务断开或设备离线!" center type="warning" effect="light" />
+			</h1>
+			<!-- 绑定接收日志,只读 -->
+			<el-col :span="24">
+				<el-input type="textarea" :rows="8" id="recv" v-model="receivedMessages" readonly class="log"></el-input>
 			</el-col>
-		</el-row>
-      </el-form>
-    </el-card>
-	
-    <el-card shadow="hover">
-      <h1>
-		  <el-button @click="clsmsg" type="Success" size="default" :icon="Delete" title="点击清空历史记录">接收(Receive)</el-button>		  
-		   <el-tag size="default" title="接收次数">收 {{recvnum}}</el-tag>
-		   <el-tag size="default" :title="dht_tm">{{dht_wsd}}</el-tag>
-		   <el-tag size="default" title="设备已工作时长">{{parseInt(runSeconds)}} 秒</el-tag>
-		   	<el-button type="success" title="关闭一路" :disabled="!connection.onlineStatus|!client.connected" v-if="connection.ch1_Status" icon="ele-Check" size="default" id="ch1" v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 01 00')">关闭</el-button>
-		   	<el-button type="warning" title="打开一路" :disabled="!connection.onlineStatus|!client.connected" v-else="!connection.ch1_Status" icon="ele-CloseBold" size="default" id="ch1"  v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 01 01')">打开</el-button>
-		   	<el-button type="success" title="关闭二路" :disabled="!connection.onlineStatus|!client.connected" v-if="connection.ch2_Status" icon="ele-Check" size="default" id="ch2"  v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 02 00')">关闭</el-button>
-		   	<el-button type="warning" title="打开二路" :disabled="!connection.onlineStatus|!client.connected" v-else="!connection.ch2_Status" icon="ele-CloseBold" size="default" id="ch2"  v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 02 01')">打开</el-button>
-		   	<el-button type="success" title="关闭三路" :disabled="!connection.onlineStatus|!client.connected" v-if="connection.ch3_Status" icon="ele-Check" size="default" id="ch3"  v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 03 00')">关闭</el-button>
-		   	<el-button type="warning" title="打开三路" :disabled="!connection.onlineStatus|!client.connected" v-else="!connection.ch3_Status" icon="ele-CloseBold" size="default" id="ch3"  v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 03 01')">打开</el-button>
-		   	<el-button type="success" title="关闭四路" :disabled="!connection.onlineStatus|!client.connected" v-if="connection.ch4_Status" icon="ele-Check" size="default" id="ch4"  v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 04 00')">关闭</el-button>
-		   	<el-button type="warning" title="打开四路" :disabled="!connection.onlineStatus|!client.connected" v-else="!connection.ch4_Status" icon="ele-CloseBold" size="default" id="ch4"  v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 04 01')">打开</el-button>
-		   	<el-button type="danger" title="四路全部关闭" :disabled="!connection.onlineStatus|!client.connected" v-if="connection.all_Status" icon="ele-SwitchButton" size="default" id="ch5"	@click="switchLight('55 AA AA AA AA 81 A4 00')">全关</el-button>
-		   	<el-button type="success" title="四路全部打开" :disabled="!connection.onlineStatus|!client.connected" v-else="!connection.all_Status" icon="ele-Switch" size="default" id="ch5" @click="switchLight('55 AA AA AA AA 81 A4 01')">全开</el-button>
-		    
-			<el-alert v-if="!client.connected ||!connection.onlineStatus" title="网络服务断开或设备离线!" center type="warning" effect="light" />
-	  </h1>
-	  <!-- 绑定接收日志,只读 -->
-      <el-col :span="24">
-        <el-input
-          type="textarea"
-          :rows="8"
-		  id="recv"
-          v-model="receivedMessages"
-          readonly
-		  class="log"
-        ></el-input>
-      </el-col>
-    </el-card>
-  </div>
+		</el-card>
+	</div>
 </template>
 
 <script setup lang="ts" name="mqttx">
-	import { reactive, ref,onMounted,nextTick } from "vue";	
-	import { Search,ChatDotSquare,TopRight,Star,Operation,Setting,Connection,Discount,Open,Delete,Position} from '@element-plus/icons-vue';
-	import * as MQTT from "mqtt/dist/mqtt.min";//针对4.3.7版本的引用方法。5.7.x会提示错误 (import * as MQTT from "mqtt")
-	import mittBus from '/@/utils/mitt';//事件总线mitt 解决打包后错误Uncaught (in promise) ReferenceError: Cannot access 'oe' before initialization
-	
+import { reactive, ref, onMounted, nextTick } from 'vue';
+import { Search, ChatDotSquare, TopRight, Star, Operation, Setting, Connection, Discount, Open, Delete, Position } from '@element-plus/icons-vue';
+import * as MQTT from 'mqtt/dist/mqtt.min'; // 针对4.3.7版本的引用方法。5.7.x会提示错误 (import * as MQTT from "mqtt")
+import mittBus from '/@/utils/mitt'; // 事件总线mitt 解决打包后错误Uncaught (in promise) ReferenceError: Cannot access 'oe' before initialization
+
 // vue 3 + vite use MQTT.js refer to https://github.com/mqttjs/MQTT.js/issues/1269
 // https://github.com/mqttjs/MQTT.js#qos
-const qosList = [0, 1, 2];//质量
+const qosList = [0, 1, 2]; // 质量
 const now = new Date();
-const recvnum=ref(0);
-const dht_wd=ref(0);//温度,湿度
-const dht_sd=ref(0);
-const dht_tm=ref("");//同步时间
-const dht_wsd=ref("温度0℃,湿度0%");
-const runSeconds=ref(0);//工作时长
+const recvnum = ref(0);
+const dht_wd = ref(0); // 温度、湿度
+const dht_sd = ref(0);
+const dht_tm = ref(''); // 同步时间
+const dht_wsd = ref('温度0℃,湿度0%');
+const runSeconds = ref(0); // 工作时长
 
 // mqtt客户端变量 let或const
 const client = ref({
-  connected: false,//未连接
+	connected: false, //未连接
 } as MQTT.MqttClient);
 
-const receivedMessages = ref("");
-const subscribedSuccess = ref(false);//订阅成功标志
-const connSuccess=ref(false);//连接成功标志
-const btnLoadingType = ref("");
-const retryTimes = ref(0);//重连次数
+const receivedMessages = ref('');
+const subscribedSuccess = ref(false); //订阅成功标志
+const connSuccess = ref(false); //连接成功标志
+const btnLoadingType = ref('');
+const retryTimes = ref(0); //重连次数
 
 /**
  * this demo uses EMQX Public MQTT Broker (https://www.emqx.com/en/mqtt/public-mqtt5-broker), here are the details:
@@ -287,457 +351,473 @@ const retryTimes = ref(0);//重连次数
  * for more options and details, please refer to https://github.com/mqttjs/MQTT.js#mqttclientstreambuilder-options
  */
 const connection = reactive({
-  protocol: "ws",
-  host: "broker.emqx.io",
-  // ws -> 8083; wss -> 8084
-  port: 8083,
-  clientId: "emqx_vue3_" + Math.random().toString(16).substring(2, 8),
-  username: "",
-  password: "",
-  repacket: "d1ca1ff51f04",//注册包(改为您的注册包)
-  dhtRegpack:"d1ca1ff51f04",//温度注册包(可以相同可以共享传感器)
-  mqttToken:'0804d4c44c1f1bd11dea461481f19868',//授权TOKEN自己约定
-  keepalive: 30,
-  clean: true,//清除 clean session
-  connectTimeout: 30 * 1000, // ms 超时毫秒
-  reconnectPeriod: 5000, // ms 重连毫秒
-  resubscribe:true,//重新订阅
-  //定义您自己的主题
-  subTopic:'mqtt/admintnet/#0#/out',
-  willTopic:'mqtt/admintnet/#0#/will',
-  dhtTopic:'mqtt/admintnet/#0#/dht',  
-  pubTopic:'mqtt/admintnet/#0#/into',
-  subTopics:[],
-  pubPayload:'{"msg":"hellow vue3 mqtt."}',
-  onlineStatus:false,
-  ch1_Status:false,
-  ch2_Status:false,
-  ch3_Status:false,
-  ch4_Status:false,
-  all_Status:false,
-  isAC:null,//强电true
-
+	protocol: 'ws',
+	host: 'broker.emqx.io',
+	// ws -> 8083; wss -> 8084
+	port: 8083,
+	clientId: 'emqx_vue3_' + Math.random().toString(16).substring(2, 8),
+	username: '',
+	password: '',
+	repacket: 'd1ca1ff51f04', //注册包(改为您的注册包)
+	dhtRegpack: 'd1ca1ff51f04', //温度注册包(可以相同可以共享传感器)
+	mqttToken: '0804d4c44c1f1bd11dea461481f19868', //授权TOKEN自己约定
+	keepalive: 30,
+	clean: true, //清除 clean session
+	connectTimeout: 30 * 1000, // ms 超时毫秒
+	reconnectPeriod: 5000, // ms 重连毫秒
+	resubscribe: true, //重新订阅
+	//定义您自己的主题
+	subTopic: 'mqtt/admintnet/#0#/out',
+	willTopic: 'mqtt/admintnet/#0#/will',
+	dhtTopic: 'mqtt/admintnet/#0#/dht',
+	pubTopic: 'mqtt/admintnet/#0#/into',
+	subTopics: [],
+	pubPayload: '{"msg":"hellow vue3 mqtt."}',
+	onlineStatus: false,
+	ch1_Status: false,
+	ch2_Status: false,
+	ch3_Status: false,
+	ch4_Status: false,
+	all_Status: false,
+	isAC: null, //强电true
 });
 // 初始化主题
-const init_topic=()=>{
-	let st='mqtt/admintnet/#0#/out';//订阅主题
-	let pt='mqtt/admintnet/#0#/into';//发布主题
-	let ptbody='{"token":"{0}","cmd":"{1}","cmdpara":"{2}","clientid":"{3}"}';
-	let wt='mqtt/admintnet/#0#/will';//遗嘱主题
-	let dh='mqtt/admintnet/#0#/dht';//温湿度
-	connection.subTopic=st.replace("#0#",connection.repacket);
-	connection.willTopic=wt.replace("#0#",connection.repacket);	
-	connection.dhtTopic=dh.replace("#0#",connection.dhtRegpack);//温湿度	
-	connection.pubTopic=pt.replace("#0#",connection.repacket);
-	connection.subTopics=[connection.subTopic,connection.willTopic,connection.dhtTopic];
-	connection.pubPayload=ptbody;
+const init_topic = () => {
+	let st = 'mqtt/admintnet/#0#/out'; //订阅主题
+	let pt = 'mqtt/admintnet/#0#/into'; //发布主题
+	let ptbody = '{"token":"{0}","cmd":"{1}","cmdpara":"{2}","clientid":"{3}"}';
+	let wt = 'mqtt/admintnet/#0#/will'; //遗嘱主题
+	let dh = 'mqtt/admintnet/#0#/dht'; //温湿度
+	connection.subTopic = st.replace('#0#', connection.repacket);
+	connection.willTopic = wt.replace('#0#', connection.repacket);
+	connection.dhtTopic = dh.replace('#0#', connection.dhtRegpack); //温湿度
+	connection.pubTopic = pt.replace('#0#', connection.repacket);
+	connection.subTopics = [connection.subTopic, connection.willTopic, connection.dhtTopic];
+	connection.pubPayload = ptbody;
 	//console.log(connection.subTopics);
-}
+};
 
-//默认 注册包同步和传感器包名一致,反之不动
-const syncdhtreg=()=>{
-	connection.dhtRegpack=connection.repacket;
-}
+// 默认注册包同步和传感器包名一致,反之不动
+const syncdhtreg = () => {
+	connection.dhtRegpack = connection.repacket;
+};
 
-//字符串替换模拟  string.format(str,ar1,arn)
-const stringFormat =(formatted, args)=> {
-  for (let i = 0; i < args.length; i++) {
-    let regexp = new RegExp('\\{' + i + '\\}', 'gi')
-    formatted = formatted.replace(regexp, args[i])
-  }
-  return formatted
-}
+// 字符串替换模拟  string.format(str,ar1,arn)
+const stringFormat = (formatted, args) => {
+	for (let i = 0; i < args.length; i++) {
+		let regexp = new RegExp('\\{' + i + '\\}', 'gi');
+		formatted = formatted.replace(regexp, args[i]);
+	}
+	return formatted;
+};
 
 onMounted(async () => {
 	init_topic();
-	nextTick(() => {		
-	});
+	nextTick(() => {});
 });
 
 // topic & QoS for MQTT subscribing 订阅主题(多个)
 const subscription = ref({
-  topic: `$(connection.subTopics.value)`,
-  qos: 0 as MQTT.QoS,
+	topic: `$(connection.subTopics.value)`,
+	qos: 0 as MQTT.QoS,
 });
 
 // topic, QoS & payload for publishing message 发布主题
 const publish = ref({
-  topic: `${connection.pubTopic}`,
-  qos: 0 as MQTT.QoS,
-  retain:false,//保留否
-  payload: "55 AA AA AA AA 91 CF",//'{ "msg": "Hello, I am browser." }',
+	topic: `${connection.pubTopic}`,
+	qos: 0 as MQTT.QoS,
+	retain: false, //保留否
+	payload: '55 AA AA AA AA 91 CF', //'{ "msg": "Hello, I am browser." }',
 });
 
-
-
 const initData = () => {
-  client.value = {
-    connected: false,
-  } as MQTT.MqttClient;
-  retryTimes.value = 0;
-  btnLoadingType.value = "";
-  subscribedSuccess.value = false;
+	client.value = {
+		connected: false,
+	} as MQTT.MqttClient;
+	retryTimes.value = 0;
+	btnLoadingType.value = '';
+	subscribedSuccess.value = false;
 };
 
 const handleOnReConnect = () => {
-  retryTimes.value++;
-  connection.clientId="emqx_vue3_" + Math.random().toString(16).substring(2, 8);
-  console.log(retryTimes.value,"重试次数");
-  if (retryTimes.value > 5) {
-    try {
-      client.value.end();//重连超过5次断开
-      initData();
-      console.log("connection maxReconnectTimes limit, stop retry");
-	  appmessage(now.toLocaleString()+'|超出重连接次数,停止重试'+retryTimes.value)
-    } catch (error) {
-      console.log("handleOnReConnect catch error:", error);
-    }
-  }
+	retryTimes.value++;
+	connection.clientId = 'emqx_vue3_' + Math.random().toString(16).substring(2, 8);
+	console.log(retryTimes.value, '重试次数');
+	if (retryTimes.value > 5) {
+		try {
+			client.value.end(); //重连超过5次断开
+			initData();
+			console.log('connection maxReconnectTimes limit, stop retry');
+			appmessage(now.toLocaleString() + '|超出重连接次数,停止重试' + retryTimes.value);
+		} catch (error) {
+			console.log('handleOnReConnect catch error:', error);
+		}
+	}
 };
 
-    /**
-     * if protocol is "ws", connectUrl = "ws://broker.emqx.io:8083/mqtt"
-     * if protocol is "wss", connectUrl = "wss://broker.emqx.io:8084/mqtt"
-     * 
-     * /mqtt: MQTT-WebSocket uniformly uses /path as the connection path,
-     * which should be specified when connecting, and the path used on EMQX is /mqtt.
-     * 
-     * for more details about "mqtt.connect" method & options,
-     * please refer to https://github.com/mqttjs/MQTT.js#mqttconnecturl-options
-     */
+/**
+ * if protocol is "ws", connectUrl = "ws://broker.emqx.io:8083/mqtt"
+ * if protocol is "wss", connectUrl = "wss://broker.emqx.io:8084/mqtt"
+ *
+ * /mqtt: MQTT-WebSocket uniformly uses /path as the connection path,
+ * which should be specified when connecting, and the path used on EMQX is /mqtt.
+ *
+ * for more details about "mqtt.connect" method & options,
+ * please refer to https://github.com/mqttjs/MQTT.js#mqttconnecturl-options
+ */
 // create MQTT connection 创建连接
 const createConnection = () => {
-  try {
-    btnLoadingType.value = "connect";
-    const { protocol, host, port, ...options } = connection;
-    const connectUrl = `${protocol}://${host}:${port}/mqtt`;//组成新的连接字符串
-	console.log(connectUrl,"连接地址");
-    client.value = MQTT.connect(connectUrl, options);
-    if (client.value.on) {
-      // https://github.com/mqttjs/MQTT.js#event-connect
-      client.value.on("connect", () => {//v5.x  reconnect
-        btnLoadingType.value = "";
-		connSuccess.value=true;//client.value.connected;
-        console.log("connection successful",client.value.connected);
-		appmessage(now.toLocaleString()+'|连接服务成功');
-      });
-
-      // https://github.com/mqttjs/MQTT.js#event-reconnect 重连回调
-      client.value.on("reconnect", handleOnReConnect);
-      // https://github.com/mqttjs/MQTT.js#event-error
-      client.value.on("error", (error) => {
-        console.log("connection error:", error);
-		appmessage(now.toLocaleString()+'|发生错误:'+error);
-      });
-
-      // https://github.com/mqttjs/MQTT.js#event-message 接收消息,处理方法单独定义
-      client.value.on("message", (topic: string, message) => {
-		//处理方法
-		recvnum.value++;//接收次数累计
-		doAction(topic,message);//处理
-        receivedMessages.value = receivedMessages.value.concat(//拼接字符串输出
-          now.toLocaleString()+" "+`${topic}\r\n`+message.toString()+'\r\n'
-        );
-       // console.log(now.toLocaleString()+`收到消息: ${message} from topic: ${topic}`);
-		//滚动此方法可行
-		nextTick(() => {
-		   setTimeout(() => {
-		      syncBottom();//滚动到底部
-		   }, 50)
-		})
-      });
-    }
-  } catch (error) {
-    btnLoadingType.value = "";
-    console.log("mqtt.connect error:", error);
-  }
+	try {
+		btnLoadingType.value = 'connect';
+		const { protocol, host, port, ...options } = connection;
+		const connectUrl = `${protocol}://${host}:${port}/mqtt`; //组成新的连接字符串
+		console.log(connectUrl, '连接地址');
+		client.value = MQTT.connect(connectUrl, options);
+		if (client.value.on) {
+			// https://github.com/mqttjs/MQTT.js#event-connect
+			client.value.on('connect', () => {
+				//v5.x  reconnect
+				btnLoadingType.value = '';
+				connSuccess.value = true; //client.value.connected;
+				console.log('connection successful', client.value.connected);
+				appmessage(now.toLocaleString() + '|连接服务成功');
+			});
+
+			// https://github.com/mqttjs/MQTT.js#event-reconnect 重连回调
+			client.value.on('reconnect', handleOnReConnect);
+			// https://github.com/mqttjs/MQTT.js#event-error
+			client.value.on('error', (error) => {
+				console.log('connection error:', error);
+				appmessage(now.toLocaleString() + '|发生错误:' + error);
+			});
+
+			// https://github.com/mqttjs/MQTT.js#event-message 接收消息,处理方法单独定义
+			client.value.on('message', (topic: string, message) => {
+				//处理方法
+				recvnum.value++; //接收次数累计
+				doAction(topic, message); //处理
+				receivedMessages.value = receivedMessages.value.concat(
+					//拼接字符串输出
+					now.toLocaleString() + ' ' + `${topic}\r\n` + message.toString() + '\r\n'
+				);
+				// console.log(now.toLocaleString()+`收到消息: ${message} from topic: ${topic}`);
+				//滚动此方法可行
+				nextTick(() => {
+					setTimeout(() => {
+						syncBottom(); //滚动到底部
+					}, 50);
+				});
+			});
+		}
+	} catch (error) {
+		btnLoadingType.value = '';
+		console.log('mqtt.connect error:', error);
+	}
 };
 
 // 处理事件
-const doAction=(t,msg)=>{
-	let res =JSON.parse(msg.toString());//必须规范的json格式否则出错,双引号不能是单引号;;;后不安全但强大 eval('(' + message.toString() + ')'); //JSON.parse(message.toString());//json对象
-	
-	//消息不能带''否则错误
-	let regp=res.regpacket;//接收的注册包
-	let regs=connection.repacket;//订阅的注册包
-	let isOK=regp==regs?true:false;//是不是本设备的消息
-	if(!isOK||regp==null){
-		return;//不是丢弃
+const doAction = (t, msg) => {
+	let res = JSON.parse(msg.toString()); //必须规范的json格式否则出错,双引号不能是单引号;;;后不安全但强大 eval('(' + message.toString() + ')'); //JSON.parse(message.toString());//json对象
+
+	// 消息不能带''否则错误
+	let regp = res.regpacket; // 接收的注册包
+	let regs = connection.repacket; // 订阅的注册包
+	let isOK = regp == regs ? true : false; // 是不是本设备的消息
+	if (!isOK || regp == null) {
+		return; // 不是丢弃
 	}
-	
-	if(t==connection.dhtTopic)
-	{//温湿度
-		let rp=res.regpacket;
-		let wd=res.temperature;
-		let sd=res.humidity;
-		let sj=res.time;
-		let sc=res.runsec;
-		if(rp!=connection.dhtRegpack)
-		{//来自订阅的温湿度包
+
+	if (t == connection.dhtTopic) {
+		// 温湿度
+		let rp = res.regpacket;
+		let wd = res.temperature;
+		let sd = res.humidity;
+		let sj = res.time;
+		let sc = res.runsec;
+		if (rp != connection.dhtRegpack) {
+			// 来自订阅的温湿度包
 			return;
 		}
-		if(rp!=null)
-		{
-			dht_wd.value=wd;//实际应用时替换此3个变量即可
-			dht_sd.value=sd;
-			dht_tm.value="更新时间:"+sj;
-			runSeconds.value=sc;
-			dht_wsd.value="温度:"+dht_wd.value+"℃,湿度:"+dht_sd.value+"%";
+		if (rp != null) {
+			dht_wd.value = wd; // 实际应用时替换此3个变量即可
+			dht_sd.value = sd;
+			dht_tm.value = '更新时间:' + sj;
+			runSeconds.value = sc;
+			dht_wsd.value = '温度:' + dht_wd.value + '℃,湿度:' + dht_sd.value + '%';
 			//state.option.title.text="实时温湿度变化趋势图(运行"+parseInt(sc)+"秒)";
 			//updatechart(false);//实时数据(这种方法是实时推送,如果用 定时器 是定时显示的)updatewsd_time(false)
 		}
 	}
-	if(t==connection.willTopic)
-	{//遗嘱
-		if(res.redata=='offline')
-		{
-			connection.onlineStatus=false;
-		}else{
-			connection.onlineStatus=true;
+	if (t == connection.willTopic) {
+		// 遗嘱
+		if (res.redata == 'offline') {
+			connection.onlineStatus = false;
+		} else {
+			connection.onlineStatus = true;
 		}
 	}
-	if(t==connection.subTopic)
-	{
-		let rp0=res.regpacket;
-		if(rp0!=undefined){
-		   if(rp0==regs)
-		   {
-			   op(res.redata);//该设备执行指令其他放弃
-		   }
+	if (t == connection.subTopic) {
+		let rp0 = res.regpacket;
+		if (rp0 != undefined) {
+			if (rp0 == regs) {
+				op(res.redata); // 该设备执行指令其他放弃
+			}
 		}
 	}
-}
+};
 
-//处理开关状态(自定义的指令,需要修改为您自己的指令)
-const op=(cmd)=>{
-	if(cmd=="55 AA AA AA AA 82 01 01"){
-		connection.ch1_Status=true;
+// 处理开关状态(自定义的指令,需要修改为您自己的指令)
+const op = (cmd: any) => {
+	if (cmd == '55 AA AA AA AA 82 01 01') {
+		connection.ch1_Status = true;
 	}
-	if(cmd=="55 AA AA AA AA 82 01 00"){
-		connection.ch1_Status=false;
+	if (cmd == '55 AA AA AA AA 82 01 00') {
+		connection.ch1_Status = false;
 	}
-	if(cmd=="55 AA AA AA AA 82 02 01"){
-		connection.ch2_Status=true;
+	if (cmd == '55 AA AA AA AA 82 02 01') {
+		connection.ch2_Status = true;
 	}
-	if(cmd=="55 AA AA AA AA 82 02 00"){
-		connection.ch2_Status=false;
+	if (cmd == '55 AA AA AA AA 82 02 00') {
+		connection.ch2_Status = false;
 	}
-	if(cmd=="55 AA AA AA AA 82 03 01"){
-		connection.ch3_Status=true;
+	if (cmd == '55 AA AA AA AA 82 03 01') {
+		connection.ch3_Status = true;
 	}
-	if(cmd=="55 AA AA AA AA 82 03 00"){
-		connection.ch3_Status=false;
+	if (cmd == '55 AA AA AA AA 82 03 00') {
+		connection.ch3_Status = false;
 	}
-	if(cmd=="55 AA AA AA AA 82 04 01"){
-		connection.ch4_Status=true;
+	if (cmd == '55 AA AA AA AA 82 04 01') {
+		connection.ch4_Status = true;
 	}
-	if(cmd=="55 AA AA AA AA 82 04 00"){
-		connection.ch4_Status=false;
+	if (cmd == '55 AA AA AA AA 82 04 00') {
+		connection.ch4_Status = false;
 	}
-	if(cmd=="55 AA AA AA AA 82 A4 01"){
-		connection.ch1_Status=true;
-		connection.ch2_Status=true;
-		connection.ch3_Status=true;
-		connection.ch4_Status=true;
+	if (cmd == '55 AA AA AA AA 82 A4 01') {
+		connection.ch1_Status = true;
+		connection.ch2_Status = true;
+		connection.ch3_Status = true;
+		connection.ch4_Status = true;
 	}
-	if(cmd=="55 AA AA AA AA 82 A4 00"){
-		connection.ch1_Status=false;
-		connection.ch2_Status=false;
-		connection.ch3_Status=false;
-		connection.ch4_Status=false;
+	if (cmd == '55 AA AA AA AA 82 A4 00') {
+		connection.ch1_Status = false;
+		connection.ch2_Status = false;
+		connection.ch3_Status = false;
+		connection.ch4_Status = false;
 	}
-	if(cmd=="55 AA AA AA AA 84 AC 01"){
-		connection.isAC=true;
+	if (cmd == '55 AA AA AA AA 84 AC 01') {
+		connection.isAC = true;
 	}
-	if(cmd=="55 AA AA AA AA 84 AC 00"){
-		connection.isAC=false;
+	if (cmd == '55 AA AA AA AA 84 AC 00') {
+		connection.isAC = false;
 	}
-		if (connection.ch1_Status && connection.ch2_Status && connection.ch3_Status && connection.ch4_Status) {
-			connection.all_Status = true;
-		}
-		if (!connection.ch1_Status && !connection.ch2_Status && !connection.ch3_Status && !connection.ch4_Status) {
-			connection.all_Status = false;
-		}
-		if (cmd == "55 AA AA AA AA 84 AC 01") {
-			connection.isAC = true;
-		}
-		if (cmd == "55 AA AA AA AA 84 AC 00") {
-			connection.isAC = false;
-		}	
-}
+	if (connection.ch1_Status && connection.ch2_Status && connection.ch3_Status && connection.ch4_Status) {
+		connection.all_Status = true;
+	}
+	if (!connection.ch1_Status && !connection.ch2_Status && !connection.ch3_Status && !connection.ch4_Status) {
+		connection.all_Status = false;
+	}
+	if (cmd == '55 AA AA AA AA 84 AC 01') {
+		connection.isAC = true;
+	}
+	if (cmd == '55 AA AA AA AA 84 AC 00') {
+		connection.isAC = false;
+	}
+};
 
 // 自动同步滚动(建议延时执行)textarea:any=null
-const syncBottom=()=>{
-		const textarea = document.getElementById('recv');
-		if(textarea)
-		{
-			textarea.scrollTop = textarea.scrollHeight-30;
-		}
-}
+const syncBottom = () => {
+	const textarea = document.getElementById('recv');
+	if (textarea) {
+		textarea.scrollTop = textarea.scrollHeight - 30;
+	}
+};
 
 // subscribe topic 开始订阅
 // https://github.com/mqttjs/MQTT.js#mqttclientsubscribetopictopic-arraytopic-object-options-callback
 const doSubscribe = () => {
-  btnLoadingType.value = "subscribe";
-  const { topic, qos } = subscription.value;
-  console.log(connection.subTopics,"订阅主题");
-  client.value.subscribe(connection.subTopics,{ qos },
-    (error: Error, granted: mqtt.ISubscriptionGrant[]) => {
-      btnLoadingType.value = "";
-      if (error) {
-        console.log("subscribe error:", error);
-        return;
-      }
-      subscribedSuccess.value = true;//订阅成功
-	  //连接成功,发布首个问询指令
-	  switchLight("55 AA AA AA AA 91 CF");//发送首页问询指令
-      console.log("订阅成功subscribe successfully:", granted);
-    }
-  );
+	btnLoadingType.value = 'subscribe';
+	const { topic, qos } = subscription.value;
+	console.log(connection.subTopics, '订阅主题');
+	client.value.subscribe(connection.subTopics, { qos }, (error: Error, granted: mqtt.ISubscriptionGrant[]) => {
+		btnLoadingType.value = '';
+		if (error) {
+			console.log('subscribe error:', error);
+			return;
+		}
+		subscribedSuccess.value = true; //订阅成功
+		// 连接成功,发布首个问询指令
+		switchLight('55 AA AA AA AA 91 CF'); //发送首页问询指令
+		console.log('订阅成功subscribe successfully:', granted);
+	});
 };
 
 // unsubscribe topic 取消订阅
 // https://github.com/mqttjs/MQTT.js#mqttclientunsubscribetopictopic-array-options-callback
 const doUnSubscribe = () => {
-  btnLoadingType.value = "unsubscribe";
-  const { topic, qos } = subscription.value;
-  client.value.unsubscribe(connection.subTopics, { qos }, (error) => {
-    btnLoadingType.value = "";
-    subscribedSuccess.value = false;
-    if (error) {
-      console.log("unsubscribe error:", error);
-      return;
-    }
-    console.log(`unsubscribed topic: ${topic}`);
-  });
+	btnLoadingType.value = 'unsubscribe';
+	const { topic, qos } = subscription.value;
+	client.value.unsubscribe(connection.subTopics, { qos }, (error) => {
+		btnLoadingType.value = '';
+		subscribedSuccess.value = false;
+		if (error) {
+			console.log('unsubscribe error:', error);
+			return;
+		}
+		console.log(`unsubscribed topic: ${topic}`);
+	});
 };
 
 // publish message发布消息
 // https://github.com/mqttjs/MQTT.js#mqttclientpublishtopic-message-options-callback
-const doPublish = (b,t) => {
-  //btnLoadingType.value = "publish";
-  const { topic, qos, payload,retain } = publish.value;
-  //console.log(t+b,"发布内容")
-  let paybody=stringFormat(connection.pubPayload,[connection.mqttToken,b??publish.value.payload,"",connection.clientId]);//标准格式payload
-  client.value.publish(t??connection.pubTopic, paybody, { qos }, (error) => {
-	  nextTick(() => {//测试延时
-	     setTimeout(() => {
-	        btnLoadingType.value = "";
-	     }, 50)
-	  })    
-    if (error) {
-		appmessage(now.toLocaleString()+'|发布消息错误.'+error);
-		console.log("publish error:", error);
-      return;
-    }    
-  });
+const doPublish = (b, t) => {
+	//btnLoadingType.value = "publish";
+	const { topic, qos, payload, retain } = publish.value;
+	//console.log(t+b,"发布内容")
+	let paybody = stringFormat(connection.pubPayload, [connection.mqttToken, b ?? publish.value.payload, '', connection.clientId]); //标准格式payload
+	client.value.publish(t ?? connection.pubTopic, paybody, { qos }, (error) => {
+		nextTick(() => {
+			// 测试延时
+			setTimeout(() => {
+				btnLoadingType.value = '';
+			}, 50);
+		});
+		if (error) {
+			appmessage(now.toLocaleString() + '|发布消息错误.' + error);
+			console.log('publish error:', error);
+			return;
+		}
+	});
 };
 
 // 消息追加消息框
-const appmessage=(msg)=>{
-	receivedMessages.value = receivedMessages.value.concat(//拼接字符串输出
-	  msg+'\r\n'
+const appmessage = (msg) => {
+	receivedMessages.value = receivedMessages.value.concat(
+		// 拼接字符串输出
+		msg + '\r\n'
 	);
-}
+};
 
 // 开关
-const switchLight=(cmd)=>{
-	if(!client.value.connected){
-		appmessage("尚未连接到服务!");
+const switchLight = (cmd) => {
+	if (!client.value.connected) {
+		appmessage('尚未连接到服务!');
 		return;
 	}
-	let paybody=stringFormat(connection.pubPayload,[connection.mqttToken,cmd??publish.value.payload,"",connection.clientId]);
-	const { topic, qos, payload,retain } = publish.value;
+	let paybody = stringFormat(connection.pubPayload, [connection.mqttToken, cmd ?? publish.value.payload, '', connection.clientId]);
+	const { topic, qos, payload, retain } = publish.value;
 	//console.log(t+b,"发布内容")
-	client.value.publish(connection.pubTopic, paybody, { qos }, retain,(error) => {
-	  btnLoadingType.value = "";
-	  if (error) {
-	    console.log("publish error:", error);
-	    return;
-	  }
+	client.value.publish(connection.pubTopic, paybody, { qos }, retain, (error) => {
+		btnLoadingType.value = '';
+		if (error) {
+			console.log('publish error:', error);
+			return;
+		}
 	});
-}
+};
 
 // disconnect 端口连接
 // https://github.com/mqttjs/MQTT.js#mqttclientendforce-options-callback
 const destroyConnection = () => {
-  if (client.value.connected) {
-    btnLoadingType.value = "disconnect";
-    try {
-      client.value.end(false, () => {
-        initData();
-		connSuccess.value=false;
-        //console.log("断开成功 disconnected successfully");
-		appmessage(now.toLocaleString()+'|连接已断开.');
-      });
-    } catch (error) {
-      btnLoadingType.value = "";
-      console.log("断开错误 disconnect error:", error);
-    }
-  }
+	if (client.value.connected) {
+		btnLoadingType.value = 'disconnect';
+		try {
+			client.value.end(false, () => {
+				initData();
+				connSuccess.value = false;
+				//console.log("断开成功 disconnected successfully");
+				appmessage(now.toLocaleString() + '|连接已断开.');
+			});
+		} catch (error) {
+			btnLoadingType.value = '';
+			console.log('断开错误 disconnect error:', error);
+		}
+	}
 };
 
 // 端口随协议而改变
 const handleProtocolChange = (value: string) => {
-  connection.port = value === "wss" ? 8084 : 8083;
+	connection.port = value === 'wss' ? 8084 : 8083;
 };
 
 // 清空消息框
 const clsmsg = () => {
-	receivedMessages.value="";
+	receivedMessages.value = '';
 };
-
-
 </script>
 
 <style lang="scss" scoped>
 .mqtt-box {
-  max-width: 100%;
-  padding:4px;
-  margin: 10px auto 0 auto;
+	max-width: 100%;
+	padding: 4px;
+	margin: 10px auto 0 auto;
 }
 
-.header{
- font-size:24px;
- font-weight:bold;
- margin:-12px auto 8px auto;
+.header {
+	font-size: 24px;
+	font-weight: bold;
+	margin: -12px auto 8px auto;
 }
 
 h1 {
-  font-size: 16px;
-  margin-top: 10px auto 20px auto;
-  padding:5px 0px 5px 0;
+	font-size: 16px;
+	margin-top: 10px auto 20px auto;
+	padding: 5px 0px 5px 0;
 }
 
-.el-col{
- padding:4px;
+.el-col {
+	padding: 4px;
 }
 
-.el-input{font-size: 13px;}
+.el-input {
+	font-size: 13px;
+}
 .el-card {
-  margin-bottom: 12px;
+	margin-bottom: 12px;
 }
 .el-card__body {
-  padding: 24px;
+	padding: 24px;
 }
 
 .el-select {
-  width: 100%;
+	width: 100%;
 }
 
 .text-right {
-  text-align: right;
+	text-align: right;
 }
 
 .sub-btn {
-  margin-top: 20px;width:160px;
+	margin-top: 20px;
+	width: 160px;
 }
 
-.hidden{display: none;}
-.w80{width: 80px;}
-.w100{width: 100px;}
-.log{font-size: 14px;color:#fff;background-color: black;}
-.center{text-align: center;}
-#ch1,#ch2,#ch3,#ch4,#ch5{width: 120px;}
-.el-tag{padding:auto 4px; margin: 5px;min-width: 60px;}
+.hidden {
+	display: none;
+}
+.w80 {
+	width: 80px;
+}
+.w100 {
+	width: 100px;
+}
+.log {
+	font-size: 14px;
+	color: #fff;
+	background-color: black;
+}
+.center {
+	text-align: center;
+}
+#ch1,
+#ch2,
+#ch3,
+#ch4,
+#ch5 {
+	width: 120px;
+}
+.el-tag {
+	padding: auto 4px;
+	margin: 5px;
+	min-width: 60px;
+}
 </style>

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

@@ -135,6 +135,7 @@ const handleQueryTable = async () => {
 
 	var res = await getAPI(SysDatabaseApi).apiSysDatabaseTableListConfigIdGet(state.configId);
 	let tableData = res.data.result ?? [];
+	state.tableData = [];
 	tableData.forEach((element: any) => {
 		//排除zero_开头的表
 		if (!element.name.startsWith('zero_')) {
@@ -150,7 +151,6 @@ const handleQueryColumn = async () => {
 	if (state.tableName == '' || typeof state.tableName == 'undefined') return;
 
 	state.loading1 = true;
-	state.tableData = [];
 	var res = await getAPI(SysDatabaseApi).apiSysDatabaseColumnListTableNameConfigIdGet(state.tableName, state.configId);
 	state.columnData = res.data.result ?? [];
 	state.loading1 = false;

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

@@ -6,9 +6,9 @@
 					<template #header>
 						<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-Collection /></el-icon>字典
 					</template>
-					<el-form :model="state.queryDictTypeParams" ref="queryForm" :inline="true">
+					<el-form :model="state.queryDictTypeParams" ref="queryForm" :inline="true" @submit.native.prevent>
 						<el-form-item label="名称">
-							<el-input v-model="state.queryDictTypeParams.name" placeholder="字典名称" clearable />
+							<el-input v-model="state.queryDictTypeParams.name" @keyup.enter.native="handleDictTypeQuery" placeholder="字典名称" clearable />
 						</el-form-item>
 						<!-- <el-form-item label="字典编码">
 							<el-input v-model="state.queryDictTypeParams.code" placeholder="字典编码" clearable />
@@ -73,12 +73,12 @@
 					<template #header>
 						<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-Collection /></el-icon>字典值【{{ state.editDictTypeName }}】
 					</template>
-					<el-form :model="state.queryDictDataParams" ref="queryForm" :inline="true">
+					<el-form :model="state.queryDictDataParams" ref="queryForm" :inline="true" @submit.native.prevent>
 						<!-- <el-form-item label="字典值">
 							<el-input v-model="state.queryDictDataParams.value" placeholder="字典值" />
 						</el-form-item> -->
 						<el-form-item label="编码">
-							<el-input v-model="state.queryDictDataParams.code" placeholder="编码" />
+							<el-input v-model="state.queryDictDataParams.code" placeholder="编码" @keyup.enter="handleDictDataQuery" />
 						</el-form-item>
 						<el-form-item>
 							<el-button-group>