From 64e4128b3066f80707a4f2c5163f0c4b66435a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Tue, 12 Mar 2024 18:03:44 +0800 Subject: [PATCH 01/23] =?UTF-8?q?:sparkles:=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=94=9F=E6=88=90=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wwwroot/CodeGenTemplate/csharp/TplIService.txt | 3 ++- .../wwwroot/CodeGenTemplate/csharp/TplService.txt | 8 +++++--- ZR.Admin.WebApi/wwwroot/CodeGenTemplate/v3/Vue.txt | 10 +++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/csharp/TplIService.txt b/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/csharp/TplIService.txt index 8c0333ef..3b56d510 100644 --- a/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/csharp/TplIService.txt +++ b/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/csharp/TplIService.txt @@ -18,8 +18,9 @@ $if(genTable.TplCategory == "tree") $end ${replaceDto.ModelTypeName} Add${replaceDto.ModelTypeName}(${replaceDto.ModelTypeName} parm); - +$if(replaceDto.ShowBtnEdit) int Update${replaceDto.ModelTypeName}(${replaceDto.ModelTypeName} parm); +$end $if(replaceDto.ShowBtnTruncate) bool Truncate${replaceDto.ModelTypeName}(); $end diff --git a/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/csharp/TplService.txt b/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/csharp/TplService.txt index 5bfc7e4e..dd817ab4 100644 --- a/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/csharp/TplService.txt +++ b/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/csharp/TplService.txt @@ -104,6 +104,7 @@ $end $end } +$if(replaceDto.ShowBtnEdit) /// /// 修改${genTable.FunctionName} /// @@ -117,6 +118,8 @@ $else return Update(model, true$if(replaceDto.enableLog), "修改${genTable.FunctionName}"$end); $end } + +$end $if(replaceDto.ShowBtnTruncate) /// /// 清空${genTable.FunctionName} @@ -133,7 +136,6 @@ $if(replaceDto.ShowBtnTruncate) return Truncate(); } $end - $if(replaceDto.ShowBtnImport) /// /// 导入${genTable.FunctionName} @@ -167,8 +169,8 @@ $end return (msg, x.ErrorList, x.IgnoreList); } -$end +$end $if(replaceDto.ShowBtnExport) /// /// 导出${genTable.FunctionName} @@ -193,8 +195,8 @@ $end return response; } -$end +$end /// /// 查询导出表达式 /// diff --git a/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/v3/Vue.txt b/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/v3/Vue.txt index d269906d..dae9f789 100644 --- a/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/v3/Vue.txt +++ b/ZR.Admin.WebApi/wwwroot/CodeGenTemplate/v3/Vue.txt @@ -791,7 +791,11 @@ function handleDelete(row) { const Ids = row.${replaceDto.FistLowerPk} || ids.value proxy - .${confirm}confirm('是否确认删除参数编号为"' + Ids + '"的数据项?') + .${confirm}confirm('是否确认删除参数编号为"' + Ids + '"的数据项?', "警告", { + confirmButtonText: proxy.${t}t('common.ok'), + cancelButtonText: proxy.${t}t('common.cancel'), + type: "warning", + }) .then(function () { return del${genTable.BusinessName}(Ids) }) @@ -807,8 +811,8 @@ $if(replaceDto.ShowBtnTruncate) function handleClear() { proxy .${confirm}confirm("是否确认清空所有数据项?", "警告", { - confirmButtonText: "确定", - cancelButtonText: "取消", + confirmButtonText: proxy.${t}t('common.ok'), + cancelButtonText: proxy.${t}t('common.cancel'), type: "warning", }) .then(function () { From 382abafdf8fd267aae9b00a58094cd65548239bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Sun, 10 Mar 2024 17:39:03 +0800 Subject: [PATCH 02/23] =?UTF-8?q?:sparkles:=E6=96=B0=E5=A2=9E=E6=97=A0?= =?UTF-8?q?=E6=9D=83=E9=99=90=E8=AE=B0=E5=BD=95=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ZR.ServiceCore/Filters/ActionPermissionFilter.cs | 6 +++++- ZR.ServiceCore/Filters/GlobalActionMonitor.cs | 12 ++++++++---- ZR.ServiceCore/Services/SysOperLogService.cs | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ZR.ServiceCore/Filters/ActionPermissionFilter.cs b/ZR.ServiceCore/Filters/ActionPermissionFilter.cs index 7c89b793..946b86a1 100644 --- a/ZR.ServiceCore/Filters/ActionPermissionFilter.cs +++ b/ZR.ServiceCore/Filters/ActionPermissionFilter.cs @@ -81,9 +81,13 @@ public override Task OnActionExecutionAsync(ActionExecutingContext context, Acti if (!HasPermi && !Permission.Equals("common")) { logger.Info($"用户{info.UserName}没有权限访问{url},当前权限[{Permission}]"); - JsonResult result = new(new ApiResult((int)ResultCode.FORBIDDEN, $"你当前没有权限访问,请联系管理员", url)) + var apiResult = new ApiResult((int)ResultCode.FORBIDDEN, $"你当前没有权限访问,请联系管理员", url); + apiResult.Put("permi", Permission); + JsonResult result = new(apiResult) { + StatusCode = 403, ContentType = "application/json", + Value = JsonConvert.SerializeObject(apiResult) }; context.HttpContext.Response.StatusCode = 403; context.Result = result; diff --git a/ZR.ServiceCore/Filters/GlobalActionMonitor.cs b/ZR.ServiceCore/Filters/GlobalActionMonitor.cs index 4a986c34..319dddaf 100644 --- a/ZR.ServiceCore/Filters/GlobalActionMonitor.cs +++ b/ZR.ServiceCore/Filters/GlobalActionMonitor.cs @@ -47,7 +47,7 @@ public override Task OnActionExecutionAsync(ActionExecutingContext context, Acti { logger.Info($"请求参数错误,{msg}"); ApiResult response = new((int)ResultCode.PARAM_ERROR, msg); - + context.Result = new JsonResult(response); } return base.OnActionExecutionAsync(context, next); @@ -60,10 +60,10 @@ public override Task OnActionExecutionAsync(ActionExecutingContext context, Acti public override void OnResultExecuted(ResultExecutedContext context) { if (context.ActionDescriptor is not ControllerActionDescriptor controllerActionDescriptor) return; - + int statusCode = context.HttpContext.Response.StatusCode; //获得注解信息 LogAttribute logAttribute = GetLogAttribute(controllerActionDescriptor); - if (logAttribute == null) return; + if (logAttribute == null && statusCode != 403) return; try { @@ -111,7 +111,11 @@ public override void OnResultExecuted(ResultExecutedContext context) sysOperLog.OperParam = logAttribute.IsSaveRequestData ? sysOperLog.OperParam : ""; sysOperLog.JsonResult = logAttribute.IsSaveResponseData ? sysOperLog.JsonResult : ""; } - + if (statusCode == 403) + { + sysOperLog.Status = 1; + sysOperLog.ErrorMsg = "无权限访问"; + } LogEventInfo ei = new(NLog.LogLevel.Info, "GlobalActionMonitor", ""); ei.Properties["jsonResult"] = !HttpMethods.IsGet(method) ? jsonResult : ""; diff --git a/ZR.ServiceCore/Services/SysOperLogService.cs b/ZR.ServiceCore/Services/SysOperLogService.cs index 48c1b55f..a140245a 100644 --- a/ZR.ServiceCore/Services/SysOperLogService.cs +++ b/ZR.ServiceCore/Services/SysOperLogService.cs @@ -18,7 +18,7 @@ public class SysOperLogService : BaseService, ISysOperLogService /// 日志对象 public void InsertOperlog(SysOperLog operLog) { - if (operLog.OperParam.Length >= 1000) + if (operLog.OperParam != null && operLog.OperParam.Length >= 1000) { operLog.OperParam = operLog.OperParam[..1000]; } From 8cbbe5e74e5d856279a753652762baf491f7770f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Thu, 21 Mar 2024 06:58:38 +0800 Subject: [PATCH 03/23] =?UTF-8?q?:zap:=E6=96=87=E7=AB=A0=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=B0=E5=A2=9E=E5=AD=97=E6=AE=B5ArticleTy?= =?UTF-8?q?pe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ZR.ServiceCore/Model/Article.cs | 4 ++++ ZR.ServiceCore/Model/Dto/ArticleDto.cs | 2 ++ ZR.ServiceCore/Services/ArticleService.cs | 9 +++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ZR.ServiceCore/Model/Article.cs b/ZR.ServiceCore/Model/Article.cs index 6866121c..258ce6a5 100644 --- a/ZR.ServiceCore/Model/Article.cs +++ b/ZR.ServiceCore/Model/Article.cs @@ -79,6 +79,10 @@ public class Article /// public int IsTop { get; set; } /// + /// 0、文章 1、随笔 + /// + public int ArticleType { get; set; } + /// /// 摘要 /// public string AbstractText { get; set; } diff --git a/ZR.ServiceCore/Model/Dto/ArticleDto.cs b/ZR.ServiceCore/Model/Dto/ArticleDto.cs index 4d17df77..2981f465 100644 --- a/ZR.ServiceCore/Model/Dto/ArticleDto.cs +++ b/ZR.ServiceCore/Model/Dto/ArticleDto.cs @@ -13,6 +13,7 @@ public class ArticleQueryDto : PagerInfo public int? CategoryId { get; set; } public DateTime? BeginTime { get; set; } public DateTime? EndTime { get; set; } + public int? ArticleType { get; set; } } /// @@ -52,5 +53,6 @@ public class ArticleDto public int IsPublic { get; set; } = 1; public string AbstractText { get; set; } public int IsTop { get; set; } + public int ArticleType { get; set; } } } diff --git a/ZR.ServiceCore/Services/ArticleService.cs b/ZR.ServiceCore/Services/ArticleService.cs index 8ece68bd..677ce2ad 100644 --- a/ZR.ServiceCore/Services/ArticleService.cs +++ b/ZR.ServiceCore/Services/ArticleService.cs @@ -89,12 +89,17 @@ public PagedInfo GetMyList(ArticleQueryDto parm) predicate = predicate.AndIF(parm.BeginTime != null, m => m.CreateTime >= parm.BeginTime); predicate = predicate.AndIF(parm.EndTime != null, m => m.CreateTime <= parm.EndTime); predicate = predicate.And(m => m.UserId == parm.UserId); + predicate = predicate.AndIF(parm.ArticleType != null, m => m.ArticleType == parm.ArticleType); var response = Queryable() - .IgnoreColumns(x => new { x.Content }) + //.IgnoreColumns(x => new { x.Content }) .Includes(x => x.ArticleCategoryNav) .Where(predicate.ToExpression()) - .ToPage(parm); + .Select((x) => new ArticleDto() + { + Content = x.ArticleType == 1 ? x.Content : string.Empty, + }, true) + .ToPage(parm); return response; } From f6a063bb19bfc321f4db7fcd773a87a476d86816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Sat, 23 Mar 2024 17:13:34 +0800 Subject: [PATCH 04/23] =?UTF-8?q?:zap:=E4=BC=98=E5=8C=96=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/System/SysDeptController.cs | 6 ++--- .../Controllers/System/SysMenuController.cs | 2 +- .../Controllers/System/SysPostController.cs | 19 +++++++++++---- ZR.Repository/BaseRepository.cs | 2 ++ ZR.ServiceCore/Model/Dto/SysDeptDto.cs | 1 + ZR.ServiceCore/Model/SysUser.cs | 14 +++++------ .../Services/IService/ISysDeptService.cs | 1 + ZR.ServiceCore/Services/SysDeptService.cs | 23 ++++++++++++++++++- .../Services/SysPermissionService.cs | 4 ++-- ZR.ServiceCore/Services/SysUserService.cs | 22 ++++++++---------- 10 files changed, 63 insertions(+), 31 deletions(-) diff --git a/ZR.Admin.WebApi/Controllers/System/SysDeptController.cs b/ZR.Admin.WebApi/Controllers/System/SysDeptController.cs index af3c1bf9..33a667b7 100644 --- a/ZR.Admin.WebApi/Controllers/System/SysDeptController.cs +++ b/ZR.Admin.WebApi/Controllers/System/SysDeptController.cs @@ -31,7 +31,7 @@ public SysDeptController(ISysDeptService deptService [HttpGet("list")] public IActionResult List([FromQuery] SysDeptQueryDto dept) { - return SUCCESS(DeptService.GetSysDepts(dept), TIME_FORMAT_FULL); + return SUCCESS(DeptService.GetList(dept)); } /// @@ -42,11 +42,11 @@ public IActionResult List([FromQuery] SysDeptQueryDto dept) [HttpGet("list/exclude/{deptId}")] public IActionResult ExcludeChild(long deptId) { - var depts = DeptService.GetSysDepts(new SysDeptQueryDto()); + var depts = DeptService.GetList(new SysDeptQueryDto()); for (int i = 0; i < depts.Count; i++) { - SysDept d = depts[i]; + SysDeptDto d = depts[i]; long[] deptIds = Tools.SpitLongArrary(d.Ancestors); if (d.DeptId == deptId || ((IList)deptIds).Contains(deptId)) { diff --git a/ZR.Admin.WebApi/Controllers/System/SysMenuController.cs b/ZR.Admin.WebApi/Controllers/System/SysMenuController.cs index 4b7c9e2d..22349dec 100644 --- a/ZR.Admin.WebApi/Controllers/System/SysMenuController.cs +++ b/ZR.Admin.WebApi/Controllers/System/SysMenuController.cs @@ -117,7 +117,7 @@ public IActionResult MenuEdit([FromBody] MenuDto menuDto) .NameMatchingStrategy(NameMatchingStrategy.IgnoreCase);//忽略字段名称的大小写;//忽略除以上配置的所有字段 var modal = menuDto.Adapt(config).ToUpdate(HttpContext); - if (UserConstants.YES_FRAME.Equals(modal.IsFrame) && !modal.Path.StartsWith("http")) + if (UserConstants.YES_FRAME.Equals(modal.IsFrame) && (!modal.Path.StartsWith("http") && !modal.Path.StartsWith("/link"))) { return ToResponse(ApiResult.Error($"修改菜单'{modal.MenuName}'失败,地址必须以http(s)://开头")); } diff --git a/ZR.Admin.WebApi/Controllers/System/SysPostController.cs b/ZR.Admin.WebApi/Controllers/System/SysPostController.cs index 9a1b360c..694867b7 100644 --- a/ZR.Admin.WebApi/Controllers/System/SysPostController.cs +++ b/ZR.Admin.WebApi/Controllers/System/SysPostController.cs @@ -1,9 +1,9 @@ using Microsoft.AspNetCore.Mvc; using SqlSugar; using ZR.Admin.WebApi.Filters; -using ZR.Model; using ZR.Model.System; - +using ZR.Repository; +using ZR.ServiceCore.Model.Dto; namespace ZR.Admin.WebApi.Controllers.System { @@ -27,11 +27,20 @@ public SysPostController(ISysPostService postService) /// [HttpGet("list")] [ActionPermissionFilter(Permission = "system:post:list")] - public IActionResult List([FromQuery] SysPost post, [FromQuery] PagerInfo pagerInfo) + public IActionResult List([FromQuery] SysPostQueryDto dto) { var predicate = Expressionable.Create(); - predicate = predicate.AndIF(post.Status.IfNotEmpty(), it => it.Status == post.Status); - var list = PostService.GetPages(predicate.ToExpression(), pagerInfo, s => new { s.PostSort }); + predicate = predicate.AndIF(dto.Status.IfNotEmpty(), it => it.Status == dto.Status); + predicate = predicate.AndIF(dto.PostName.IfNotEmpty(), it => it.PostName.Contains(dto.PostName)); + predicate = predicate.AndIF(dto.PostCode.IfNotEmpty(), it => it.PostCode.Contains(dto.PostCode)); + + var list = PostService.Queryable() + .Where(predicate.ToExpression()) + .Select((it) => new SysPostDto + { + UserNum = SqlFunc.Subqueryable().Where(f => f.PostId == it.PostId).Sum(f => f.UserId) + }, true) + .ToPage(dto); return SUCCESS(list); } diff --git a/ZR.Repository/BaseRepository.cs b/ZR.Repository/BaseRepository.cs index 97f33099..f8a86c95 100644 --- a/ZR.Repository/BaseRepository.cs +++ b/ZR.Repository/BaseRepository.cs @@ -159,7 +159,9 @@ public DbResult UseTran(SqlSugarClient client, Action action) /// public bool UseTran2(Action action) { + Console.WriteLine("---事务开始---"); var result = Context.Ado.UseTran(() => action()); + Console.WriteLine("---事务结束---"); return result.IsSuccess; } diff --git a/ZR.ServiceCore/Model/Dto/SysDeptDto.cs b/ZR.ServiceCore/Model/Dto/SysDeptDto.cs index ee76ddf8..6e223979 100644 --- a/ZR.ServiceCore/Model/Dto/SysDeptDto.cs +++ b/ZR.ServiceCore/Model/Dto/SysDeptDto.cs @@ -27,5 +27,6 @@ public class SysDeptDto : SysBase public int Status { get; set; } public int DelFlag { get; set; } + public int UserNum { get; set; } } } diff --git a/ZR.ServiceCore/Model/SysUser.cs b/ZR.ServiceCore/Model/SysUser.cs index 01e9dbc1..5ec96655 100644 --- a/ZR.ServiceCore/Model/SysUser.cs +++ b/ZR.ServiceCore/Model/SysUser.cs @@ -81,15 +81,15 @@ public class SysUser : SysBase public string Province { get; set; } public string City { get; set; } #region 表额外字段 - public bool IsAdmin() - { - return IsAdmin(UserId); - } - public static bool IsAdmin(long userId) + + [SugarColumn(IsIgnore = true)] + public bool IsAdmin { - return 1 == userId; + get + { + return UserId == 1; + } } - /// /// 拥有角色个数 /// diff --git a/ZR.ServiceCore/Services/IService/ISysDeptService.cs b/ZR.ServiceCore/Services/IService/ISysDeptService.cs index 44b04328..7d9186ea 100644 --- a/ZR.ServiceCore/Services/IService/ISysDeptService.cs +++ b/ZR.ServiceCore/Services/IService/ISysDeptService.cs @@ -7,6 +7,7 @@ namespace ZR.ServiceCore.Services { public interface ISysDeptService : IBaseService { + List GetList(SysDeptQueryDto dept); List GetSysDepts(SysDeptQueryDto dept); string CheckDeptNameUnique(SysDept dept); int InsertDept(SysDept dept); diff --git a/ZR.ServiceCore/Services/SysDeptService.cs b/ZR.ServiceCore/Services/SysDeptService.cs index 475f978b..11b2fa33 100644 --- a/ZR.ServiceCore/Services/SysDeptService.cs +++ b/ZR.ServiceCore/Services/SysDeptService.cs @@ -18,7 +18,28 @@ public SysDeptService(ISysRoleDeptService roleDeptRepository) { RoleDeptRepository = roleDeptRepository; } + /// + /// 查询部门管理数据 + /// + /// + /// + public List GetList(SysDeptQueryDto dept) + { + var predicate = Expressionable.Create(); + predicate = predicate.And(it => it.DelFlag == 0); + predicate = predicate.AndIF(dept.DeptName.IfNotEmpty(), it => it.DeptName.Contains(dept.DeptName)); + predicate = predicate.AndIF(dept.Status != null, it => it.Status == dept.Status); + var response = Queryable() + .Where(predicate.ToExpression()) + .Select((it) => new SysDeptDto() + { + UserNum = SqlFunc.Subqueryable().Where(f => f.DeptId == it.DeptId).Count() + }, true) + .ToList(); + + return response; + } /// /// 查询部门管理数据 /// @@ -31,7 +52,7 @@ public List GetSysDepts(SysDeptQueryDto dept) predicate = predicate.AndIF(dept.DeptName.IfNotEmpty(), it => it.DeptName.Contains(dept.DeptName)); predicate = predicate.AndIF(dept.Status != null, it => it.Status == dept.Status); - var response = GetList(predicate.ToExpression()); + var response = Queryable().Where(predicate.ToExpression()).ToList(); return response; } diff --git a/ZR.ServiceCore/Services/SysPermissionService.cs b/ZR.ServiceCore/Services/SysPermissionService.cs index fc06f155..c31b5b52 100644 --- a/ZR.ServiceCore/Services/SysPermissionService.cs +++ b/ZR.ServiceCore/Services/SysPermissionService.cs @@ -30,7 +30,7 @@ public List GetRolePermission(SysUser user) { List roles = new(); // 管理员拥有所有权限 - if (user.IsAdmin()) + if (user.IsAdmin) { roles.Add("admin"); } @@ -50,7 +50,7 @@ public List GetMenuPermission(SysUser user) { List perms = new(); // 管理员拥有所有权限 - if (user.IsAdmin() || GetRolePermission(user).Exists(f => f.Equals(GlobalConstant.AdminRole))) + if (user.IsAdmin || GetRolePermission(user).Exists(f => f.Equals(GlobalConstant.AdminRole))) { perms.Add(GlobalConstant.AdminPerm); } diff --git a/ZR.ServiceCore/Services/SysUserService.cs b/ZR.ServiceCore/Services/SysUserService.cs index 4dd270e3..ab2e268c 100644 --- a/ZR.ServiceCore/Services/SysUserService.cs +++ b/ZR.ServiceCore/Services/SysUserService.cs @@ -193,11 +193,15 @@ public int ChangeUserStatus(SysUser user) public int DeleteUser(long userid) { CheckUserAllowed(new SysUser() { UserId = userid }); - //删除用户与角色关联 - UserRoleService.DeleteUserRoleByUserId((int)userid); - // 删除用户与岗位关联 - UserPostService.Delete(userid); - return Update(new SysUser() { UserId = userid, DelFlag = 2 }, it => new { it.DelFlag }, f => f.UserId == userid); + bool result = UseTran2(() => + { + //删除用户与角色关联 + UserRoleService.DeleteUserRoleByUserId((int)userid); + // 删除用户与岗位关联 + UserPostService.Delete(userid); + Update(new SysUser() { UserId = userid, DelFlag = 2 }, it => new { it.DelFlag }, f => f.UserId == userid); + }); + return result ? 1 : 0; } /// @@ -254,7 +258,7 @@ public SysUser Register(RegisterDto dto) /// public void CheckUserAllowed(SysUser user) { - if (user.IsAdmin()) + if (user.IsAdmin) { throw new CustomException("不允许操作超级管理员角色"); } @@ -267,12 +271,6 @@ public void CheckUserAllowed(SysUser user) /// public void CheckUserDataScope(long userid, long loginUserId) { - if (!SysUser.IsAdmin(loginUserId)) - { - SysUser user = new SysUser() { UserId = userid }; - - //TODO 判断用户是否有数据权限 - } } /// From 711898be22dbdf18cb088025965728f79b983bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Sat, 23 Mar 2024 19:27:55 +0800 Subject: [PATCH 05/23] add SysPostDto.cs --- ZR.ServiceCore/Model/Dto/SysPostDto.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 ZR.ServiceCore/Model/Dto/SysPostDto.cs diff --git a/ZR.ServiceCore/Model/Dto/SysPostDto.cs b/ZR.ServiceCore/Model/Dto/SysPostDto.cs new file mode 100644 index 00000000..521b8ed5 --- /dev/null +++ b/ZR.ServiceCore/Model/Dto/SysPostDto.cs @@ -0,0 +1,20 @@ +using ZR.Model; +using ZR.Model.System; + +namespace ZR.ServiceCore.Model.Dto +{ + public class SysPostDto : SysPost + { + /// + /// 用户个数 + /// + public long UserNum { get; set; } + } + + public class SysPostQueryDto : PagerInfo + { + public string PostName { get; set; } + public string Status { get; set; } + public string PostCode { get; set; } + } +} From d50e87264f5dedc866ea493d6299d9f66cb4be41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Mon, 25 Mar 2024 19:52:59 +0800 Subject: [PATCH 06/23] =?UTF-8?q?:bug:=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=94=9F=E6=88=90=E7=BC=96=E8=BE=91=E5=90=8Evue=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=AD=97=E6=AE=B5=E4=BF=9D=E5=AD=98=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ZR.Vue/src/views/tool/gen/editTable.vue | 7 ++++--- ZR.Vue/src/views/tool/gen/genInfoForm.vue | 11 ++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ZR.Vue/src/views/tool/gen/editTable.vue b/ZR.Vue/src/views/tool/gen/editTable.vue index fdb11917..d81542ce 100644 --- a/ZR.Vue/src/views/tool/gen/editTable.vue +++ b/ZR.Vue/src/views/tool/gen/editTable.vue @@ -9,9 +9,9 @@ - - - + + + diff --git a/ZR.Vue/src/views/system/menu/index.vue b/ZR.Vue/src/views/system/menu/index.vue index b96c017b..0a9d2e86 100644 --- a/ZR.Vue/src/views/system/menu/index.vue +++ b/ZR.Vue/src/views/system/menu/index.vue @@ -2,25 +2,25 @@
- + - + - 搜索 - 重置 + 搜索 + 重置 - 新增 + 新增 - 展开/折叠 + 展开/折叠 diff --git a/ZR.Vue/src/views/system/notice/index.vue b/ZR.Vue/src/views/system/notice/index.vue index 301947cc..d18e1c79 100644 --- a/ZR.Vue/src/views/system/notice/index.vue +++ b/ZR.Vue/src/views/system/notice/index.vue @@ -2,48 +2,33 @@
- + - + - + - 搜索 - 重置 + 搜索 + 重置 - 新增 + 新增 - + 修改 - 删除 diff --git a/ZR.Vue/src/views/system/post/index.vue b/ZR.Vue/src/views/system/post/index.vue index 0d0138b1..b48a3f08 100644 --- a/ZR.Vue/src/views/system/post/index.vue +++ b/ZR.Vue/src/views/system/post/index.vue @@ -2,47 +2,38 @@
- + - + - + - 搜索 - 重置 + 搜索 + 重置 - 新增 + 新增 - 修改 - 删除 + + 删除 + - 导出 + 导出 diff --git a/ZR.Vue/src/views/system/role/index.vue b/ZR.Vue/src/views/system/role/index.vue index edd8a44c..a33bc9eb 100644 --- a/ZR.Vue/src/views/system/role/index.vue +++ b/ZR.Vue/src/views/system/role/index.vue @@ -6,7 +6,6 @@ v-model="queryParams.roleName" placeholder="请输入角色名称" clearable - size="small" style="width: 240px" @keyup.enter.native="handleQuery" /> @@ -15,20 +14,20 @@ --> - + - 搜索 - 重置 + 搜索 + 重置 - 新增 + 新增 diff --git a/ZR.Vue/src/views/system/roleusers/index.vue b/ZR.Vue/src/views/system/roleusers/index.vue index c396a5a5..ccd323ab 100644 --- a/ZR.Vue/src/views/system/roleusers/index.vue +++ b/ZR.Vue/src/views/system/roleusers/index.vue @@ -20,22 +20,22 @@ @keyup.enter.native="searchRoleUser" /> - 搜索 + 搜索 - 添加用户 + 添加用户 - + 批量取消授权 - 关闭 + 关闭 diff --git a/ZR.Vue/src/views/system/user/index.vue b/ZR.Vue/src/views/system/user/index.vue index 6bda765c..554b423a 100644 --- a/ZR.Vue/src/views/system/user/index.vue +++ b/ZR.Vue/src/views/system/user/index.vue @@ -8,7 +8,6 @@ v-model="deptName" placeholder="请输入部门名称" clearable - size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px" /> @@ -33,7 +32,6 @@ v-model="queryParams.userName" placeholder="请输入用户名称" clearable - size="small" style="width: 240px" @keyup.enter.native="handleQuery" /> @@ -43,7 +41,6 @@ v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable - size="small" style="width: 240px" @keyup.enter.native="handleQuery" /> @@ -57,7 +54,6 @@ - 搜索 - 重置 + 搜索 + 重置 - 新增 + 新增 - 导入 - 导出 From 3e3b557856a8aa1e8a3fb79b18a545ede8176900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Wed, 27 Mar 2024 08:33:58 +0800 Subject: [PATCH 09/23] =?UTF-8?q?:sparkles:=E6=96=B0=E5=A2=9E=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=9C=A8=E7=BA=BF=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../System/monitor/UserOnlineLogController.cs | 74 +++++++++++++++ ZR.Admin.WebApi/wwwroot/data.xlsx | Bin 39020 -> 39318 bytes ZR.ServiceCore/Model/Dto/UserOnlineLogDto.cs | 70 ++++++++++++++ ZR.ServiceCore/Model/UserOnlineLog.cs | 54 +++++++++++ .../IService/IUserOnlineLogService.cs | 18 ++++ .../Services/UserOnlineLogService.cs | 88 ++++++++++++++++++ ZR.ServiceCore/Signalr/MessageHub.cs | 20 +++- ZR.ServiceCore/SqlSugar/DataPermi.cs | 2 + ZR.ServiceCore/SqlSugar/InitTable.cs | 1 + 9 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 ZR.Admin.WebApi/Controllers/System/monitor/UserOnlineLogController.cs create mode 100644 ZR.ServiceCore/Model/Dto/UserOnlineLogDto.cs create mode 100644 ZR.ServiceCore/Model/UserOnlineLog.cs create mode 100644 ZR.ServiceCore/Services/IService/IUserOnlineLogService.cs create mode 100644 ZR.ServiceCore/Services/UserOnlineLogService.cs diff --git a/ZR.Admin.WebApi/Controllers/System/monitor/UserOnlineLogController.cs b/ZR.Admin.WebApi/Controllers/System/monitor/UserOnlineLogController.cs new file mode 100644 index 00000000..1a0bce42 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/monitor/UserOnlineLogController.cs @@ -0,0 +1,74 @@ +using Microsoft.AspNetCore.Mvc; +using ZR.Admin.WebApi.Filters; +using ZR.ServiceCore.Model.Dto; +using ZR.ServiceCore.Monitor.IMonitorService; + +//创建时间:2024-03-27 +namespace ZR.Admin.WebApi.Controllers +{ + /// + /// 用户在线时长 + /// + [Verify] + [Route("monitor/UserOnlineLog")] + public class UserOnlineLogController : BaseController + { + /// + /// 用户在线时长接口 + /// + private readonly IUserOnlineLogService _UserOnlineLogService; + + public UserOnlineLogController(IUserOnlineLogService UserOnlineLogService) + { + _UserOnlineLogService = UserOnlineLogService; + } + + /// + /// 查询用户在线时长列表 + /// + /// + /// + [HttpGet("list")] + //[ActionPermissionFilter(Permission = "useronlinelog:list")] + public IActionResult QueryUserOnlineLog([FromQuery] UserOnlineLogQueryDto parm) + { + var response = _UserOnlineLogService.GetList(parm); + return SUCCESS(response); + } + + /// + /// 删除用户在线时长 + /// + /// + [HttpDelete("delete/{ids}")] + [ActionPermissionFilter(Permission = "useronlinelog:delete")] + [Log(Title = "用户在线时长", BusinessType = BusinessType.DELETE)] + public IActionResult DeleteUserOnlineLog([FromRoute]string ids) + { + var idArr = Tools.SplitAndConvert(ids); + + return ToResponse(_UserOnlineLogService.Delete(idArr)); + } + + /// + /// 导出用户在线时长 + /// + /// + [Log(Title = "用户在线时长", BusinessType = BusinessType.EXPORT, IsSaveResponseData = false)] + [HttpGet("export")] + [ActionPermissionFilter(Permission = "useronlinelog:export")] + public IActionResult Export([FromQuery] UserOnlineLogQueryDto parm) + { + parm.PageNum = 1; + parm.PageSize = 100000; + var list = _UserOnlineLogService.ExportList(parm).Result; + if (list == null || list.Count <= 0) + { + return ToResponse(ResultCode.FAIL, "没有要导出的数据"); + } + var result = ExportExcelMini(list, "用户在线时长", "用户在线时长"); + return ExportExcel(result.Item2, result.Item1); + } + + } +} \ No newline at end of file diff --git a/ZR.Admin.WebApi/wwwroot/data.xlsx b/ZR.Admin.WebApi/wwwroot/data.xlsx index 7fca8b6af1f16b409a895fde24edea30f5941c4c..18345c0de72a1747c959de4d587cf52800be34e5 100644 GIT binary patch delta 15720 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 rBVSXo2>&@SI2afT1Q;0F|9HMrSP>wWG$Dw + /// 用户在线时长查询对象 + ///
+ public class UserOnlineLogQueryDto : PagerInfo + { + public int? UserId { get; set; } + public string UserIP { get; set; } + public DateTime? BeginAddTime { get; set; } + public DateTime? EndAddTime { get; set; } + } + + /// + /// 用户在线时长输入输出对象 + /// + public class UserOnlineLogDto + { + [Required(ErrorMessage = "Id不能为空")] + [ExcelColumn(Name = "Id")] + [ExcelColumnName("Id")] + [JsonConverter(typeof(ValueToStringConverter))] + public long Id { get; set; } + + [Required(ErrorMessage = "用户id不能为空")] + [ExcelColumn(Name = "用户id")] + [ExcelColumnName("用户id")] + public long UserId { get; set; } + + [Required(ErrorMessage = "在线时长(分)不能为空")] + [ExcelColumn(Name = "在线时长(分)")] + [ExcelColumnName("在线时长(分)")] + public double OnlineTime { get; set; } + + [ExcelColumn(Name = "结束时间", Format = "yyyy-MM-dd HH:mm:ss", Width = 20)] + [ExcelColumnName("结束时间")] + public DateTime? AddTime { get; set; } + + [ExcelColumn(Name = "地址位置")] + [ExcelColumnName("地址位置")] + public string Location { get; set; } + + [ExcelColumn(Name = "用户IP")] + [ExcelColumnName("用户IP")] + public string UserIP { get; set; } + + [ExcelColumn(Name = "备注")] + [ExcelColumnName("备注")] + public string Remark { get; set; } + + [ExcelColumn(Name = "登录时间", Format = "yyyy-MM-dd HH:mm:ss", Width = 20)] + [ExcelColumnName("登录时间")] + public DateTime? LoginTime { get; set; } + + [ExcelColumn(Name = "今日在线时长")] + [ExcelColumnName("今日在线时长")] + public string TodayOnlineTime { get; set; } + + [ExcelColumn(Name = "登录平台")] + [ExcelColumnName("登录平台")] + public string Platform { get; set; } + [ExcelColumn(Name = "用户昵称")] + public string NickName { get; set; } + } +} \ No newline at end of file diff --git a/ZR.ServiceCore/Model/UserOnlineLog.cs b/ZR.ServiceCore/Model/UserOnlineLog.cs new file mode 100644 index 00000000..6e341bf5 --- /dev/null +++ b/ZR.ServiceCore/Model/UserOnlineLog.cs @@ -0,0 +1,54 @@ + +namespace ZR.ServiceCore.Model +{ + /// + /// 用户在线时长 + /// + [SugarTable("UserOnlineLog")] + public class UserOnlineLog + { + /// + /// Id + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = false)] + public long Id { get; set; } + + /// + /// 用户id + /// + public long UserId { get; set; } + + /// + /// 在线时长(分) + /// + public double OnlineTime { get; set; } + /// + /// 今日在线时长 + /// + public double TodayOnlineTime { get; set; } + + /// + /// 结束时间 + /// + public DateTime? AddTime { get; set; } + + /// + /// 地址位置 + /// + public string Location { get; set; } + + /// + /// 用户IP + /// + public string UserIP { get; set; } + public DateTime LoginTime { get; set; } + /// + /// 备注 + /// + public string Remark { get; set; } + /// + /// 登录平台 + /// + public string Platform { get; set; } + } +} \ No newline at end of file diff --git a/ZR.ServiceCore/Services/IService/IUserOnlineLogService.cs b/ZR.ServiceCore/Services/IService/IUserOnlineLogService.cs new file mode 100644 index 00000000..0a5e7318 --- /dev/null +++ b/ZR.ServiceCore/Services/IService/IUserOnlineLogService.cs @@ -0,0 +1,18 @@ +using ZR.Model; +using ZR.ServiceCore.Model; +using ZR.ServiceCore.Model.Dto; + +namespace ZR.ServiceCore.Monitor.IMonitorService +{ + /// + /// 用户在线时长service接口 + /// + public interface IUserOnlineLogService : IBaseService + { + PagedInfo GetList(UserOnlineLogQueryDto parm); + + UserOnlineLog AddUserOnlineLog(UserOnlineLog parm); + + PagedInfo ExportList(UserOnlineLogQueryDto parm); + } +} diff --git a/ZR.ServiceCore/Services/UserOnlineLogService.cs b/ZR.ServiceCore/Services/UserOnlineLogService.cs new file mode 100644 index 00000000..1566b30c --- /dev/null +++ b/ZR.ServiceCore/Services/UserOnlineLogService.cs @@ -0,0 +1,88 @@ +using Infrastructure.Attribute; +using ZR.Model; +using ZR.Model.System; +using ZR.Repository; +using ZR.ServiceCore.Model; +using ZR.ServiceCore.Model.Dto; +using ZR.ServiceCore.Monitor.IMonitorService; + +namespace ZR.ServiceCore.Monitor +{ + /// + /// 用户在线时长Service业务层处理 + /// + [AppService(ServiceType = typeof(IUserOnlineLogService), ServiceLifetime = LifeTime.Transient)] + public class UserOnlineLogService : BaseService, IUserOnlineLogService + { + /// + /// 查询用户在线时长列表 + /// + /// + /// + public PagedInfo GetList(UserOnlineLogQueryDto parm) + { + var predicate = QueryExp(parm); + + var response = Queryable() + //.OrderBy("Id desc") + .Where(predicate.ToExpression()) + .LeftJoin((it, u) => it.UserId == u.UserId) + .Select((it, u) => new UserOnlineLogDto() + { + NickName = u.NickName + }, true) + .ToPage(parm); + + return response; + } + + /// + /// 添加用户在线时长 + /// + /// + /// + public UserOnlineLog AddUserOnlineLog(UserOnlineLog model) + { + if (model.OnlineTime >= 0.5) + { + Insertable(model).ExecuteReturnSnowflakeId(); + } + return model; + } + + /// + /// 导出用户在线时长 + /// + /// + /// + public PagedInfo ExportList(UserOnlineLogQueryDto parm) + { + var predicate = QueryExp(parm); + + var response = Queryable() + .Where(predicate.ToExpression()) + .Select((it) => new UserOnlineLogDto() + { + }, true) + .ToPage(parm); + + return response; + } + + /// + /// 查询导出表达式 + /// + /// + /// + private static Expressionable QueryExp(UserOnlineLogQueryDto parm) + { + var predicate = Expressionable.Create(); + + predicate = predicate.AndIF(parm.UserId != null, it => it.UserId == parm.UserId); + predicate = predicate.AndIF(!string.IsNullOrEmpty(parm.UserIP), it => it.UserIP == parm.UserIP); + predicate = predicate.AndIF(parm.BeginAddTime == null, it => it.AddTime >= DateTime.Now.ToShortDateString().ParseToDateTime()); + predicate = predicate.AndIF(parm.BeginAddTime != null, it => it.AddTime >= parm.BeginAddTime); + return predicate; + } + } +} \ No newline at end of file diff --git a/ZR.ServiceCore/Signalr/MessageHub.cs b/ZR.ServiceCore/Signalr/MessageHub.cs index a21d728a..a3e73dd0 100644 --- a/ZR.ServiceCore/Signalr/MessageHub.cs +++ b/ZR.ServiceCore/Signalr/MessageHub.cs @@ -6,6 +6,7 @@ using System.Web; using UAParser; using ZR.ServiceCore.Model.Dto; +using ZR.ServiceCore.Monitor.IMonitorService; using ZR.ServiceCore.Services; namespace ZR.ServiceCore.Signalr @@ -21,11 +22,13 @@ public class MessageHub : Hub //private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private readonly ISysNoticeService SysNoticeService; private readonly ISysUserService UserService; + private readonly IUserOnlineLogService UserOnlineLogService; - public MessageHub(ISysNoticeService noticeService, ISysUserService userService) + public MessageHub(ISysNoticeService noticeService, ISysUserService userService, IUserOnlineLogService userOnlineLogService) { SysNoticeService = noticeService; UserService = userService; + UserOnlineLogService = userOnlineLogService; } private ApiResult SendNotice() @@ -130,8 +133,21 @@ public override Task OnDisconnectedAsync(Exception exception) if (userInfo != null) { userInfo.TodayOnlineTime += user?.OnlineTime ?? 0; + + UserOnlineLogService.AddUserOnlineLog(new Model.UserOnlineLog() + { + UserId = user.Userid, + AddTime = DateTime.Now, + Location = user?.Location, + OnlineTime = user.OnlineTime, + UserIP = user.UserIP, + TodayOnlineTime = userInfo.TodayOnlineTime, + Platform = user.Platform, + Remark = user.Browser, + LoginTime = user.LoginTime, + }); } - Log.WriteLine(ConsoleColor.Red, msg: $"用户{user?.Name}离开了,已在线{userInfo?.TodayOnlineTime}分,当前已连接{onlineClients.Count}个"); + Log.WriteLine(ConsoleColor.Red, msg: $"用户{user?.Name}离开了,已在线{userInfo?.TodayOnlineTime}分种,当前已连接{onlineClients.Count}个"); } return base.OnDisconnectedAsync(exception); } diff --git a/ZR.ServiceCore/SqlSugar/DataPermi.cs b/ZR.ServiceCore/SqlSugar/DataPermi.cs index 0de5d4b1..a07f0847 100644 --- a/ZR.ServiceCore/SqlSugar/DataPermi.cs +++ b/ZR.ServiceCore/SqlSugar/DataPermi.cs @@ -1,6 +1,7 @@ using Infrastructure; using SqlSugar.IOC; using ZR.Model.System; +using ZR.ServiceCore.Model; namespace ZR.ServiceCore.SqlSugar { @@ -84,6 +85,7 @@ public static void FilterData(int configId) db.QueryFilter.AddTableFilter(expUser.ToExpression()); db.QueryFilter.AddTableFilter(expRole.ToExpression()); db.QueryFilter.AddTableFilter(expLoginlog.ToExpression()); + db.QueryFilter.AddTableFilter(f => f.UserId == user.UserId, QueryFilterProvider.FilterJoinPosition.Where); } } } diff --git a/ZR.ServiceCore/SqlSugar/InitTable.cs b/ZR.ServiceCore/SqlSugar/InitTable.cs index 47c07bff..f565cd75 100644 --- a/ZR.ServiceCore/SqlSugar/InitTable.cs +++ b/ZR.ServiceCore/SqlSugar/InitTable.cs @@ -52,6 +52,7 @@ public static void InitDb() db.CodeFirst.InitTables(typeof(EmailTpl)); db.CodeFirst.InitTables(typeof(SmsCodeLog)); db.CodeFirst.InitTables(typeof(EmailLog)); + db.CodeFirst.InitTables(typeof(UserOnlineLog)); } } } From e332e2599a7801b4e71cd864ab864ece6270f18f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Thu, 28 Mar 2024 07:09:34 +0800 Subject: [PATCH 10/23] =?UTF-8?q?:zap:fix=E5=90=AF=E5=8A=A8=E8=AD=A6?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ZR.Admin.WebApi/Controllers/CommonController.cs | 4 ++-- ZR.ServiceCore/Model/Dto/UploadDto.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ZR.Admin.WebApi/Controllers/CommonController.cs b/ZR.Admin.WebApi/Controllers/CommonController.cs index 5afea0c9..9564017e 100644 --- a/ZR.Admin.WebApi/Controllers/CommonController.cs +++ b/ZR.Admin.WebApi/Controllers/CommonController.cs @@ -86,9 +86,9 @@ public IActionResult SendMsg(string msg, string toUser = "") [ActionPermissionFilter(Permission = "common")] public async Task UploadFile([FromForm] UploadDto uploadDto, StoreType storeType = StoreType.LOCAL) { - IFormFile formFile = uploadDto.File; - if (formFile == null) throw new CustomException(ResultCode.PARAM_ERROR, "上传文件不能为空"); + if (uploadDto?.File == null) throw new CustomException(ResultCode.PARAM_ERROR, "上传文件不能为空"); SysFile file = new(); + IFormFile formFile = uploadDto.File; string fileExt = Path.GetExtension(formFile.FileName);//文件后缀 double fileSize = Math.Round(formFile.Length / 1024.0, 2);//文件大小KB diff --git a/ZR.ServiceCore/Model/Dto/UploadDto.cs b/ZR.ServiceCore/Model/Dto/UploadDto.cs index a629cbf5..4e1a3218 100644 --- a/ZR.ServiceCore/Model/Dto/UploadDto.cs +++ b/ZR.ServiceCore/Model/Dto/UploadDto.cs @@ -7,15 +7,15 @@ public class UploadDto /// /// 自定文件名 /// - public string? FileName { get; set; } + public string FileName { get; set; } /// /// 存储目录 /// - public string? FileDir { get; set; } + public string FileDir { get; set; } /// /// 文件名生成类型 1 原文件名 2 自定义 3 自动生成 /// public int FileNameType { get; set; } - public IFormFile? File { get; set; } + public IFormFile File { get; set; } } } From 9f3acff77476ee00814c63ff69b3feee09293b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Thu, 28 Mar 2024 18:36:16 +0800 Subject: [PATCH 11/23] =?UTF-8?q?:zap:vue2=E5=A2=9E=E5=8A=A0=E5=8D=A0?= =?UTF-8?q?=E4=BD=8D=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ZR.Vue/src/views/dataScreen/index.vue | 4 ++++ ZR.Vue/src/views/index.vue | 12 +++++++++++- ZR.Vue/src/views/monitor/UserOnlineLog.vue | 3 +++ ZR.Vue/src/views/monitor/onlineuser/index.vue | 2 +- 4 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 ZR.Vue/src/views/dataScreen/index.vue create mode 100644 ZR.Vue/src/views/monitor/UserOnlineLog.vue diff --git a/ZR.Vue/src/views/dataScreen/index.vue b/ZR.Vue/src/views/dataScreen/index.vue new file mode 100644 index 00000000..64db54d3 --- /dev/null +++ b/ZR.Vue/src/views/dataScreen/index.vue @@ -0,0 +1,4 @@ + + diff --git a/ZR.Vue/src/views/index.vue b/ZR.Vue/src/views/index.vue index 4dbe4650..0f1a30dd 100644 --- a/ZR.Vue/src/views/index.vue +++ b/ZR.Vue/src/views/index.vue @@ -1,6 +1,5 @@