diff --git a/Api/Ewide.Application/Ewide.Application.xml b/Api/Ewide.Application/Ewide.Application.xml index 22433b6..5b8cd8b 100644 --- a/Api/Ewide.Application/Ewide.Application.xml +++ b/Api/Ewide.Application/Ewide.Application.xml @@ -626,6 +626,11 @@ 房屋详细信息 + + + 住宅查询 + + 分页查询用户 diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeOutput.cs b/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeOutput.cs index 0316d8b..de2cda4 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeOutput.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeOutput.cs @@ -29,6 +29,7 @@ namespace Ewide.Application public class GetHouseCodeOutput { public string Id { get; set; } + public string HouseCode { get; set; } public int Type { get; set; } public int Industry { get; set; } public string AreaCode { get; set; } diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseInfo/HouseInfoService.cs b/Api/Ewide.Application/Service/HouseSafety/HouseInfo/HouseInfoService.cs index 3813291..a162309 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseInfo/HouseInfoService.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseInfo/HouseInfoService.cs @@ -120,7 +120,7 @@ WHERE HC.Id=@HouseCodeId", new { houseTask.HouseCodeId } var houseEntity = await _houseInfoRep.DetachedEntities.FirstOrDefaultAsync(h => h.HouseCodeId == input.houseCode.Id); //建档审核通过的房屋数据修改时,对应的建档任务Task不处理 - if (houseEntity.State != 6) + if (houseEntity == null || houseEntity.State != 6) { var houseTask = input.PatrolInfo.Adapt(); houseTask.HouseCodeId = input.houseCode.Id; diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseMember/HouseMemberService.cs b/Api/Ewide.Application/Service/HouseSafety/HouseMember/HouseMemberService.cs index ad44a3e..b099b03 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseMember/HouseMemberService.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseMember/HouseMemberService.cs @@ -13,6 +13,7 @@ using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using System; namespace Ewide.Application.Service { @@ -108,10 +109,10 @@ WHERE 1=1"; var users = await _dapperRepository.QueryPageData(sql, input, param); - foreach (var user in users.Items) - { - user.SysEmpInfo = await _sysEmpService.GetEmpInfo(user.Id); - } + //foreach (var user in users.Items) + //{ + // user.SysEmpInfo = await _sysEmpService.GetEmpInfo(user.Id); + //} return PageDataResult.PageResult(users); } @@ -170,7 +171,10 @@ WHERE 1=1"; [UnitOfWork] public async Task DeleteUser(DeleteUserInput input) { - // 片区监管员在本片区已有安全员的情况下无法删除 + /* + * 未处理逻辑 + * 片区监管员在本片区已有安全员的情况下无法删除 + */ await _sysUserService.DeleteUser(input); } @@ -185,6 +189,7 @@ WHERE 1=1"; public async Task UpdateUser(UpdateUserInput input) { /* + * 未处理逻辑 * 如果移动组织架构,会产生以下几种逻辑 * 片区1监管员(无安全员或有其他监管员)=>片区2,直接成功 * 片区1监管员(有安全员并且无其他监管员)=>片区2,无法移动 @@ -203,7 +208,30 @@ WHERE 1=1"; [HttpGet("/houseMember/detail")] public async Task GetUser([FromQuery] QueryUserInput input) { - return await _sysUserService.GetUser(input); + var sql = @"SELECT +SU.*, +SO.Name OrgName, +SUR.RoleName, +SUR.RoleCode +FROM sys_user SU +LEFT JOIN sys_emp SE ON SU.Id = SE.Id +LEFT JOIN sys_org SO ON SE.OrgId = SO.Id +LEFT JOIN ( + SELECT + SUR.SysUserId, + GROUP_CONCAT(SR.Name) RoleName, + GROUP_CONCAT(SR.Code) RoleCode + FROM sys_user_role SUR + LEFT JOIN sys_role SR ON SUR.SysRoleId = SR.Id + GROUP BY SUR.SysUserId +) SUR ON SU.Id = SUR.SysUserId +WHERE SU.Id=@Id"; + var user = (await _dapperRepository.QueryAsync(sql, new { input.Id })).SingleOrDefault(); + if (user != null) + { + user.SysEmpInfo = await _sysEmpService.GetEmpInfo(user.Id); + } + return user; } /// @@ -254,7 +282,7 @@ WHERE 1=1"; var _sysOrgRep = Db.GetRepository(); var org = await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(p => p.Id == orgId); // 如果当前组织为街道, 则直接返回安全员 - if (org == null || org.Type <= 30) + if (org == null || org.Type <= (int)OrgType.乡镇街道办事处) { return roles.LastOrDefault(); } @@ -265,7 +293,7 @@ WHERE 1=1"; var roleIds = await _sysUserRoleRep.DetachedEntities.Where(p => userIds.Contains(p.SysUserId)).Select(p => p.SysRoleId).ToListAsync(); var _sysRoleRep = Db.GetRepository(); - var isExistZoneManager = await _sysRoleRep.DetachedEntities.AnyAsync(p => roleIds.Contains(p.Id) && p.Code == System.Enum.GetName(HouseManagerRole.ZoneManager).ToUnderScoreCase()); + var isExistZoneManager = await _sysRoleRep.DetachedEntities.AnyAsync(p => roleIds.Contains(p.Id) && p.Code == Enum.GetName(HouseManagerRole.ZoneManager).ToUnderScoreCase()); // 存在片区监管员,返回安全员, 否则返回监管员 if (isExistZoneManager) { @@ -282,7 +310,10 @@ WHERE 1=1"; [NonAction] private async Task> GetRoleRange() { - var codes = System.Enum.GetNames(typeof(HouseManagerRole)).Select(p => p.ToUnderScoreCase()); + var codes = (new[] { + Enum.GetName(HouseManagerRole.ZoneManager), + Enum.GetName(HouseManagerRole.HouseSecurityManager), + }).Select(p => p.ToUnderScoreCase()); var _sysRoleRep = Db.GetRepository(); var roles = await _sysRoleRep.DetachedEntities.Where(p => codes.Contains(p.Code)).ToListAsync(); diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseProjectInfo/HouseProjectInfoService.cs b/Api/Ewide.Application/Service/HouseSafety/HouseProjectInfo/HouseProjectInfoService.cs index 3efac64..60048d3 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseProjectInfo/HouseProjectInfoService.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseProjectInfo/HouseProjectInfoService.cs @@ -88,6 +88,7 @@ namespace Ewide.Application.Service.HouseProjectInfo var areaCodeRep = Db.GetRepository(); var projects = await _houseProjectInfoRep.DetachedEntities .Join(areaCodeRep.DetachedEntities, p => p.AreaCode, a => a.Code, (p, a) => new { p, AreaName = a.Name }) + .Where(input.Type>0, x => x.p.Type == input.Type) .Where(!string.IsNullOrEmpty(input.Name), x => x.p.Name.Contains(input.Name)) .Where(!string.IsNullOrEmpty(input.Note), x => x.p.Note.Contains(input.Note)) .Where(!string.IsNullOrEmpty(input.AreaCode), x => x.p.AreaCode == input.AreaCode) diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseQuery/Dto/HouseQueryInput.cs b/Api/Ewide.Application/Service/HouseSafety/HouseQuery/Dto/HouseQueryInput.cs new file mode 100644 index 0000000..7a97453 --- /dev/null +++ b/Api/Ewide.Application/Service/HouseSafety/HouseQuery/Dto/HouseQueryInput.cs @@ -0,0 +1,14 @@ +using Ewide.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Ewide.Application +{ + public class HouseQueryInput: PageInputBase + { + + } +} diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseQuery/Dto/HouseQueryOutput.cs b/Api/Ewide.Application/Service/HouseSafety/HouseQuery/Dto/HouseQueryOutput.cs new file mode 100644 index 0000000..a4464e1 --- /dev/null +++ b/Api/Ewide.Application/Service/HouseSafety/HouseQuery/Dto/HouseQueryOutput.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Ewide.Application +{ + public class HouseQueryOutput + { + } +} diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseQuery/HouseQueryService.cs b/Api/Ewide.Application/Service/HouseSafety/HouseQuery/HouseQueryService.cs new file mode 100644 index 0000000..453fd84 --- /dev/null +++ b/Api/Ewide.Application/Service/HouseSafety/HouseQuery/HouseQueryService.cs @@ -0,0 +1,81 @@ +using Dapper; +using Ewide.Core.Extension; +using Furion.DatabaseAccessor; +using Furion.DependencyInjection; +using Furion.DynamicApiController; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Ewide.Application.Service.HouseSafety.HouseQuery +{ + /// + /// 住宅查询 + /// + [ApiDescriptionSettings(Name = "HouseQuery", Order = 210)] + public class HouseQueryService : IHouseQueryService, IDynamicApiController, ITransient + { + private readonly IRepository _houseCodeRep; + private readonly IRepository _houseInfoRep; + private readonly IRepository _houseTaskRep; + private readonly IDapperRepository _dapperRepository; + + public HouseQueryService(IRepository HouseCodeRep, IRepository HouseInfoRep, IRepository HouseTaskRep, IDapperRepository dapperRepository) + { + _houseCodeRep = HouseCodeRep; + _houseInfoRep = HouseInfoRep; + _houseTaskRep = HouseTaskRep; + _dapperRepository = dapperRepository; + } + + [HttpPost("/houseQuery/page")] + public async Task QueryPage([FromBody] HouseQueryInput input) + { + var sql = @"SELECT + HC.ID, + HC.HouseCode, + AA.Name AreaName, + RA.Name RoadName, + CA.Name CommName, + Proj.AreaCode, + Proj.Note, + Proj.Name, + CONCAT(Proj.Name,'(',Proj.Note,')') FullProjName, + HC.Address, + IFNULL(HI.BuildingName,'') BuildingName, + IFNULL(HI.TotalFloor,0) TotalFloor, + IFNULL(HI.TotalArea,0) TotalArea, + HI.LandAttribute, + IFNULL(HI.HouseGrade,0) HouseGrade, + HC.Type, + HC.No, + HI.State, + HI.CompletedDate, + HI.CreatedTime +FROM bs_house_code HC +LEFT JOIN bs_house_info HI ON HI.HouseCodeId = HC.Id +LEFT JOIN bs_house_projectinfo Proj ON Proj.Id=HC.ProjectId +LEFT JOIN sys_area_code CA ON CA.Code = Proj.AreaCode +LEFT JOIN sys_area_code RA ON RA.AdCode = SUBSTR(CA.AdCode,1,9) +LEFT JOIN sys_area_code AA ON AA.AdCode = SUBSTR(CA.AdCode,1,6) +WHERE 1=1"; + + return await _dapperRepository.QueryPageDataDynamic(sql, input, filterFields: new string[] { + "HouseCode", + "Address", + "BuildingName", + "State", + "AreaCode", + "LandAttribute", + "HouseGrade", + "CompletedDate", + "CreatedTime", + "TotalArea", + "TotalFloor" + }); + } + } +} diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseQuery/IHouseQueryService.cs b/Api/Ewide.Application/Service/HouseSafety/HouseQuery/IHouseQueryService.cs new file mode 100644 index 0000000..aa1e217 --- /dev/null +++ b/Api/Ewide.Application/Service/HouseSafety/HouseQuery/IHouseQueryService.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Ewide.Application.Service.HouseSafety.HouseQuery +{ + public interface IHouseQueryService + { + } +} diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseTask/HouseTaskService.cs b/Api/Ewide.Application/Service/HouseSafety/HouseTask/HouseTaskService.cs index b8340ff..a99b541 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseTask/HouseTaskService.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseTask/HouseTaskService.cs @@ -40,7 +40,7 @@ namespace Ewide.Application.Service [HttpPost("/houseTask/page")] public async Task QueryPage([FromBody] QueryHouseTaskInput input) { - var sql = @"SELECT T.Id,AA.Name AreaName,RA.Name RoadName,CA.Name CommName,Proj.AreaCode,Proj.Note,Proj.Name,CONCAT(Proj.Name,'(',Proj.Note,')') FullProjName,HC.HouseCode,HC.Address,T.EndTime,HC.Type,HC.Industry,HC.No FROM `bs_house_task` T + var sql = @"SELECT T.Id,AA.Name AreaName,RA.Name RoadName,CA.Name CommName,Proj.AreaCode,Proj.Note,Proj.Name,CONCAT(Proj.Name,'(',Proj.Note,')') FullProjName,HC.HouseCode,HC.Address,T.EndTime,HC.Type,HC.Industry,HC.No,T.Status,IFNULL(HI.State,0) State FROM `bs_house_task` T LEFT JOIN bs_house_code HC ON T.HouseCodeId = HC.Id LEFT JOIN bs_house_info HI ON HI.HouseCodeId = T.HouseCodeId LEFT JOIN bs_house_projectinfo Proj ON Proj.Id=HC.ProjectId @@ -56,7 +56,7 @@ WHERE {0}"; var param = new DynamicParameters(); if (userRoles.Where(r => r.Code == Enum.GetName(HouseManagerRole.HouseSecurityManager).ToUnderScoreCase()).Count() > 0) { - sql = String.Format(sql, " (T.Status !=3) AND T.UserID=@UserID "); + sql = String.Format(sql, " T.UserID=@UserID "); param.Add("UserID", user.Id); } @@ -66,7 +66,7 @@ WHERE {0}"; param.Add("ZoneId", userOrg.Id); } - return await _dapperRepository.QueryPageDataDynamic(sql, input, param, filterFields: new string[] { "Type", "Address", "HouseCode" }); + return await _dapperRepository.QueryPageDataDynamic(sql, input, param, filterFields: new string[] { "Type", "Address", "HouseCode", "Status","State" }); } [HttpPost("/houseTask/submit")] diff --git a/Api/Ewide.Core/Enum/MenuType.cs b/Api/Ewide.Core/Enum/MenuType.cs index 80363cd..ddc74ac 100644 --- a/Api/Ewide.Core/Enum/MenuType.cs +++ b/Api/Ewide.Core/Enum/MenuType.cs @@ -20,9 +20,9 @@ namespace Ewide.Core MENU = 1, /// - /// 按钮 + /// 功能 /// - [Description("按钮")] - BTN = 2 + [Description("功能")] + FUNCTION = 2 } } diff --git a/Api/Ewide.Core/Ewide.Core.xml b/Api/Ewide.Core/Ewide.Core.xml index 51a582c..daef599 100644 --- a/Api/Ewide.Core/Ewide.Core.xml +++ b/Api/Ewide.Core/Ewide.Core.xml @@ -2417,9 +2417,9 @@ 菜单 - + - 按钮 + 功能 @@ -4933,7 +4933,7 @@ 路由元信息(路由附带扩展信息) - + 路径 @@ -4943,6 +4943,11 @@ 控制路由和子路由是否显示在 sidebar + + + 打开方式 + + 路由元信息内部类 @@ -5068,6 +5073,11 @@ 菜单类型(字典 0目录 1菜单 2按钮) + + + 打开方式(字典 0无 1组件 2内链 3外链) + + 菜单Id @@ -5138,6 +5148,16 @@ 关联显示父级 + + + 权限标识 + + + + + 备注 + + 排序,越小优先级越高 diff --git a/Api/Ewide.Core/Filter/RequestActionFilter.cs b/Api/Ewide.Core/Filter/RequestActionFilter.cs index fa6ce44..6a37036 100644 --- a/Api/Ewide.Core/Filter/RequestActionFilter.cs +++ b/Api/Ewide.Core/Filter/RequestActionFilter.cs @@ -8,6 +8,8 @@ using System.Diagnostics; using System.Security.Claims; using System.Threading.Tasks; using UAParser; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Ewide.Core { @@ -34,12 +36,29 @@ namespace Ewide.Core var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor; var descAtt = Attribute.GetCustomAttribute(actionDescriptor.MethodInfo, typeof(DescriptionAttribute)) as DescriptionAttribute; + var message = "请求成功"; + if (isRequestSucceed) + { + var result = actionContext.Result; + var resultType = result.GetType(); + if (resultType.Name == "ContentResult") + { + var resultContent = ((Microsoft.AspNetCore.Mvc.ContentResult)actionContext.Result)?.Content; + var resultJson = JsonConvert.DeserializeObject(resultContent); + message = resultJson["message"]?.ToString(); + } + } + else + { + message = actionContext.Exception.Message; + } + var sysOpLog = new SysLogOp { Name = descAtt != null ? descAtt.Description : actionDescriptor.ActionName, OpType = 1, Success = isRequestSucceed, - //Message = isRequestSucceed ? "成功" : "失败", + Message = message, Ip = httpContext.GetRemoteIpAddressToIPv4(), Location = httpRequest.GetRequestUrlAddress(), Browser = clent.UA.Family + clent.UA.Major, @@ -48,13 +67,13 @@ namespace Ewide.Core ClassName = context.Controller.ToString(), MethodName = actionDescriptor.ActionName, ReqMethod = httpRequest.Method, - //Param = JsonSerializerUtility.Serialize(context.ActionArguments), - //Result = JsonSerializerUtility.Serialize(actionContext.Result), + Param = JsonConvert.SerializeObject(context.ActionArguments), + // Result = resultContent, ElapsedTime = sw.ElapsedMilliseconds, OpTime = DateTime.Now, Account = httpContext.User?.FindFirstValue(ClaimConst.CLAINM_ACCOUNT) }; - await sysOpLog.InsertAsync(); + await sysOpLog.InsertNowAsync(); } } } diff --git a/Api/Ewide.Core/Service/Area/AreaCodeService.cs b/Api/Ewide.Core/Service/Area/AreaCodeService.cs index 66bf906..337431e 100644 --- a/Api/Ewide.Core/Service/Area/AreaCodeService.cs +++ b/Api/Ewide.Core/Service/Area/AreaCodeService.cs @@ -93,11 +93,17 @@ namespace Ewide.Core.Service.Area #endif } var query = cachedAreaCodes.Where(p => p.LevelType <= level); + var resAreaCode = new List(); if (areaCodeList != null) { - query = query.Where(p => areaCodeList.Contains(p.Code)); + foreach (var code in areaCodeList) + { + var queryRes = query.Where(p => p.Code.StartsWith(code)); + resAreaCode.AddRange(queryRes); + } + cachedAreaCodes = resAreaCode.Distinct().ToList(); } - cachedAreaCodes = query.ToList(); + return new TreeBuildUtil().DoTreeBuild(cachedAreaCodes.Select(u => new AreaTreeNode { Code = u.Code, diff --git a/Api/Ewide.Core/Service/Auth/AuthService.cs b/Api/Ewide.Core/Service/Auth/AuthService.cs index b9b3773..fba2662 100644 --- a/Api/Ewide.Core/Service/Auth/AuthService.cs +++ b/Api/Ewide.Core/Service/Auth/AuthService.cs @@ -101,6 +101,24 @@ namespace Ewide.Core.Service // 设置刷新Token令牌 _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken; + // 增加登录日志 + var loginOutput = user.Adapt(); + var clent = Parser.GetDefault().Parse(App.GetService().HttpContext.Request.Headers["User-Agent"]); + loginOutput.LastLoginBrowser = clent.UA.Family + clent.UA.Major; + loginOutput.LastLoginOs = clent.OS.Family + clent.OS.Major; + await new SysLogVis + { + Name = "登录", + Success = true, + Message = "登录成功", + Ip = loginOutput.LastLoginIp, + Browser = loginOutput.LastLoginBrowser, + Os = loginOutput.LastLoginOs, + VisType = 1, + VisTime = loginOutput.LastLoginTime, + Account = loginOutput.Account + }.InsertAsync(); + return accessToken; } @@ -163,20 +181,6 @@ namespace Ewide.Core.Service loginOutput.Menus = await _sysMenuService.GetLoginMenusAntDesign(userId, defaultActiveAppCode); } - // 增加登录日志 - //await new SysLogVis - //{ - // Name = "登录", - // Success = true, - // Message = "登录成功", - // Ip = loginOutput.LastLoginIp, - // Browser = loginOutput.LastLoginBrowser, - // Os = loginOutput.LastLoginOs, - // VisType = 1, - // VisTime = loginOutput.LastLoginTime, - // Account = loginOutput.Account - //}.InsertAsync(); - return loginOutput; } diff --git a/Api/Ewide.Core/Service/Log/SysOpLogService.cs b/Api/Ewide.Core/Service/Log/SysOpLogService.cs index 7c23226..5a6e346 100644 --- a/Api/Ewide.Core/Service/Log/SysOpLogService.cs +++ b/Api/Ewide.Core/Service/Log/SysOpLogService.cs @@ -1,4 +1,5 @@ -using Ewide.Core.Extension; +using Dapper; +using Ewide.Core.Extension; using Furion.DatabaseAccessor; using Furion.DatabaseAccessor.Extensions; using Furion.DependencyInjection; @@ -20,10 +21,12 @@ namespace Ewide.Core.Service public class SysOpLogService : ISysOpLogService, IDynamicApiController, ITransient { private readonly IRepository _sysOpLogRep; // 操作日志表仓储 + private readonly IDapperRepository _dapperRepository; - public SysOpLogService(IRepository sysOpLogRep) + public SysOpLogService(IRepository sysOpLogRep, IDapperRepository dapperRepository) { _sysOpLogRep = sysOpLogRep; + _dapperRepository = dapperRepository; } /// @@ -54,11 +57,7 @@ namespace Ewide.Core.Service [HttpPost("/sysOpLog/delete")] public async Task ClearOpLog() { - var opLogs = await _sysOpLogRep.Entities.ToListAsync(); - opLogs.ForEach(u => - { - u.Delete(); - }); + await _dapperRepository.ExecuteAsync("DELETE FROM sys_log_op"); } } } diff --git a/Api/Ewide.Core/Service/Menu/Dto/AntDesignTreeNode.cs b/Api/Ewide.Core/Service/Menu/Dto/AntDesignTreeNode.cs index adec4a9..8739f9e 100644 --- a/Api/Ewide.Core/Service/Menu/Dto/AntDesignTreeNode.cs +++ b/Api/Ewide.Core/Service/Menu/Dto/AntDesignTreeNode.cs @@ -38,12 +38,17 @@ /// /// 路径 /// - public string Path { get; set; } + public string Link { get; set; } /// /// 控制路由和子路由是否显示在 sidebar /// public bool Hidden { get; set; } + + /// + /// 打开方式 + /// + public int OpenType { get; set; } } /// diff --git a/Api/Ewide.Core/Service/Menu/Dto/MenuInput.cs b/Api/Ewide.Core/Service/Menu/Dto/MenuInput.cs index 4c86f61..eae1c9d 100644 --- a/Api/Ewide.Core/Service/Menu/Dto/MenuInput.cs +++ b/Api/Ewide.Core/Service/Menu/Dto/MenuInput.cs @@ -25,7 +25,7 @@ namespace Ewide.Core.Service /// /// 菜单类型(字典 0目录 1菜单 2按钮) /// - public virtual string Type { get; set; } + public virtual int Type { get; set; } /// /// 图标 @@ -55,7 +55,7 @@ namespace Ewide.Core.Service /// /// 打开方式(字典 0无 1组件 2内链 3外链) /// - public virtual string OpenType { get; set; } + public virtual int OpenType { get; set; } /// /// 是否可见(Y-是,N-否) @@ -99,7 +99,13 @@ namespace Ewide.Core.Service /// 菜单类型(字典 0目录 1菜单 2按钮) /// [Required(ErrorMessage = "菜单类型不能为空")] - public override string Type { get; set; } + public override int Type { get; set; } + + /// + /// 打开方式(字典 0无 1组件 2内链 3外链) + /// + [Required(ErrorMessage = "打开方式不能为空")] + public override int OpenType { get; set; } } public class DeleteMenuInput diff --git a/Api/Ewide.Core/Service/Menu/Dto/MenuTreeOutput.cs b/Api/Ewide.Core/Service/Menu/Dto/MenuTreeOutput.cs index 11fe065..c194b11 100644 --- a/Api/Ewide.Core/Service/Menu/Dto/MenuTreeOutput.cs +++ b/Api/Ewide.Core/Service/Menu/Dto/MenuTreeOutput.cs @@ -38,6 +38,16 @@ namespace Ewide.Core.Service /// public bool VisibleParent { get; set; } + /// + /// 权限标识 + /// + public string Permission { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + /// /// 排序,越小优先级越高 /// diff --git a/Api/Ewide.Core/Service/Menu/SysMenuService.cs b/Api/Ewide.Core/Service/Menu/SysMenuService.cs index fa73ffa..52cc6cf 100644 --- a/Api/Ewide.Core/Service/Menu/SysMenuService.cs +++ b/Api/Ewide.Core/Service/Menu/SysMenuService.cs @@ -53,7 +53,7 @@ namespace Ewide.Core.Service var roleIdList = await _sysUserRoleService.GetUserRoleIdList(userId); var menuIdList = await _sysRoleMenuService.GetRoleMenuIdList(roleIdList); permissions = await _sysMenuRep.DetachedEntities.Where(u => menuIdList.Contains(u.Id)) - .Where(u => u.Type == (int)MenuType.BTN) + .Where(u => u.Type == (int)MenuType.FUNCTION) .Where(u => u.Status == (int)CommonStatus.ENABLE) .Select(u => u.Permission).ToListAsync(); #if DEBUG @@ -83,8 +83,8 @@ namespace Ewide.Core.Service sysMenuList = await _sysMenuRep.DetachedEntities .Where(u => u.Status == (int)CommonStatus.ENABLE) .Where(u => u.Application == appCode) - .Where(u => u.Type != (int)MenuType.BTN) - //.Where(u => u.Weight != (int)MenuWeight.DEFAULT_WEIGHT) + .Where(u => u.Type != (int)MenuType.FUNCTION) + .Where(u => u.Weight != (int)MenuWeight.DEFAULT_WEIGHT) .OrderBy(u => u.Sort).ToListAsync(); } else @@ -96,7 +96,7 @@ namespace Ewide.Core.Service .Where(u => menuIdList.Contains(u.Id)) .Where(u => u.Status == (int)CommonStatus.ENABLE) .Where(u => u.Application == appCode) - .Where(u => u.Type != (int)MenuType.BTN) + .Where(u => u.Type != (int)MenuType.FUNCTION) .OrderBy(u => u.Sort).ToListAsync(); } // 转换成登录菜单 @@ -106,8 +106,9 @@ namespace Ewide.Core.Service Pid = u.Pid, Name = u.Code, Component = u.Component, - Redirect = u.OpenType == (int)MenuOpenType.OUTER ? u.Link : u.Redirect, - Path = u.OpenType == (int)MenuOpenType.OUTER ? u.Link : u.Router, + Redirect = u.Redirect, + Link = u.Link, + OpenType = u.OpenType, Meta = new Meta { Title = u.Name, @@ -137,7 +138,7 @@ namespace Ewide.Core.Service var roleIdList = await _sysUserRoleService.GetUserRoleIdList(userId); var menuIdList = await _sysRoleMenuService.GetRoleMenuIdList(roleIdList); return await _sysMenuRep.DetachedEntities - .Where(u => u.VisibleParent) + .Where(u => (u.Type == 2 && u.VisibleParent) || u.Type < 2) .Where(u => menuIdList.Contains(u.Id)) .Where(u => u.Status == (int)CommonStatus.ENABLE) .Select(u => u.Application).ToListAsync(); @@ -185,7 +186,7 @@ namespace Ewide.Core.Service /// 增加和编辑时检查参数 /// /// - private static void CheckMenuParam(MenuInput input) + private async Task CheckMenuParam(MenuInput input) { var type = input.Type; var router = input.Router; @@ -195,17 +196,17 @@ namespace Ewide.Core.Service if (type.Equals((int)MenuType.DIR)) { - if (string.IsNullOrEmpty(router)) - throw Oops.Oh(ErrorCode.D4001); + //if (string.IsNullOrEmpty(router)) + // throw Oops.Oh(ErrorCode.D4001); } else if (type.Equals((int)MenuType.MENU)) { - if (string.IsNullOrEmpty(router)) - throw Oops.Oh(ErrorCode.D4001); - if (string.IsNullOrEmpty(openType)) - throw Oops.Oh(ErrorCode.D4002); + //if (string.IsNullOrEmpty(router)) + // throw Oops.Oh(ErrorCode.D4001); + //if (string.IsNullOrEmpty(openType)) + // throw Oops.Oh(ErrorCode.D4002); } - else if (type.Equals((int)MenuType.BTN)) + else if (type.Equals((int)MenuType.FUNCTION)) { if (string.IsNullOrEmpty(permission)) throw Oops.Oh(ErrorCode.D4003); @@ -217,10 +218,37 @@ namespace Ewide.Core.Service //if (!urlSet.Contains(permission.Replace(":","/"))) // throw Oops.Oh(ErrorCode.meu1005); } - //按钮可以设置绑定菜单 - if(!isVisibleParent && type.Equals((int)MenuType.BTN)) + + // 检查上级菜单的类型是否正确 + var pid = input.Pid; + var flag = true; + var empty = System.Guid.Empty.ToString(); + switch(type) { - throw Oops.Oh(ErrorCode.D4004); + // 目录必须在顶级下 + case (int)MenuType.DIR: + flag = pid.Equals(empty); + break; + // 菜单必须在顶级或目录下 + case (int)MenuType.MENU: + if (!pid.Equals(empty)) + { + var parent = await _sysMenuRep.DetachedEntities.FirstOrDefaultAsync(p => p.Id == pid); + flag = parent.Type.Equals((int)MenuType.DIR); + } + break; + // 功能必须在菜单下 + case (int)MenuType.FUNCTION: + { + var parent = await _sysMenuRep.DetachedEntities.FirstOrDefaultAsync(p => p.Id == pid); + flag = parent == null ? false : parent.Type.Equals((int)MenuType.MENU); + } + break; + } + + if (!flag) + { + throw Oops.Oh("父级菜单类型错误"); } } @@ -240,7 +268,7 @@ namespace Ewide.Core.Service } // 校验参数 - CheckMenuParam(input); + await CheckMenuParam(input); var menu = input.Adapt(); menu.Pids = await CreateNewPids(input.Pid); @@ -296,7 +324,7 @@ namespace Ewide.Core.Service } // 校验参数 - CheckMenuParam(input); + await CheckMenuParam(input); // 如果是编辑,父id不能为自己的子节点 var childIdList = await _sysMenuRep.DetachedEntities.Where(u => u.Pids.Contains(input.Id.ToString())) .Select(u => u.Id).ToListAsync(); @@ -360,7 +388,7 @@ namespace Ewide.Core.Service // 更新当前菜单 oldMenu = input.Adapt(); oldMenu.Pids = newPids; - await oldMenu.UpdateAsync(ignoreNullValues: true); + await oldMenu.UpdateExcludeAsync(new[] { nameof(SysMenu.Type) }, ignoreNullValues: true); // 清除缓存 await _sysCacheService.DelByPatternAsync(CommonConst.CACHE_KEY_MENU); @@ -432,6 +460,8 @@ namespace Ewide.Core.Service Title = u.Name, Type = u.Type, VisibleParent = u.VisibleParent, + Permission = u.Permission, + Remark = u.Remark, Sort = u.Sort }).ToListAsync(); return new TreeBuildUtil().DoTreeBuild(menus); diff --git a/Api/Ewide.Core/Service/User/SysUserService.cs b/Api/Ewide.Core/Service/User/SysUserService.cs index 54dcfd3..01e6950 100644 --- a/Api/Ewide.Core/Service/User/SysUserService.cs +++ b/Api/Ewide.Core/Service/User/SysUserService.cs @@ -86,10 +86,10 @@ namespace Ewide.Core.Service // emps.Add(_sysEmpService.GetEmpInfo(long.Parse(u.Id))); //}); //await Task.WhenAll(emps); - foreach (var user in users.Items) - { - user.SysEmpInfo = await _sysEmpService.GetEmpInfo(user.Id); - } + //foreach (var user in users.Items) + //{ + // user.SysEmpInfo = await _sysEmpService.GetEmpInfo(user.Id); + //} return PageDataResult.PageResult(users); } @@ -170,8 +170,17 @@ namespace Ewide.Core.Service if (isExist) throw Oops.Oh(ErrorCode.D1003); var user = input.Adapt(); - await user.UpdateExcludeAsync(new[] { nameof(SysUser.Password), nameof(SysUser.Status), nameof(SysUser.AdminType) }, true); - user.UpdateIncludeNow(new[] { nameof(SysUser.Birthday) }); + await user.UpdateIncludeAsync(new[] { + nameof(SysUser.Account), + nameof(SysUser.NickName), + nameof(SysUser.Name), + nameof(SysUser.Birthday), + nameof(SysUser.Sex), + nameof(SysUser.Email), + nameof(SysUser.Phone), + nameof(SysUser.Tel), + }, true); + // user.UpdateIncludeNow(new[] { nameof(SysUser.Birthday) }); input.SysEmpParam.Id = user.Id.ToString(); // 更新员工及附属机构职位信息 await _sysEmpService.AddOrUpdate(input.SysEmpParam); diff --git a/Api/Ewide.Core/Util/MachineUtil.cs b/Api/Ewide.Core/Util/MachineUtil.cs index fdb12a5..b083552 100644 --- a/Api/Ewide.Core/Util/MachineUtil.cs +++ b/Api/Ewide.Core/Util/MachineUtil.cs @@ -1,5 +1,6 @@ using Furion.RemoteRequest.Extensions; using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -26,10 +27,8 @@ namespace Ewide.Core var ramInfo = GetRamInfo(); return new { - TotalRam = Math.Ceiling(ramInfo.Total / 1024).ToString() + " GB", // 总内存 RamRate = Math.Ceiling(100 * ramInfo.Used / ramInfo.Total), // 内存使用率 CpuRate = Math.Ceiling(double.Parse(GetCPURate())), // cpu使用率 - RunTime = GetRunTime() }; } @@ -40,20 +39,28 @@ namespace Ewide.Core public static async Task GetMachineBaseInfo() { var assemblyName = typeof(Furion.App).Assembly.GetName(); - //var networkInfo = NetworkInfo.GetNetworkInfo(); - //var (Received, Send) = networkInfo.GetInternetSpeed(1000); return new { WanIp = await GetWanIpFromPCOnline(), // 外网IP - SendAndReceived = "",// "上行" + Math.Round(networkInfo.SendLength / 1024.0 / 1024 / 1024, 2) + "GB 下行" + Math.Round(networkInfo.ReceivedLength / 1024.0 / 1024 / 1024, 2) + "GB", // 上下行流量统计 - LanIp = "",//networkInfo.AddressIpv4.ToString(), // 局域网IP + LanIp = Dns.GetHostAddresses(string.Empty).Last().ToString(), // 局域网IP IpMac = "",//networkInfo.Mac, // Mac地址 HostName = Environment.MachineName, // HostName + CurrentDirectory = Environment.CurrentDirectory, // 系统路径 SystemOs = RuntimeInformation.OSDescription, // 系统名称 OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(), // 系统架构 - ProcessorCount = Environment.ProcessorCount.ToString() + "核", // CPU核心数 + ProcessorCount = Environment.ProcessorCount, // CPU核心数 FrameworkDescription = RuntimeInformation.FrameworkDescription + " + " + assemblyName.Name.ToString() + assemblyName.Version.ToString(), // .NET和Furion版本 - NetworkSpeed = ""//"上行" + Send / 1024 + "kb/s 下行" + Received / 1024 + "kb/s" // 网络速度 + NetworkSpeed = "",//"上行" + Send / 1024 + "kb/s 下行" + Received / 1024 + "kb/s" // 网络速度 + // Cpu名称 + CpuName = GetCPUName(), + // Cpu基准速度 + CpuBaseSpeed = GetCPUSpeed(), + // 内存总量 + TotalRam = GetRamInfo().Total, + // 运行时间 + RunTime = (long)(DateTime.Now - Process.GetCurrentProcess().StartTime).TotalMilliseconds, + // 磁盘信息 + DiskInfo = GetDiskInfo(), }; } @@ -94,24 +101,86 @@ namespace Ewide.Core return RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux); } + private static string GetCPUName() + { + string result; + if (IsUnix()) + { + // ?????? + var output = ShellUtil.Bash(""); + result = output.Trim(); + } + else + { + var output = ShellUtil.Cmd("wmic", "cpu get Name"); + result = output.Replace("Name", string.Empty).Trim(); + } + return result; + } + + private static string GetCPUSpeed() + { + string result; + if (IsUnix()) + { + // ?????? + var output = ShellUtil.Bash(""); + result = output.Trim(); + } + else + { + var output = ShellUtil.Cmd("wmic", "cpu get CurrentClockSpeed"); + result = output.Replace("CurrentClockSpeed", string.Empty).Trim(); + } + return result; + } + + private static List> GetDiskInfo() + { + var result = new List>(); + if (IsUnix()) + { + // ?????? + var output = ShellUtil.Bash(""); + } + else + { + var output = ShellUtil.Cmd("wmic", "LOGICALDISK get Name,Description,FileSystem,Size,FreeSpace"); + var strArray = output.Replace("\r", "").Trim().Split('\n') + .Select(p => System.Text.RegularExpressions.Regex.Replace(p.Trim(), @"\s+", ",").Split(',')); + var keyArray = strArray.First(); + var valueArray = strArray.Where((p, i) => i > 0).ToArray(); + foreach(var value in valueArray) + { + var dict = new Dictionary(); + for(var i = 0;i /// 获取CPU使用率 /// /// private static string GetCPURate() { - string cpuRate; + string result; if (IsUnix()) { var output = ShellUtil.Bash("top -b -n1 | grep \"Cpu(s)\" | awk '{print $2 + $4}'"); - cpuRate = output.Trim(); + result = output.Trim(); } else { var output = ShellUtil.Cmd("wmic", "cpu get LoadPercentage"); - cpuRate = output.Replace("LoadPercentage", string.Empty).Trim(); + result = output.Replace("LoadPercentage", string.Empty).Trim(); } - return cpuRate; + return result; } /// diff --git a/web-react/package.json b/web-react/package.json index 502591d..8f2e44b 100644 --- a/web-react/package.json +++ b/web-react/package.json @@ -19,7 +19,9 @@ "nprogress": "^0.2.0", "photoswipe": "^4.1.3", "react": "^17.0.2", + "react-color": "^2.19.3", "react-dom": "^17.0.2", + "react-json-view": "^1.21.3", "react-monaco-editor": "^0.43.0", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", @@ -58,4 +60,4 @@ "last 1 safari version" ] } -} \ No newline at end of file +} diff --git a/web-react/public/seed/query-table-form/form.jsx b/web-react/public/seed/query-table-form/form.jsx index 0c83022..cebd795 100644 --- a/web-react/public/seed/query-table-form/form.jsx +++ b/web-react/public/seed/query-table-form/form.jsx @@ -1,7 +1,7 @@ import React, { Component } from 'react' import { Form, Spin } from 'antd' import { AntIcon } from 'components' -import { cloneDeep } from 'lodash' +import { api } from 'common/api' const initialValues = {} @@ -31,14 +31,15 @@ export default class form extends Component { * @param {*} params */ async fillData(params) { - this.record = cloneDeep(params.record) + const state = { loading: false } //#region 从后端转换成前段所需格式,也可以在此处调用获取详细数据接口 + if (params.id) { + this.record = (await api).data + } //#endregion this.form.current.setFieldsValue(this.record) - this.setState({ - loading: false, - }) + this.setState(state) } /** diff --git a/web-react/public/seed/query-table/index.jsx b/web-react/public/seed/query-table/index.jsx index def3cf2..95eb779 100644 --- a/web-react/public/seed/query-table/index.jsx +++ b/web-react/public/seed/query-table/index.jsx @@ -63,16 +63,16 @@ export default class index extends Component { title: '操作', width: 150, dataIndex: 'actions', - render: (text, record) => ( + render: (text, { id }) => ( - this.onOpen(this.editForm, record)}>编辑 + this.onOpen(this.editForm, id)}>编辑 this.onDelete(record)} + onConfirm={() => this.onDelete(id)} > 删除 @@ -145,12 +145,10 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 * @param {*} modal - * @param {*} record + * @param {*} id */ - onOpen(modal, record) { - modal.current.open({ - record, - }) + onOpen(modal, id) { + modal.current.open({ id }) } /** @@ -177,10 +175,10 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} id */ - onDelete(record) { - this.onAction(apiAction.delete(record), '删除成功') + onDelete(id) { + this.onAction(apiAction.delete({ id }), '删除成功') } //#region 自定义方法 diff --git a/web-react/src/assets/style/app.less b/web-react/src/assets/style/app.less index f4849cc..1e27ce4 100644 --- a/web-react/src/assets/style/app.less +++ b/web-react/src/assets/style/app.less @@ -35,6 +35,7 @@ @import './lib/tree-layout.less'; @import './lib/authority-view.less'; @import './lib/icon-selector.less'; +@import './lib/color-selector.less'; @import './lib/anchor.less'; @import './lib/disabled.less'; @import './theme/primary.less'; diff --git a/web-react/src/assets/style/lib/authority-view.less b/web-react/src/assets/style/lib/authority-view.less index 7fd6b37..94c2f71 100644 --- a/web-react/src/assets/style/lib/authority-view.less +++ b/web-react/src/assets/style/lib/authority-view.less @@ -9,7 +9,12 @@ width: 150px; } .ant-descriptions { + clear: both; + margin-bottom: @padding-sm; + .ant-descriptions-view { + overflow: visible; + } &:last-child { margin-bottom: 0; } @@ -26,4 +31,23 @@ } } } + .ant-card-grid { + width: 25%; + margin-bottom: @padding-sm; + padding: @padding-xs; + + cursor: pointer; + } + .ant-card { + margin-bottom: 0; + + background-color: transparent; + &-body { + margin: -1px 0 0 -1px; + padding: 0; + } + .ant-card-grid { + margin-bottom: 0; + } + } } diff --git a/web-react/src/assets/style/lib/color-selector.less b/web-react/src/assets/style/lib/color-selector.less new file mode 100644 index 0000000..e030c16 --- /dev/null +++ b/web-react/src/assets/style/lib/color-selector.less @@ -0,0 +1,18 @@ +@import (reference) '../extend.less'; +.ant-select-dropdown { + .chrome-picker { + width: auto !important; + margin: -@padding-xxs 0; + + border-radius: 0 !important; + background: transparent !important; + box-shadow: none !important; + } +} +.color-selector--palette { + width: 32px; + height: 32px; + + border-radius: @border-radius-base; + box-shadow: inset 0 0 0 @border-width-base @border-color-base, inset 0 0 0 3px @white; +} diff --git a/web-react/src/assets/style/lib/form.less b/web-react/src/assets/style/lib/form.less index 771d844..c587a27 100644 --- a/web-react/src/assets/style/lib/form.less +++ b/web-react/src/assets/style/lib/form.less @@ -315,6 +315,13 @@ white-space: normal; } } +.ant-form-vertical { + .ant-form-item-label { + >label { + font-weight: bold; + } + } +} .ant-form-item-required { &::before { content: '' !important; diff --git a/web-react/src/assets/style/lib/list.less b/web-react/src/assets/style/lib/list.less index f8a937f..2dbb2de 100644 --- a/web-react/src/assets/style/lib/list.less +++ b/web-react/src/assets/style/lib/list.less @@ -26,7 +26,7 @@ } } } - >.ant-pagination { + .ant-pagination { margin: @padding-md 0; } .ant-descriptions { @@ -44,6 +44,14 @@ } } } + &--scroll { + position: relative; + + overflow-x: auto; + } + .ant-list-items { + min-width: 1000px; + } .ant-list-item { transition: @animation-duration-slow; transition-property: background, border-bottom-color; @@ -52,4 +60,36 @@ background: linear-gradient(90deg, transparent 10%, @background-color-light 70%, transparent); } } + &-container { + position: relative; + &::before, + &::after { + position: absolute; + top: 0; + bottom: 0; + z-index: 3; + + width: 30px; + + content: ''; + transition: box-shadow @animation-duration-slow; + pointer-events: none; + } + &::before { + left: 0; + } + &::after { + right: 0; + } + &.yo-list--ping-left { + &::before { + box-shadow: inset 10px 0 8px -8px fade(@black, 15%); + } + } + &.yo-list--ping-right { + &::after { + box-shadow: inset -10px 0 8px -8px fade(@black, 15%); + } + } + } } diff --git a/web-react/src/assets/style/lib/table.less b/web-react/src/assets/style/lib/table.less index 0e748c7..6ed273f 100644 --- a/web-react/src/assets/style/lib/table.less +++ b/web-react/src/assets/style/lib/table.less @@ -1,5 +1,4 @@ @import (reference) '../extend.less'; - .yo-query-bar { margin-bottom: @padding-xs; .ant-form-inline { @@ -8,220 +7,234 @@ } } } - .yo-action-bar { - display: flex; - justify-content: space-between; + display: flex; + justify-content: space-between; - margin-bottom: @padding-md; - - &--actions { - > .ant-btn, - > .ant-btn-group { - + .ant-btn, - + .ant-btn-group { - margin-left: @padding-xs; - } - } + margin-bottom: @padding-md; + &--actions { + >.ant-btn, + >.ant-btn-group { + +.ant-btn, + +.ant-btn-group { + margin-left: @padding-xs; + } } + } +} +.ant-table { + .ant-table-container { + &::before, + &::after { + z-index: 3; + } + } } - .ant-table-thead { - th.ant-table-column-has-sorters { - &:hover { - background-color: darken(@background-color-base, 5%); - } + th.ant-table-column-has-sorters { + &:hover { + background-color: darken(@background-color-base, 5%); } + } } - .ant-table-tbody { - > tr { - > td { - transition-property: background, border-bottom-color; - } + >tr { + >td { + transition-property: background, border-bottom-color; } - - > tr.ant-table-row:hover { - > td { - border-bottom-color: lighten(@primary-color, 30%); - } + } + >tr.ant-table-row:hover { + >td { + border-bottom-color: lighten(@primary-color, 30%); } + } } - .ant-table-small { - > .ant-table-content { - > .ant-table-body { - margin: 0; - - > table { - > .ant-table-thead { - > tr { - > th { - background-color: @table-selected-row-bg; - } - } - } + >.ant-table-content { + >.ant-table-body { + margin: 0; + >table { + >.ant-table-thead { + >tr { + >th { + background-color: @table-selected-row-bg; } + } } + } } + } } - .ant-table-thead { - > tr { - > th { - font-weight: bold; - } + >tr { + >th { + font-weight: bold; } + } } - .ant-table-sticky-scroll { display: none; } - .yo-table { - .ant-table { - margin: 0 !important; + .ant-table { + margin: 0 !important; + } + .border-right-none { + border-right-width: 0 !important; + &:last-child { + border-right-width: 1px !important; } - - .border-right-none() { - border-right-width: 0 !important; - - &:last-child { - border-right-width: 1px !important; + } + .ant-table-content { + .ant-table-body { + overflow-x: auto !important; + >table { + >.ant-table-thead { + >tr { + >th { + .border-right-none(); + } + } } + >.ant-table-tbody { + >tr { + >td { + .border-right-none(); + } + } + } + } } - - .ant-table-content { - .ant-table-body { - overflow-x: auto !important; - - > table { - > .ant-table-thead { - > tr { - > th { - .border-right-none(); - } - } - } - - > .ant-table-tbody { - > tr { - > td { - .border-right-none(); - } - } - } - } + .ant-table-fixed-left { + .ant-table-thead { + >tr { + >th { + border-right-width: 0 !important; + } } - - .ant-table-fixed-left { - .ant-table-thead { - > tr { - > th { - border-right-width: 0 !important; - } - } - } - - .ant-table-tbody { - > tr { - > td { - border-right-width: 0 !important; - } - } - } - } - - .ant-table-fixed-right { - .ant-table-fixed { - border-left-width: 0 !important; - } - - .ant-table-thead { - > tr { - > th { - .border-right-none(); - } - } - } - - .ant-table-tbody { - > tr { - > td { - .border-right-none(); - } - } - } + } + .ant-table-tbody { + >tr { + >td { + border-right-width: 0 !important; + } } + } } - + .ant-table-fixed-right { + .ant-table-fixed { + border-left-width: 0 !important; + } + .ant-table-thead { + >tr { + >th { + .border-right-none(); + } + } + } + .ant-table-tbody { + >tr { + >td { + .border-right-none(); + } + } + } + } + } .ant-table-bordered { >.ant-table-container { border-top: @border-width-base @border-style-base @table-border-color; } } - &--row-no { + width: 30px !important; + background-color: @table-header-bg; } } - .yo-table-actions { - display: inline-block; + display: inline-block; - vertical-align: middle; + vertical-align: middle; + &--inner { + display: flex; + align-items: center; - &--inner { - display: flex; - align-items: center; - - height: 18px; - } + height: 18px; + } } - .yo-table--column-setting { - width: 240px; + width: 240px; + .ant-dropdown-menu-item { + display: flex; + align-items: center; + justify-content: space-between; + } + .anticon-pushpin { + transition: @animation-duration-slow; + transform: rotate(45deg); - .ant-dropdown-menu-item { - display: flex; - justify-content: space-between; - align-items: center; - } - - .anticon-pushpin { - color: darken(@white, 40%); - transition: @animation-duration-slow; - transform: rotate(45deg); - } - - .yo-table--fixed { - transform: rotate(-45deg); - } + color: darken(@white, 40%); + } + .yo-table--fixed { + transform: rotate(-45deg); + } } +.yo-menu-table { + .ant-table { + .ant-table-expand-icon-col { + width: 28px; + } + .ant-table-row-expand-icon-cell { + z-index: 1; -.yo-inner-table { - .ant-table { - .ant-table-tbody { - > .ant-table-expanded-row-level-1 > td { - padding: 0; - border-right: none !important; - .ant-table-wrapper { - border: none; - .ant-table { - margin: 0 !important; - } - .ant-table-container { - border: none; - .ant-table-cell:first-child { - padding-left: 25px; - } - .ant-table-expanded-row-level-1 > td { - border-right: @border-width-base @border-style-base @table-border-color !important; - padding: @padding-sm @padding-xs @padding-sm @padding-xl; - } - } - } + padding-right: 0 !important; + + border-right: none !important; + +.ant-table-cell { + padding-left: 0; + } + } + .ant-table-tbody { + >.ant-table-expanded-row-level-1>td { + padding: 0; + + border-right: none !important; + .ant-table-wrapper { + border: none; + .ant-table { + margin: 0 !important; + } + .ant-table-container { + border: none; + .ant-table-row-expand-icon-cell { + .ant-table-row-expand-icon { + left: @padding-md; + } + +.ant-table-cell { + padding-left: @padding-md; + } } + .ant-table-expanded-row-level-1>td { + padding: @padding-sm @padding-xs @padding-sm @padding-xl; + + border-right: @border-width-base @border-style-base @table-border-color !important; + .ant-card { + max-width: fit-content; + margin-bottom: 0; + + background: none; + .ant-card-grid { + width: 300px; + padding: @padding-xs @padding-sm; + + background-color: @white; + } + } + } + } } + } } + } } diff --git a/web-react/src/assets/style/lib/tree-layout.less b/web-react/src/assets/style/lib/tree-layout.less index 4b1cc97..a54090a 100644 --- a/web-react/src/assets/style/lib/tree-layout.less +++ b/web-react/src/assets/style/lib/tree-layout.less @@ -19,6 +19,20 @@ } } } + &--collapsed { + position: absolute; + top: 0; + left: 0; + bottom: 0; + z-index: 4; + + transform: translateX(-100%); + &.open { + transform: translateX(0); + + box-shadow: 2px 0 8px fade(@black , 20%); + } + } &--bar { line-height: 20px; diff --git a/web-react/src/assets/style/lib/visibility.less b/web-react/src/assets/style/lib/visibility.less index 85c9124..3e16f62 100644 --- a/web-react/src/assets/style/lib/visibility.less +++ b/web-react/src/assets/style/lib/visibility.less @@ -20,3 +20,27 @@ .flex { display: flex; } +.ellipsis { + overflow: hidden; + + white-space: nowrap; + text-overflow: ellipsis; +} +.ellipsis-2 { + display: -webkit-box; + overflow: hidden; + -webkit-box-orient: vertical; + + text-overflow: ellipsis; + + -webkit-line-clamp: 2; +} +.ellipsis-3 { + display: -webkit-box; + overflow: hidden; + -webkit-box-orient: vertical; + + text-overflow: ellipsis; + + -webkit-line-clamp: 3; +} diff --git a/web-react/src/assets/style/main.less b/web-react/src/assets/style/main.less index 7cf67c9..3abb0d5 100644 --- a/web-react/src/assets/style/main.less +++ b/web-react/src/assets/style/main.less @@ -300,6 +300,14 @@ opacity: 0; } + >iframe { + display: block; + + width: 100%; + height: 100%; + + border: 0; + } } } } diff --git a/web-react/src/assets/style/public.less b/web-react/src/assets/style/public.less index d935b12..7714511 100644 --- a/web-react/src/assets/style/public.less +++ b/web-react/src/assets/style/public.less @@ -33,3 +33,12 @@ background: url('~assets/image/adorn/house-top-01.png') no-repeat bottom right; } } +a.link-gray { + color: fade(@black, 50%); + &:hover { + color: @link-hover-color; + } + &:active { + color: @link-active-color; + } +} diff --git a/web-react/src/common/api/requests/business/houseSafety/houseInfo.js b/web-react/src/common/api/requests/business/houseSafety/houseInfo.js index 7d6a58e..0177ae8 100644 --- a/web-react/src/common/api/requests/business/houseSafety/houseInfo.js +++ b/web-react/src/common/api/requests/business/houseSafety/houseInfo.js @@ -1,6 +1,7 @@ const urls = { houseInfoGetByTaskId: ['/houseInfo/getByTaskId', 'get'], - houseInfoSave: ['houseInfo/save', 'post'] + houseInfoSave: ['houseInfo/save', 'post'], + houseInfoSubmitToCheck: ['/houseInfo/submitToCheck', 'post'] } export default urls \ No newline at end of file diff --git a/web-react/src/common/api/requests/business/houseSafety/houseMember.js b/web-react/src/common/api/requests/business/houseSafety/houseMember.js index bf8c24f..74654c3 100644 --- a/web-react/src/common/api/requests/business/houseSafety/houseMember.js +++ b/web-react/src/common/api/requests/business/houseSafety/houseMember.js @@ -3,6 +3,7 @@ const urls = { houseMemberAdd: ['/houseMember/add', 'post'], houseMemberEdit: ['/houseMember/edit', 'post'], houseMemberDelete: ['/houseMember/delete', 'post'], + houseMemberDetail: ['/houseMember/detail', 'detail'], houseMemberOwnRole: ['/houseMember/ownRole', 'get'], houseMemberOwnData: ['/houseMember/ownData', 'get'], houseMemberGrantData: ['/houseMember/grantData', 'post'], diff --git a/web-react/src/common/api/requests/business/houseSafety/houseQuery.js b/web-react/src/common/api/requests/business/houseSafety/houseQuery.js new file mode 100644 index 0000000..6357a36 --- /dev/null +++ b/web-react/src/common/api/requests/business/houseSafety/houseQuery.js @@ -0,0 +1,5 @@ +const urls = { + houseQueryPage: ['/houseQuery/page', 'post'], +} + +export default urls \ No newline at end of file diff --git a/web-react/src/common/api/requests/business/houseSafety/index.js b/web-react/src/common/api/requests/business/houseSafety/index.js index d8e22cb..6732039 100644 --- a/web-react/src/common/api/requests/business/houseSafety/index.js +++ b/web-react/src/common/api/requests/business/houseSafety/index.js @@ -5,6 +5,7 @@ import houseMember from './houseMember' import houseSelector from './houseSelector' import houseTask from './houseTask' import houseInfo from './houseInfo' +import houseQuery from './houseQuery' const urls = { ...houseProjectInfo, @@ -13,7 +14,8 @@ const urls = { ...houseMember, ...houseSelector, ...houseTask, - ...houseInfo + ...houseInfo, + ...houseQuery } export default urls \ No newline at end of file diff --git a/web-react/src/common/api/requests/sys/appManage.js b/web-react/src/common/api/requests/sys/appManage.js index 06447d5..0596537 100644 --- a/web-react/src/common/api/requests/sys/appManage.js +++ b/web-react/src/common/api/requests/sys/appManage.js @@ -28,6 +28,7 @@ const urls = { * 修改应用状态 */ sysAppChangeStatus: ['/sysApp/changeStatus', 'post'], + sysAppDetail: ['/sysApp/detail', 'get'] } export default urls \ No newline at end of file diff --git a/web-react/src/components/authority-view/index.jsx b/web-react/src/components/authority-view/index.jsx index adabbe9..58cf93d 100644 --- a/web-react/src/components/authority-view/index.jsx +++ b/web-react/src/components/authority-view/index.jsx @@ -1,5 +1,5 @@ import React, { Component } from 'react' -import { Checkbox, Descriptions, Empty, Spin, Tooltip } from 'antd' +import { Card, Checkbox, Descriptions, Empty, Popover, Spin, Tooltip } from 'antd' import { AntIcon } from 'components' import { EMPTY_ID } from 'util/global' @@ -17,11 +17,11 @@ function getVisible() { const caseChildren = checked.filter(item => item.visibleParent || item.type != 2) const visibleParents = [] // 递归寻找父级 - const findVisibleParents = (children) => { + const findVisibleParents = children => { const parents = [] children.forEach(item => { if (item.parentId) { - const parent = this.list.find(item => item.id === item.parentId) + const parent = this.list.find(p => p.id === item.parentId) if (parent) { parents.push(parent) visibleParents.push(parent) @@ -50,24 +50,28 @@ function getVisible() { function renderDescriptions(data) { return data.map(item => { - return item.children && item.children.length ? renderItem.call(this, item) : renderCheckbox.call(this, item) + return item.children && item.children.length + ? renderItem.call(this, item) + : renderCheckbox.call(this, item) }) } function renderItem(data) { return ( - + this.onChange(e, data)} - >{data.title} + onChange={e => this.onChange(e, data)} + > + {data.title} + } > - {renderDescriptions.call(this, data.children)} + {renderDescriptions.call(this, data.children)} ) @@ -75,27 +79,33 @@ function renderItem(data) { function renderCheckbox(data) { return ( -
- this.onChange(e, data)} - >{data.title} - { - data.visibleParent && data.type == 2 && - - - - } -
+ ) } export default class AuthorityView extends Component { - state = { loading: false, - dataSource: [] + dataSource: [], } list = [] @@ -104,7 +114,8 @@ export default class AuthorityView extends Component { super(props) this.autoLoad = typeof this.props.autoLoad === 'boolean' ? this.props.autoLoad : true - this.loadData = typeof this.props.loadData === 'function' ? this.props.loadData : async () => { } + this.loadData = + typeof this.props.loadData === 'function' ? this.props.loadData : async () => {} } /** @@ -126,7 +137,10 @@ export default class AuthorityView extends Component { if (this.props.defaultSelectedKeys) { this.list.map(item => { - if (this.props.defaultSelectedKeys.includes(item.id) && (!item.children || !item.children.length)) { + if ( + this.props.defaultSelectedKeys.includes(item.id) && + (!item.children || !item.children.length) + ) { this.onSelect(true, item) } }) @@ -134,8 +148,10 @@ export default class AuthorityView extends Component { this.setState({ dataSource: res, - loading: false + loading: false, }) + + this.onChange() } onReloadData = () => { @@ -143,16 +159,18 @@ export default class AuthorityView extends Component { } onChange = (e, item) => { - this.onSelect(e.target.checked, item) + if (e && item) { + this.onSelect(e.target.checked, item) + } const visible = getVisible.call(this) if (this.props.onSelect) { this.props.onSelect( // 返回所有选中 - this.list.filter(item => item.checked).map(item => item.id), + this.list.filter(p => p.checked).map(p => p.id), // 返回所有选中和半选 - this.list.filter(item => item.checked || item.indeterminate).map(item => item.id), + this.list.filter(p => p.checked || p.indeterminate).map(p => p.id), // 返回所有选中和半选,但是不返回没有子级选中visibleParent的半选 visible ) @@ -170,7 +188,7 @@ export default class AuthorityView extends Component { } this.setState({ - dataSource: this.list.filter(item => item.parentId === EMPTY_ID) + dataSource: this.list.filter(p => p.parentId === EMPTY_ID), }) } @@ -210,31 +228,30 @@ export default class AuthorityView extends Component { return (
}> - { - !this.state.loading ? - - { - this.state.dataSource.map(item => { - return ( - this.onChange(e, item)} - >{item.title} - } + {!this.state.loading ? ( + + {this.state.dataSource.map(item => { + return ( + this.onChange(e, item)} > - {renderDescriptions.call(this, item.children)} - - ) - }) - } - - : - - } + {item.title} + + } + > + {renderDescriptions.call(this, item.children)} + + ) + })} + + ) : ( + + )}
) diff --git a/web-react/src/components/form/color-selector/index.jsx b/web-react/src/components/form/color-selector/index.jsx new file mode 100644 index 0000000..3d67ab9 --- /dev/null +++ b/web-react/src/components/form/color-selector/index.jsx @@ -0,0 +1,55 @@ +import React, { Component } from 'react' +import { Col, Row, Select } from 'antd' +import { ChromePicker } from 'react-color' + +export default class index extends Component { + select = React.createRef() + + state = { + color: null, + } + + onChange(color) { + this.setState({ color: color.hex }) + } + + onChangeComplete(color) { + this.props.onChange && this.props.onChange(color.hex) + } + + render() { + const { color } = this.state + + const { value } = this.props + + return ( + + + + {this.state.options.roleData.map(item => { + return ( + + {item.name} + + ) + })} + + + + + + + + + {this.props.mode == 'add' && ( + <> + + + + + + + + )} + + + + + + + + + + + 保密 + + + + + + + + + + + + + + + + + + + + + + + ) } } diff --git a/web-react/src/pages/business/house/member/index.jsx b/web-react/src/pages/business/house/member/index.jsx index 1b67dae..e5b64d0 100644 --- a/web-react/src/pages/business/house/member/index.jsx +++ b/web-react/src/pages/business/house/member/index.jsx @@ -13,13 +13,23 @@ import { Switch, Tag, } from 'antd' -import { AntIcon, Auth, Container, Image, ModalForm, QueryList, QueryTreeLayout } from 'components' +import { + AntIcon, + Auth, + Container, + Image, + ModalForm, + QueryList, + QueryTableActions, + QueryTreeLayout, +} from 'components' import { api } from 'common/api' import { toCamelCase } from 'util/format' import { isEqual } from 'lodash' import getDictData from 'util/dic' import FormBody from './form' import Selector from './selector' +import DataForm from './data' // 配置页面所需接口函数 const apiAction = { @@ -31,10 +41,12 @@ const apiAction = { changeStatus: api.houseMemberChangeStatus, resetPwd: api.sysUserResetPwd, + + grantData: api.houseMemberGrantData, } // 用于弹窗标题 -const name = '用户' +const name = '人员' export default class index extends Component { state = { @@ -52,6 +64,7 @@ export default class index extends Component { // 编辑窗口实例 editForm = React.createRef() + dataForm = React.createRef() // 树选中节点 selectId = undefined @@ -145,12 +158,12 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 * @param {*} modal - * @param {*} record + * @param {*} id */ - onOpen(modal, record) { + onOpen(modal, id) { modal.current.open({ orgId: this.selectId, - record, + id, }) } @@ -173,33 +186,52 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} id */ - onDelete(record) { - this.onAction(apiAction.delete(record), '删除成功') + onDelete(id) { + this.onAction(apiAction.delete({ id }), '删除成功') } //#region 自定义方法 renderItem(record) { + const { + id, + account, + name, + nickName, + avatar, + sex, + phone, + email, + status, + roleCode, + roleName, + orgName, + } = record return ( - this.onOpen(this.editForm, record)}>编辑 -
, - - this.onDelete(record)} - > - 删除 - - , - - this.onResetPassword(record)}>重置密码 - , + + + this.onOpen(this.editForm, id)}>编辑 + + + this.onDelete(id)} + > + 删除 + + + + this.onResetPassword(id)}>重置密码 + + + this.onOpen(this.dataForm, id)}>授权额外数据 + + , ]} > } /> - {record.roleCode && record.roleCode.includes('house_security_manager') && ( + {roleCode && roleCode.includes('house_security_manager') && ( @@ -226,9 +258,9 @@ export default class index extends Component { } title={ <> - {record.nickName || record.name} - {record.roleName && - record.roleName.split(',').map((item, i) => ( + {nickName || name} + {roleName && + roleName.split(',').map((item, i) => ( {item} @@ -236,25 +268,24 @@ export default class index extends Component { ))} } - description={record.account} + description={account} /> - {record.orgName} + {orgName} - {this.bindCodeValue(record.sex, 'sex')} + {this.bindCodeValue(sex, 'sex')} - {record.phone || '未设置'} - {record.email || '未设置'} + {phone || '未设置'} + {email || '未设置'}
this.onSetUserStatus(record, checked)} + onChange={checked => this.onSetUserStatus(id, checked)} />
@@ -263,18 +294,18 @@ export default class index extends Component { ) } - onSetUserStatus(record, checked) { + onSetUserStatus(id, checked) { this.onAction( apiAction.changeStatus({ - id: record.id, + id, status: +!checked, }), '设置成功' ) } - onResetPassword(record) { - this.onAction(apiAction.resetPwd(record), '重置成功') + onResetPassword(id) { + this.onAction(apiAction.resetPwd({ id }), '重置成功') } //#endregion @@ -349,6 +380,15 @@ export default class index extends Component { + this.list.current.onReloadData()} + > + + + 0 && areaCode[areaCode.length - 1].length == 12 ? areaCode : [], } //#endregion @@ -213,6 +214,7 @@ export default class form extends Component { rules={[{ required: true, message: '请选择所属区域' }]} > { + if (values.hasOwnProperty('type')) { + this.setState({ type: values.type }) + } + }} query={ + + + 全部 + + 住宅 + + + 非住宅 + + + diff --git a/web-react/src/pages/business/house/query/index.jsx b/web-react/src/pages/business/house/query/index.jsx new file mode 100644 index 0000000..0c03ce9 --- /dev/null +++ b/web-react/src/pages/business/house/query/index.jsx @@ -0,0 +1,538 @@ +import React, { Component } from 'react' +import { + Button, + Card, + Checkbox, + Col, + DatePicker, + Form, + Input, + InputNumber, + message as Message, + Row, + Tag, +} from 'antd' +import { AntIcon, Auth, Container, InputNumberRange, QueryTable } from 'components' +import { api } from 'common/api' +import auth from 'components/authorized/handler' +import { first, isEqual, last } from 'lodash' +import getDictData from 'util/dic' +import { toCamelCase } from 'util/format' +import { getSearchDateRange, getSearchInfo, QueryType } from 'util/query' + +/** + * 注释段[\/**\/]为必须要改 + */ + +/** + * 配置页面所需接口函数 + */ +const apiAction = { + page: api.houseQueryPage, +} + +/** + * 用于弹窗标题 + * [必要] + */ +const name = '/**/' + +/** + * 统一配置权限标识 + * [必要] + */ +const authName = 'houseQuery' + +export default class index extends Component { + state = { + codes: { + houseUsedStatus: [], + housePropertyRights: [], + landAttribute: [], + houseBaseInfo: [], + houseStructureType: [], + houseStorageOfDrawings: [], + houseGrade: [], + }, + + showDrawingMaterialText: false, + } + + // 表格实例 + table = React.createRef() + + // 新增窗口实例 + addForm = React.createRef() + // 编辑窗口实例 + editForm = React.createRef() + + columns = [ + { + title: '房屋编码', + dataIndex: 'houseCode', + sorter: true, + width: 300, + render: (text, record) => ( + <> + {`${record.areaName}-${record.roadName}-${record.commName}-${ + record.note + }-${record.no.toString().padStart(3, '0')}`} +
+ {text} + + ), + }, + { + title: '房屋性质及行业', + dataIndex: 'type', + sorter: true, + width: 150, + render: text => this.bindCodeValue(text, 'house_type'), + }, + { + title: '地址', + dataIndex: 'address', + sorter: true, + }, + { + title: '任务截止时间', + dataIndex: 'endTime', + sorter: true, + width: 150, + }, + ] + + /** + * 构造函数,在渲染前动态添加操作字段等 + * @param {*} props + */ + constructor(props) { + super(props) + + const flag = auth({ [authName]: [['edit'], ['delete']] }) + } + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * 加载字典数据,之后开始加载表格数据 + * 如果必须要加载字典数据,可直接对表格设置autoLoad=true + */ + componentDidMount() { + const { onLoading, onLoadData } = this.table.current + onLoading() + getDictData( + 'house_used_status', + 'house_property_rights', + 'land_attribute', + 'house_base_info', + 'house_structure_type', + 'house_storage_of_drawings', + 'house_grade' + ).then(codes => { + this.setState({ codes }, () => { + onLoadData() + }) + }) + } + + /** + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ + loadData = async (params, query) => { + query.completedDate = getSearchDateRange(query.completedDate) + query.createdTime = getSearchDateRange(query.createdTime) + + const searchInfo = getSearchInfo({ + query, + queryType: { + areaCode: QueryType.Like, + completedDate: [QueryType.GreaterThanOrEqual, QueryType.LessThan], + createdTime: [QueryType.GreaterThanOrEqual, QueryType.LessThan], + totalArea: [QueryType.GreaterThanOrEqual, QueryType.LessThanOrEqual], + totalFloor: [QueryType.GreaterThanOrEqual, QueryType.LessThanOrEqual], + }, + }) + + const { data } = await apiAction.page({ + ...params, + searchInfo, + }) + return data + } + + /** + * 绑定字典数据 + * @param {*} code + * @param {*} name + * @returns + */ + bindCodeValue(code, name) { + name = toCamelCase(name) + const codes = this.state.codes[name] + if (codes) { + const c = codes.find(p => p.code == code) + if (c) { + return c.value + } + } + return null + } + + /** + * 打开新增/编辑弹窗 + * @param {*} modal + * @param {*} id + */ + onOpen(modal, id) { + modal.current.open({ id }) + } + + /** + * 对表格上的操作进行统一处理 + * [异步] + * @param {*} action + * @param {*} successMessage + */ + async onAction(action, successMessage) { + const { onLoading, onLoaded, onReloadData } = this.table.current + onLoading() + try { + if (action) { + await action + } + if (successMessage) { + Message.success(successMessage) + } + onReloadData() + } catch { + onLoaded() + } + } + + /** + * 删除 + * @param {*} id + */ + onDelete(id) { + this.onAction(apiAction.delete({ id }), '删除成功') + } + + //#region 自定义方法 + rednerMoreQuery() { + const { codes, showDrawingMaterialText } = this.state + + return ( + + + + + {codes.houseUsedStatus.map(item => ( + + {item.value} + + ))} + + + + + + + 全部 + + + + + + + + + 全部 + + + + + + + + + 全部 + + + + + + + + + 全部 + + + + + + + + + + + + + + + + + + + 全部 + {codes.housePropertyRights.map(item => ( + + {item.value} + + ))} + + + + + + + 全部 + {codes.landAttribute.map(item => ( + + {item.value} + + ))} + + + + + + + 全部 + {codes.houseBaseInfo.map(item => ( + + {item.value} + + ))} + + + + + + + 全部 + {codes.houseStructureType.map(item => ( + + {item.value} + + ))} + + + + + + + + {codes.houseStorageOfDrawings.map(item => ( + + {item.value} + + ))} + + + {showDrawingMaterialText && ( + + + + )} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 全部 + {codes.houseGrade.map(item => ( + + {item.value} + + ))} + + + + + ) + } + + onQueryChange(changedValues, allValues) { + console.log(changedValues) + if (changedValues.hasOwnProperty('drawingMaterial') && changedValues.drawingMaterial) { + this.setState({ + showDrawingMaterialText: changedValues.drawingMaterial.includes('100'), + }) + return + } + + // 全部 + const { codes } = this.state + const key = first(Object.keys(changedValues)) + const mapCount = { + curtainWall: 2, + faceBrick: 2, + coating: 2, + painting: 2, + propertyRights: codes.housePropertyRights.length, + landAttribute: codes.landAttribute.length, + baseInfo: codes.houseBaseInfo.length, + structureType: codes.houseStructureType.length, + houseGrade: codes.houseGrade.length, + } + if (Object.keys(mapCount).includes(key)) { + return { + [key]: this.checkedNone(changedValues[key], mapCount[key]), + } + } + } + + checkedNone(value, count) { + if (first(value) == '' && value.length > 1) { + // 在'无'之后选中其他值 + value.shift() + } else if ((last(value) == '' && value.length > 1) || value.length === count) { + // 在其他值之后选中'无' + value = [''] + } + return value + } + //#endregion + + render() { + const { codes, type } = this.state + + return ( + +
+ + + + + + + + +
+ } + moreQuery={ + {this.rednerMoreQuery()} + } + onQueryChange={(changedValues, allValues) => + this.onQueryChange(changedValues, allValues) + } + operator={ + + + + } + /> + + + ) + } +} diff --git a/web-react/src/pages/business/house/task/check/index.jsx b/web-react/src/pages/business/house/task/check/index.jsx new file mode 100644 index 0000000..aef209a --- /dev/null +++ b/web-react/src/pages/business/house/task/check/index.jsx @@ -0,0 +1,304 @@ +import React, { Component } from 'react' +import { Button, Card, Form, Input, message as Message, Popconfirm, Radio, Select, Tag } from 'antd' +import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components' +import { api } from 'common/api' +import auth from 'components/authorized/handler' +import { isEqual } from 'lodash' +import getDictData from 'util/dic' +import { toCamelCase } from 'util/format' +import { getSearchInfo } from 'util/query' + +/** + * 注释段[\/**\/]为必须要改 + */ + +/** + * 配置页面所需接口函数 + */ +const apiAction = { + page: api.houseTaskPage, +} + +/** + * 统一配置权限标识 + * [必要] + */ +const authName = 'houseTask' + +export default class index extends Component { + state = { + codes: { + status: [ + { code: 3, value: '审核中' }, + { code: 6, value: '审核通过' }, + ], + houseType: [], + houseIndustry: [], + }, + + type: '', + } + + // 表格实例 + table = React.createRef() + + // 新增窗口实例 + addForm = React.createRef() + // 编辑窗口实例 + editForm = React.createRef() + + columns = [ + { + title: '房屋编码', + dataIndex: 'houseCode', + sorter: true, + width: 300, + render: (text, record) => ( + <> + {`${record.areaName}-${record.roadName}-${record.commName}-${ + record.note + }-${record.no.toString().padStart(3, '0')}`} +
+ {text} + + ), + }, + { + title: '房屋性质及行业', + dataIndex: 'type', + sorter: true, + width: 150, + render: (text, record) => + this.bindCodeValue(text, 'house_type') + + (text === 2 ? `(${this.bindCodeValue(record.industry, 'house_industry')})` : ''), + }, + { + title: '地址', + dataIndex: 'address', + sorter: true, + }, + { + title: '任务截止时间', + dataIndex: 'endTime', + sorter: true, + width: 150, + }, + { + title: '审核状态', + dataIndex: 'status', + sorter: true, + width: 100, + render: text => this.bindCodeValue(text, 'status'), + }, + ] + + /** + * 构造函数,在渲染前动态添加操作字段等 + * @param {*} props + */ + constructor(props) { + super(props) + + const flag = auth({ houseInfo: 'getByTaskId' }) + + if (flag) { + this.columns.push({ + title: '操作', + width: 150, + dataIndex: 'actions', + render: (text, record) => ( + + + this.onOpen(record.id)}> + {record.state === 3 ? `审核` : `查看`} + + + + ), + }) + } + } + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * 加载字典数据,之后开始加载表格数据 + * 如果必须要加载字典数据,可直接对表格设置autoLoad=true + */ + componentDidMount() { + const { onLoading, onLoadData } = this.table.current + onLoading() + getDictData('house_type', 'house_industry').then(codes => { + this.setState({ codes: { ...this.state.codes, ...codes } }, () => { + onLoadData() + }) + }) + } + + /** + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ + loadData = async (params, query) => { + const searchInfo = getSearchInfo({ + query, + queryType: { + type: '=', + industry: '=', + address: 'like', + houseCode: 'like', + status: '=', + }, + }) + + const { data } = await apiAction.page({ + ...params, + searchInfo, + }) + return data + } + + /** + * 绑定字典数据 + * @param {*} code + * @param {*} name + * @returns + */ + bindCodeValue(code, name) { + name = toCamelCase(name) + const codes = this.state.codes[name] + if (codes) { + const c = codes.find(p => p.code == code) + if (c) { + return c.value + } + } + return null + } + + /** + * 打开新增/编辑弹窗 + * @param {*} modal + * @param {*} record + */ + onOpen(taskId) { + window.openContentWindow({ + title: '房屋登记', + path: 'business/house/info/form', + param: { + taskId, + table: this.table, + }, + }) + } + + /** + * 对表格上的操作进行统一处理 + * [异步] + * @param {*} action + * @param {*} successMessage + */ + async onAction(action, successMessage) { + const { onLoading, onLoaded, onReloadData } = this.table.current + onLoading() + try { + if (action) { + await action + } + if (successMessage) { + Message.success(successMessage) + } + onReloadData() + } catch { + onLoaded() + } + } + + //#region 自定义方法 + //#endregion + + render() { + const { codes, type } = this.state + + return ( + +
+ + { + if (values.hasOwnProperty('type')) { + this.setState({ type: values.type }) + } + }} + query={ + + + + 全部 + {codes.houseType.map(item => ( + + {item.value} + + ))} + + + {type == 2 && ( + + + + )} + + + + + + + + + 全部 + {codes.status.map(item => ( + + {item.value} + + ))} + + + + } + /> + +
+ ) + } +} diff --git a/web-react/src/pages/business/house/task/index.jsx b/web-react/src/pages/business/house/task/index.jsx index a386e3c..635eded 100644 --- a/web-react/src/pages/business/house/task/index.jsx +++ b/web-react/src/pages/business/house/task/index.jsx @@ -1,11 +1,12 @@ import React, { Component } from 'react' -import { Button, Card, Form, Input, message as Message, Popconfirm, Radio, Select, Tag } from 'antd' -import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components' +import { Card, Form, Input, message as Message, Radio, Select, Tag } from 'antd' +import { Auth, Container, QueryTable, QueryTableActions } from 'components' import { api } from 'common/api' import auth from 'components/authorized/handler' import { isEqual } from 'lodash' import getDictData from 'util/dic' import { toCamelCase } from 'util/format' +import { getSearchInfo } from 'util/query' /** * 注释段[\/**\/]为必须要改 @@ -27,6 +28,14 @@ const authName = 'houseTask' export default class index extends Component { state = { codes: { + status: [ + { code: -1, value: '审核退回' }, + { code: 0, value: '待处理' }, + { code: 1, value: '暂存' }, + { code: 2, value: '待提交' }, + { code: 3, value: '审核中' }, + { code: 6, value: '审核通过' }, + ], houseType: [], houseIndustry: [], }, @@ -63,7 +72,9 @@ export default class index extends Component { dataIndex: 'type', sorter: true, width: 150, - render: text => this.bindCodeValue(text, 'house_type'), + render: (text, record) => + this.bindCodeValue(text, 'house_type') + + (text === 2 ? `(${this.bindCodeValue(record.industry, 'house_industry')})` : ''), }, { title: '地址', @@ -76,6 +87,13 @@ export default class index extends Component { sorter: true, width: 150, }, + { + title: '建档状态', + dataIndex: 'state', + sorter: true, + width: 100, + render: text => this.bindCodeValue(text, 'status'), + }, ] /** @@ -95,7 +113,13 @@ export default class index extends Component { render: (text, record) => ( - this.onOpen(record.id)}>登记 + this.onOpen(record.id)}> + {record.state === -1 || record.state === 1 || record.state === 2 + ? `修改` + : record.state === 3 || record.state === 6 + ? `查看` + : `登记`} + ), @@ -123,7 +147,7 @@ export default class index extends Component { const { onLoading, onLoadData } = this.table.current onLoading() getDictData('house_type', 'house_industry').then(codes => { - this.setState({ codes }, () => { + this.setState({ codes: { ...this.state.codes, ...codes } }, () => { onLoadData() }) }) @@ -137,9 +161,20 @@ export default class index extends Component { * @returns */ loadData = async (params, query) => { + const searchInfo = getSearchInfo({ + query, + queryType: { + type: '=', + industry: '=', + address: 'like', + houseCode: 'like', + state: '=', + }, + }) + const { data } = await apiAction.page({ ...params, - ...query, + searchInfo, }) return data } @@ -173,6 +208,7 @@ export default class index extends Component { path: 'business/house/info/form', param: { taskId, + table: this.table, }, }) } @@ -216,6 +252,7 @@ export default class index extends Component { columns={this.columns} queryInitialValues={{ type: '', + state: 0, }} onQueryChange={values => { if (values.hasOwnProperty('type')) { @@ -255,6 +292,16 @@ export default class index extends Component { + + + } /> diff --git a/web-react/src/pages/system/app/form.jsx b/web-react/src/pages/system/app/form.jsx index 45ec668..f2e0fc4 100644 --- a/web-react/src/pages/system/app/form.jsx +++ b/web-react/src/pages/system/app/form.jsx @@ -1,14 +1,13 @@ import React, { Component } from 'react' import { Form, Input, InputNumber, Spin } from 'antd' -import { AntIcon, IconSelector } from 'components' -import { cloneDeep } from 'lodash' +import { AntIcon, ColorSelector, IconSelector } from 'components' +import { api } from 'common/api' const initialValues = { - sort: 100 + sort: 100, } export default class form extends Component { - state = { // 加载状态 loading: true, @@ -33,17 +32,18 @@ export default class form extends Component { * 填充数据 * 可以在设置this.record之后对其作出数据结构调整 * [异步,必要] - * @param {*} params + * @param {*} params */ async fillData(params) { - - this.record = cloneDeep(params.record) //#region 从后端转换成前段所需格式 + if (params.id) { + this.record = (await api.sysAppDetail({ id: params.id })).data + } //#endregion this.form.current.setFieldsValue(this.record) this.setState({ - loading: false + loading: false, }) } @@ -51,7 +51,7 @@ export default class form extends Component { * 获取数据 * 可以对postData进行数据结构调整 * [异步,必要] - * @returns + * @returns */ async getData() { const form = this.form.current @@ -73,17 +73,21 @@ export default class form extends Component { render() { return ( -
+ }>
- + - + @@ -94,15 +98,17 @@ export default class form extends Component { - this - .iconSelector - .current - .open(this.form.current.getFieldValue('icon')) + this.iconSelector.current.open( + this.form.current.getFieldValue('icon') + ) } /> } /> + + +
- this.form.current.setFieldsValue({ - icon - })} /> + + this.form.current.setFieldsValue({ + icon, + }) + } + /> ) } diff --git a/web-react/src/pages/system/app/index.jsx b/web-react/src/pages/system/app/index.jsx index 458fb4c..4f1e853 100644 --- a/web-react/src/pages/system/app/index.jsx +++ b/web-react/src/pages/system/app/index.jsx @@ -38,6 +38,33 @@ export default class index extends Component { // 表格字段 columns = [ + { + title: '图标', + dataIndex: 'icon', + width: 32, + render: (text, record) => ( +
+ +
+ ), + }, { title: '应用名称', dataIndex: 'name', @@ -87,6 +114,7 @@ export default class index extends Component { dataIndex: 'sort', width: 100, sorter: true, + defaultSortOrder: 'ascend', }, ] @@ -107,7 +135,7 @@ export default class index extends Component { render: (text, record) => ( - this.onOpen(this.editForm, record)}>编辑 + this.onOpen(this.editForm, record.id)}>编辑 <>{this.bindCodeValue(text, 'areacode_type')}, }, { @@ -60,14 +60,15 @@ export default class index extends Component { { title: '区域编号', dataIndex: 'code', - width: 80, + width: 100, sorter: true, }, { title: '行政编号', dataIndex: 'adCode', - width: 80, + width: 100, sorter: true, + defaultSortOrder: 'ascend', }, { title: '描述', diff --git a/web-react/src/pages/system/config/index.jsx b/web-react/src/pages/system/config/index.jsx index 9668b4f..53a0b7a 100644 --- a/web-react/src/pages/system/config/index.jsx +++ b/web-react/src/pages/system/config/index.jsx @@ -1,5 +1,5 @@ import React, { Component } from 'react' -import { Button, Card, Form, Input, message as Message, Popconfirm } from 'antd' +import { Button, Card, Form, Input, message as Message, Popconfirm, Tooltip } from 'antd' import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components' import { api } from 'common/api' import auth from 'components/authorized/handler' @@ -41,28 +41,41 @@ export default class index extends Component { { title: '参数名称', dataIndex: 'name', + width: 200, sorter: true, }, { title: '唯一编码', dataIndex: 'code', + width: 200, sorter: true, + ellipsis: { + showTitle: false, + }, + render: text => {text}, }, { title: '参数值', dataIndex: 'value', + width: 200, sorter: true, }, { title: '所属分类', dataIndex: 'groupCode', + width: 140, sorter: true, render: text => this.bindCodeValue(text, 'consts_type'), }, { title: '备注', dataIndex: 'remark', + width: 400, sorter: true, + ellipsis: { + showTitle: false, + }, + render: text => {text}, }, ] @@ -213,6 +226,7 @@ export default class index extends Component { autoLoad={false} loadData={this.loadData} columns={this.columns} + scroll={{ x: 1140 }} query={ diff --git a/web-react/src/pages/system/dict/dictdata/index.jsx b/web-react/src/pages/system/dict/dictdata/index.jsx index c42c5af..ddc22b1 100644 --- a/web-react/src/pages/system/dict/dictdata/index.jsx +++ b/web-react/src/pages/system/dict/dictdata/index.jsx @@ -14,20 +14,19 @@ const apiAction = { add: api.sysDictDataAdd, edit: api.sysDictDataEdit, delete: api.sysDictDataDelete, - deleteBatch: api.sysDictDataDeleteBatch + deleteBatch: api.sysDictDataDeleteBatch, } // 用于弹窗标题 const name = '字典值' export default class index extends Component { - state = { codes: { - commonStatus: [] + commonStatus: [], }, - selectedRowKeys: [] + selectedRowKeys: [], } // 表格实例 @@ -45,7 +44,7 @@ export default class index extends Component { dataIndex: 'value', sorter: true, width: 200, - render: (text, record, index) => + render: (text, record, index) => ( + ), }, { title: '字典值', dataIndex: 'code', sorter: true, width: 200, - render: (text, record, index) => + render: (text, record, index) => ( + ), }, { title: '扩展值', dataIndex: 'extCode', width: 80, align: 'center', - render: (text, record, index) => + render: (text, record, index) => ( <> - {auth('sysDictData:edit') ? + {auth('sysDictData:edit') ? ( this.onOpen(this.jsonForm, record)} style={{ @@ -87,50 +88,58 @@ export default class index extends Component { transform: 'scaleY(.85)', color: 'transparent', backgroundImage: 'linear-gradient(135deg, #007bff, #52c41a)', - WebkitBackgroundClip: 'text' + WebkitBackgroundClip: 'text', }} - >JSON - : + > + JSON + + ) : ( <>{text} - } + )} + ), }, { title: '排序', dataIndex: 'sort', sorter: true, width: 100, - render: (text, record, index) => - - + render: (text, record, index) => ( + + + + ), + defaultSortOrder: 'ascend', }, { title: '备注', dataIndex: 'remark', sorter: true, - render: (text, record, index) => - - + render: (text, record, index) => ( + + + + ), }, { title: '状态', dataIndex: 'status', sorter: true, width: 80, - render: text => this.bindCodeValue(text, 'common_status') - } + render: text => this.bindCodeValue(text, 'common_status'), + }, ] /** * 构造函数,在渲染前动态添加操作字段等 - * @param {*} props + * @param {*} props */ constructor(props) { super(props) @@ -142,27 +151,28 @@ export default class index extends Component { title: '操作', width: 150, dataIndex: 'actions', - render: (text, record, index) => ( - { - record.id !== -1 ? + render: (text, record, index) => ( + + {record.id !== -1 ? ( this.onEdit(index)}>保存编辑 - : + ) : ( this.onAdd(index)}>保存新增 - } - - this.onDelete(record)} - > - 删除 - - - ) + )} + + this.onDelete(record)} + > + 删除 + + + + ), }) } } @@ -171,9 +181,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -186,26 +196,28 @@ export default class index extends Component { componentDidMount() { this.table.current.onLoading() getDictData('common_status').then(res => { - this.setState({ - codes: res - }, () => { - this.table.current.onLoadData() - }) + this.setState( + { + codes: res, + }, + () => { + this.table.current.onLoadData() + } + ) }) } /** * 调用加载数据接口,可在调用前对query进行处理 * [异步,必要] - * @param {*} params - * @param {*} query - * @returns + * @param {*} params + * @param {*} query + * @returns */ loadData = async (params, query) => { - query = { ...query, - typeId: this.props.type.id + typeId: this.props.type.id, } const { data } = await apiAction.page({ @@ -225,15 +237,15 @@ export default class index extends Component { /** * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns + * @param {*} code + * @param {*} name + * @returns */ bindCodeValue(code, name) { name = toCamelCase(name) const codes = this.state.codes[name] if (codes) { - const c = codes.find((p) => p.code == code) + const c = codes.find(p => p.code == code) if (c) { return c.value } @@ -243,20 +255,20 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record + * @param {*} modal + * @param {*} record */ onOpen(modal, record) { modal.current.open({ - record + record, }) } /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage, reload = true) { const table = this.table.current @@ -276,13 +288,10 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } //#region 自定义方法 @@ -295,12 +304,12 @@ export default class index extends Component { typeId: this.props.type.id, sort: 100, status: 0, - remark: null + remark: null, } const index = this.table.current.onAddRow(record) if (index !== false) { this.form.current.setFieldsValue({ - [index]: record + [index]: record, }) } } @@ -318,10 +327,7 @@ export default class index extends Component { const record = form.getFieldsValue([index])[index] // 为了正常显示checkbox,默认给id赋予了-1,在这里删除id以表示新增 record.id = undefined - this.onAction( - apiAction.add(record), - '新增成功' - ) + this.onAction(apiAction.add(record), '新增成功') } async onEdit(index) { @@ -335,21 +341,14 @@ export default class index extends Component { } } const record = form.getFieldsValue([index])[index] - this.onAction( - apiAction.edit(record), - '编辑成功', - false - ) + this.onAction(apiAction.edit(record), '编辑成功', false) } async onDeleteBatch() { - await this.onAction( - apiAction.deleteBatch(this.state.selectedRowKeys), - '删除成功' - ) + await this.onAction(apiAction.deleteBatch(this.state.selectedRowKeys), '删除成功') this.setState({ - selectedRowKeys: [] + selectedRowKeys: [], }) } @@ -360,8 +359,8 @@ export default class index extends Component { index = dataSource.indexOf(data) this.form.current.setFieldsValue({ [index]: { - extCode - } + extCode, + }, }) dataSource[index].extCode = extCode table.setState({ dataSource }) @@ -369,7 +368,6 @@ export default class index extends Component { //#endregion render() { - const { selectedRowKeys } = this.state return ( @@ -387,9 +385,9 @@ export default class index extends Component { rowSelection={{ selectedRowKeys, onChange: selectedRowKeys => this.setState({ selectedRowKeys }), - getCheckboxProps: (record) => ({ - disabled: record.id === -1 - }) + getCheckboxProps: record => ({ + disabled: record.id === -1, + }), }} query={ @@ -409,28 +407,27 @@ export default class index extends Component { title="是否确认批量删除" onConfirm={() => this.onDeleteBatch()} > - + } - footer={ - () => - - - - } + footer={() => ( + + + + )} /> - + diff --git a/web-react/src/pages/system/dict/index.jsx b/web-react/src/pages/system/dict/index.jsx index 7d6053a..8c46ab5 100644 --- a/web-react/src/pages/system/dict/index.jsx +++ b/web-react/src/pages/system/dict/index.jsx @@ -1,6 +1,14 @@ import React, { Component } from 'react' import { Button, Card, Form, Input, message as Message, Popconfirm, Radio } from 'antd' -import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions, QueryTreeLayout } from 'components' +import { + AntIcon, + Auth, + Container, + ModalForm, + QueryTable, + QueryTableActions, + QueryTreeLayout, +} from 'components' import { api } from 'common/api' import auth from 'components/authorized/handler' import { toCamelCase } from 'util/format' @@ -14,17 +22,16 @@ const apiAction = { page: api.sysDictTypePage, add: api.sysDictTypeAdd, edit: api.sysDictTypeEdit, - delete: api.sysDictTypeDelete + delete: api.sysDictTypeDelete, } const name = '字典' export default class index extends Component { - state = { codes: { - commonStatus: [] - } + commonStatus: [], + }, } // 表格实例 @@ -43,24 +50,29 @@ export default class index extends Component { { title: '字典名称', dataIndex: 'name', + width: 200, sorter: true, }, { title: '类型', key: 'type', dataIndex: 'code', + width: 120, sorter: true, - render: text => text ? '字典类型' : '目录' + render: text => (text ? '字典类型' : '目录'), }, { title: '唯一编码', dataIndex: 'code', + width: 120, sorter: true, }, { title: '排序', dataIndex: 'sort', + width: 80, sorter: true, + defaultSortOrder: 'ascend', }, { title: '备注', @@ -71,14 +83,15 @@ export default class index extends Component { { title: '状态', dataIndex: 'status', + width: 80, sorter: true, - render: text => this.bindCodeValue(text, 'common_status') + render: text => this.bindCodeValue(text, 'common_status'), }, ] /** * 构造函数,在渲染前动态添加操作字段等 - * @param {*} props + * @param {*} props */ constructor(props) { super(props) @@ -90,20 +103,22 @@ export default class index extends Component { title: '操作', width: 150, dataIndex: 'actions', - render: (text, record) => ( - - this.onOpen(this.editForm, record)}>编辑 - - - this.onDelete(record)} - > - 删除 - - - ) + render: (text, record) => ( + + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + + ), }) } } @@ -112,9 +127,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -127,26 +142,28 @@ export default class index extends Component { componentDidMount() { this.table.current.onLoading() getDictData('common_status').then(res => { - this.setState({ - codes: res - }, () => { - this.table.current.onLoadData() - }) + this.setState( + { + codes: res, + }, + () => { + this.table.current.onLoadData() + } + ) }) } /** - * 调用加载数据接口,可在调用前对query进行处理 - * [异步,必要] - * @param {*} params - * @param {*} query - * @returns - */ + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ loadData = async (params, query) => { - query = { ...query, - pid: this.selectId + pid: this.selectId, } const { data } = await apiAction.page({ @@ -157,10 +174,10 @@ export default class index extends Component { } /** - * 调用树结构数据接口 - * [异步,必要] - * @returns - */ + * 调用树结构数据接口 + * [异步,必要] + * @returns + */ loadTreeData = async () => { const { data } = await apiAction.tree() return data @@ -169,7 +186,7 @@ export default class index extends Component { /** * 树节点选中事件 * [必要] - * @param {*} id + * @param {*} id */ onSelectTree(id) { this.selectId = id @@ -178,15 +195,15 @@ export default class index extends Component { /** * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns + * @param {*} code + * @param {*} name + * @returns */ bindCodeValue(code, name) { name = toCamelCase(name) const codes = this.state.codes[name] if (codes) { - const c = codes.find((p) => p.code == code) + const c = codes.find(p => p.code == code) if (c) { return c.value } @@ -196,21 +213,21 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record + * @param {*} modal + * @param {*} record */ onOpen(modal, record) { modal.current.open({ pid: this.selectId, - record + record, }) } /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { this.table.current.onLoading() @@ -225,13 +242,10 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } //#region 自定义方法 @@ -242,7 +256,7 @@ export default class index extends Component { this.onSelectTree(key)} + onSelect={key => this.onSelectTree(key)} > @@ -252,7 +266,7 @@ export default class index extends Component { loadData={this.loadData} columns={this.columns} queryInitialValues={{ - type: 2 + type: 2, }} query={ @@ -275,12 +289,14 @@ export default class index extends Component { + > + 新增{name} + } expandable={{ expandedRowRender: record => , - rowExpandable: record => !!record.code + rowExpandable: record => !!record.code, }} /> diff --git a/web-react/src/pages/system/log/oplog/index.jsx b/web-react/src/pages/system/log/oplog/index.jsx index 52bf8d0..6c18cd7 100644 --- a/web-react/src/pages/system/log/oplog/index.jsx +++ b/web-react/src/pages/system/log/oplog/index.jsx @@ -1,23 +1,42 @@ import React, { Component } from 'react' -import { Alert, Button, Card, Descriptions, Form, Popconfirm, Input, message as Message, Select, DatePicker } from 'antd' +import { + Alert, + Button, + Card, + Descriptions, + Form, + Popconfirm, + Input, + message as Message, + Select, + DatePicker, + Tag, +} from 'antd' import { Auth, Container, QueryTable } from 'components' import { api } from 'common/api' import { toCamelCase } from 'util/format' import { isEqual } from 'lodash' import getDictData from 'util/dic' import moment from 'moment' +import ReactJson from 'react-json-view' -const { RangePicker } = DatePicker; +const { RangePicker } = DatePicker const apiAction = { page: api.sysOpLogPage, - delete: api.sysOpLogDelete + delete: api.sysOpLogDelete, } + +const methodColor = { + POST: 'orange', + GET: 'green', +} + export default class index extends Component { state = { codes: { - opType: [] - } + opType: [], + }, } // 表格实例 table = React.createRef() @@ -26,37 +45,54 @@ export default class index extends Component { { title: '日志名称', dataIndex: 'name', - sorter: true, - }, - { - title: '操作类型', - dataIndex: 'opType', - render: text => (<>{this.bindCodeValue(text, 'op_type')}), - sorter: true, - }, - { - title: '是否成功', - dataIndex: 'success', - render: text => (<> {text ? '是' : '否'}), - sorter: true, - }, - { - title: 'ip', - dataIndex: 'ip', + width: 200, sorter: true, }, { title: '请求地址', dataIndex: 'url', + width: 300, + sorter: true, + render: (text, record) => ( + <> + + {record.reqMethod} + {' '} + {text} + + ), + }, + { + title: '是否成功', + dataIndex: 'success', + width: 100, + render: text => ( + <> + {text ? ( + + ) : ( + + )} + + ), + sorter: true, + }, + { + title: 'ip', + dataIndex: 'ip', + width: 120, sorter: true, }, { title: '操作时间', dataIndex: 'opTime', + width: 140, sorter: true, + defaultSortOrder: 'descend', }, { title: '操作人', + width: 140, dataIndex: 'account', sorter: true, }, @@ -66,9 +102,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -78,29 +114,20 @@ export default class index extends Component { * 加载字典数据,之后开始加载表格数据 * 如果必须要加载字典数据,可直接对表格设置autoLoad=true */ - componentDidMount() { - this.table.current.onLoading() - getDictData('op_type').then(res => { - this.setState({ - codes: res - }, () => { - this.table.current.onLoadData() - }) - }) - } + componentDidMount() {} /** * 调用加载数据接口,可在调用前对query进行处理 * [异步,必要] - * @param {*} params - * @param {*} query - * @returns + * @param {*} params + * @param {*} query + * @returns */ loadData = async (params, query) => { if (query.dates && query.dates.length) { - query.searchBeginTime = moment(query.dates[0]).format('YYYY-MM-DD HH:mm:ss'); - query.searchEndTime = moment(query.dates[1]).format('YYYY-MM-DD HH:mm:ss'); - delete query.dates; + query.searchBeginTime = moment(query.dates[0]).format('YYYY-MM-DD HH:mm:ss') + query.searchEndTime = moment(query.dates[1]).format('YYYY-MM-DD HH:mm:ss') + delete query.dates } const { data } = await apiAction.page({ ...params, @@ -109,16 +136,16 @@ export default class index extends Component { return data } /** - * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns - */ + * 绑定字典数据 + * @param {*} code + * @param {*} name + * @returns + */ bindCodeValue(code, name) { name = toCamelCase(name) const codes = this.state.codes[name] if (codes) { - const c = codes.find((p) => +p.code === code) + const c = codes.find(p => +p.code === code) if (c) { return c.value } @@ -129,8 +156,8 @@ export default class index extends Component { /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { this.table.current.onLoading() @@ -144,27 +171,27 @@ export default class index extends Component { } onOpLogClear() { - this.onAction( - apiAction.delete(), - '清空成功' - ) + this.onAction(apiAction.delete(), '清空成功') } render() { return (
- -
后端bug:任何操作的操作类型都是增加
-
没有记录请求参数.返回结果等信息
- - } /> + +
后端bug:任何操作的操作类型都是增加
+
没有记录请求参数.返回结果等信息
+ + } + />
- - - - + > } @@ -222,8 +231,8 @@ export default class index extends Component { } expandable={{ - expandedRowRender: record => - + expandedRowRender: record => ( + {record.methodName} @@ -240,20 +249,26 @@ export default class index extends Component { {record.className} - {record.result} + - {record.param} + {record.message} + ), }} - />
) } -} \ No newline at end of file +} diff --git a/web-react/src/pages/system/log/vislog/index.jsx b/web-react/src/pages/system/log/vislog/index.jsx index ea0f940..3f5cafa 100644 --- a/web-react/src/pages/system/log/vislog/index.jsx +++ b/web-react/src/pages/system/log/vislog/index.jsx @@ -1,5 +1,16 @@ import React, { Component } from 'react' -import { Alert, Button, Card, Descriptions, Form, Popconfirm, Input, message as Message, Select, DatePicker } from 'antd' +import { + Alert, + Button, + Card, + Descriptions, + Form, + Popconfirm, + Input, + message as Message, + Select, + DatePicker, +} from 'antd' import { Auth, Container, QueryTable } from 'components' import { api } from 'common/api' import { toCamelCase } from 'util/format' @@ -7,17 +18,17 @@ import { isEqual } from 'lodash' import getDictData from 'util/dic' import moment from 'moment' -const { RangePicker } = DatePicker; +const { RangePicker } = DatePicker const apiAction = { page: api.sysVisLogPage, - delete: api.sysVisLogDelete + delete: api.sysVisLogDelete, } export default class index extends Component { state = { codes: { - visType: [] - } + visType: [], + }, } // 表格实例 table = React.createRef() @@ -26,38 +37,54 @@ export default class index extends Component { { title: '日志名称', dataIndex: 'name', + width: 200, sorter: true, }, { title: '访问类型', dataIndex: 'visType', - render: text => (<>{this.bindCodeValue(text, 'vis_type')}), + width: 120, + render: text => <>{this.bindCodeValue(text, 'vis_type')}, sorter: true, }, { title: '是否成功', dataIndex: 'success', - render: text => (<> {text ? '是' : '否'}), + width: 120, + render: text => ( + <> + {text ? ( + + ) : ( + + )} + + ), sorter: true, }, { title: 'ip', dataIndex: 'ip', + width: 180, sorter: true, }, { title: '浏览器', dataIndex: 'browser', + width: 180, sorter: true, }, { title: '访问时间', dataIndex: 'visTime', + width: 180, sorter: true, + defaultSortOrder: 'descend', }, { title: '访问人', dataIndex: 'account', + width: 180, sorter: true, }, ] @@ -66,9 +93,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -81,26 +108,29 @@ export default class index extends Component { componentDidMount() { this.table.current.onLoading() getDictData('vis_type').then(res => { - this.setState({ - codes: res - }, () => { - this.table.current.onLoadData() - }) + this.setState( + { + codes: res, + }, + () => { + this.table.current.onLoadData() + } + ) }) } /** * 调用加载数据接口,可在调用前对query进行处理 * [异步,必要] - * @param {*} params - * @param {*} query - * @returns + * @param {*} params + * @param {*} query + * @returns */ loadData = async (params, query) => { if (query.dates && query.dates.length) { - query.searchBeginTime = moment(query.dates[0]).format('YYYY-MM-DD HH:mm:ss'); - query.searchEndTime = moment(query.dates[1]).format('YYYY-MM-DD HH:mm:ss'); - delete query.dates; + query.searchBeginTime = moment(query.dates[0]).format('YYYY-MM-DD HH:mm:ss') + query.searchEndTime = moment(query.dates[1]).format('YYYY-MM-DD HH:mm:ss') + delete query.dates } const { data } = await apiAction.page({ ...params, @@ -109,16 +139,16 @@ export default class index extends Component { return data } /** - * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns - */ + * 绑定字典数据 + * @param {*} code + * @param {*} name + * @returns + */ bindCodeValue(code, name) { name = toCamelCase(name) const codes = this.state.codes[name] if (codes) { - const c = codes.find((p) => +p.code === code) + const c = codes.find(p => +p.code === code) if (c) { return c.value } @@ -129,8 +159,8 @@ export default class index extends Component { /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { this.table.current.onLoading() @@ -144,17 +174,18 @@ export default class index extends Component { } onVisLogClear() { - this.onAction( - apiAction.delete(), - '清空成功' - ) + this.onAction(apiAction.delete(), '清空成功') } render() { return (
- +
- - + > } @@ -220,16 +245,22 @@ export default class index extends Component { } expandable={{ - expandedRowRender: record => - - + expandedRowRender: record => ( + + {record.message} + ), }} />
) } -} \ No newline at end of file +} diff --git a/web-react/src/pages/system/machine/base.jsx b/web-react/src/pages/system/machine/base.jsx new file mode 100644 index 0000000..9437b73 --- /dev/null +++ b/web-react/src/pages/system/machine/base.jsx @@ -0,0 +1,33 @@ +import React, { Component } from 'react' +import { Card, Col, Descriptions } from 'antd' + +export default class base extends Component { + render() { + const { base } = this.props + + const { hostName, systemOs, wanIp, lanIp, osArchitecture, frameworkDescription } = base + + return ( + <> + + + + {hostName} + {systemOs} + {wanIp} + {lanIp} + {osArchitecture} + + {frameworkDescription} + + + + + + ) + } +} diff --git a/web-react/src/pages/system/machine/disk-charts.jsx b/web-react/src/pages/system/machine/disk-charts.jsx new file mode 100644 index 0000000..0024161 --- /dev/null +++ b/web-react/src/pages/system/machine/disk-charts.jsx @@ -0,0 +1,107 @@ +import React, { Component } from 'react' +import { Card, Col, Row } from 'antd' +import * as echarts from 'echarts' + +export default class diskCharts extends Component { + diskInfo = [] + + diskChart1 = [] + diskChart2 = [] + diskChart3 = [] + diskChart4 = [] + + constructor(props) { + super(props) + + const { base } = props + + this.diskInfo = base.diskInfo + } + + componentDidMount() { + this.diskInfo.forEach(({ size, freeSpace }, i) => { + const dom = this.refs[`disk-chart-${i}`] + this[`diskChart${i}`] = echarts.init(dom) + + const usedSpace = size - freeSpace + const sizeGB = (size / 1024 / 1024 / 1024).toFixed(1) + const usedGB = (usedSpace / 1024 / 1024 / 1024).toFixed(1) + const freeGB = (freeSpace / 1024 / 1024 / 1024).toFixed(1) + + const option = { + tooltip: false, + series: [ + { + name: '磁盘使用量', + type: 'pie', + radius: ['70%', '100%'], + label: { + show: true, + fontSize: '14', + position: 'center', + formatter: `共 ${sizeGB} GB`, + }, + emphasis: { + label: { + show: true, + fontSize: '14', + formatter: '{b}{c}GB({d}%)', + }, + }, + labelLine: { + show: false, + }, + data: [ + { + value: usedGB, + name: '已用', + itemStyle: { + color: usedGB / sizeGB >= 0.85 ? '#ff4d4f' : '#007bff', + }, + }, + { + value: freeGB, + name: '可用', + itemStyle: { color: '#e0e0e0' }, + }, + ], + hoverAnimation: false, + animation: false, + }, + ], + } + this[`diskChart${i}`].setOption(option) + }) + + window.addEventListener('resize', this.onResizeCharts) + } + + componentWillUnmount() { + window.removeEventListener('resize', this.onResizeCharts) + } + + onResizeCharts = () => { + this.diskInfo.forEach((item, i) => { + this[`diskChart${i}`].resize() + }) + } + + render() { + const { diskInfo } = this + + return ( + <> + {diskInfo.map((item, i) => ( + + +
+ {item.description}({item.name}) +
+
+
+ + ))} + + ) + } +} diff --git a/web-react/src/pages/system/machine/index.jsx b/web-react/src/pages/system/machine/index.jsx new file mode 100644 index 0000000..fa6a8a9 --- /dev/null +++ b/web-react/src/pages/system/machine/index.jsx @@ -0,0 +1,44 @@ +import React, { Component } from 'react' +import { Card, Col, Descriptions, Row, Statistic } from 'antd' +import { api } from 'common/api' +import { Container } from 'components' +import { isEqual } from 'lodash' +import moment from 'moment' + +import Base from './base' +import UseCharts from './use-charts' +import DiskCharts from './disk-charts' + +export default class index extends Component { + state = { + loading: true, + base: {}, + network: {}, + } + + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) || this.props.paneActived !== props.paneActived + } + + async componentDidMount() { + const { data: base } = await api.sysMachineBase() + this.setState({ loading: false, base }) + } + + render() { + const { paneActived } = this.props + + const { loading } = this.state + + return ( + +
+ + {!loading && } + {!loading && } + {!loading && } + +
+ ) + } +} diff --git a/web-react/src/pages/system/machine/use-charts.jsx b/web-react/src/pages/system/machine/use-charts.jsx new file mode 100644 index 0000000..2693159 --- /dev/null +++ b/web-react/src/pages/system/machine/use-charts.jsx @@ -0,0 +1,297 @@ +import React, { Component } from 'react' +import { Card, Col, Descriptions, Row, Statistic } from 'antd' +import * as echarts from 'echarts' +import moment from 'moment' +import { api } from 'common/api' + +export default class useCharts extends Component { + state = { + use: {}, + + nowMoment: moment(), + } + + timer = null + timerMoment = null + + systemStart = moment() + + now = Date.now() + + cpuChart = null + cpuData = [] + + ramChart = null + ramData = [] + + shouldComponentUpdate(props) { + // 当前页签未选中时停止获取状态 + if (this.props.actived !== props.actived) { + if (props.actived) { + this.start() + } else { + this.stop() + } + } + return true + } + + componentDidMount() { + this.systemStart = moment().add(-this.props.base.runTime) + this.initCpuChart() + this.initRamChart() + + this.start() + + window.addEventListener('resize', this.onResizeCharts) + } + + componentWillUnmount() { + this.stop() + + window.removeEventListener('resize', this.onResizeCharts) + } + + start() { + this.timer = setInterval(() => { + this.refreshData() + }, 3000) + this.refreshData() + this.timerMoment = setInterval(() => { + this.setState({ nowMoment: moment() }) + }, 1000) + } + + stop() { + clearInterval(this.timer) + clearInterval(this.timerMoment) + } + + async refreshData() { + const { data: use } = await api.sysMachineUse() + + this.now = Date.now() + + this.cpuData.shift() + this.cpuData.push({ + name: this.now, + value: [this.now, use.cpuRate], + }) + this.cpuChart.setOption({ + series: [{ data: this.cpuData }], + }) + + this.ramData.shift() + this.ramData.push({ + name: this.now, + value: [this.now, use.ramRate], + }) + this.ramChart.setOption({ + series: [{ data: this.ramData }], + }) + + this.setState({ use }) + } + + initCpuChart() { + for (let i = 0; i < 60; i++) { + const past = this.now - (60 - i) * 1000 + this.cpuData.push({ + name: past, + value: [past, -1], + }) + } + + const dom = this.refs['cpu-chart'] + this.cpuChart = echarts.init(dom) + const option = { + grid: { + show: true, + top: 0, + left: 0, + right: 0, + bottom: 0, + borderColor: 'rgba(0, 123, 255, 1)', + borderWidth: 2, + zlevel: 2, + }, + tooltip: false, + xAxis: { + type: 'time', + axisTick: { + show: false, + }, + axisLabel: { + show: false, + }, + axisLine: { + show: false, + }, + }, + yAxis: { + type: 'value', + max: 100, + min: 0, + axisLabel: { + show: false, + }, + }, + series: [ + { + type: 'line', + showSymbol: false, + hoverAnimation: false, + animation: false, + data: this.cpuData, + lineStyle: { + width: 1, + color: 'rgba(0, 123, 255, .8)', + }, + areaStyle: { + color: 'rgba(0, 123, 255, .3)', + }, + }, + ], + } + this.cpuChart.setOption(option) + } + + initRamChart() { + for (let i = 0; i < 60; i++) { + const past = this.now - (60 - i) * 1000 + this.ramData.push({ + name: past, + value: [past, -1], + }) + } + + const dom = this.refs['ram-chart'] + this.ramChart = echarts.init(dom) + const option = { + grid: { + show: true, + top: 0, + left: 0, + right: 0, + bottom: 0, + borderColor: 'rgba(83, 29, 171, 1)', + borderWidth: 2, + zlevel: 2, + }, + tooltip: false, + xAxis: { + type: 'time', + axisTick: { + show: false, + }, + axisLabel: { + show: false, + }, + axisLine: { + show: false, + }, + }, + yAxis: { + type: 'value', + max: 100, + min: 0, + axisLabel: { + show: false, + }, + }, + series: [ + { + type: 'line', + showSymbol: false, + hoverAnimation: false, + animation: false, + data: this.ramData, + lineStyle: { + width: 1, + color: 'rgba(83, 29, 171, .8)', + }, + areaStyle: { + color: 'rgba(83, 29, 171, .3)', + }, + }, + ], + } + this.ramChart.setOption(option) + } + + onResizeCharts = () => { + this.cpuChart.resize() + this.ramChart.resize() + } + + render() { + const { base } = this.props + const { use, nowMoment } = this.state + const { cpuName, cpuBaseSpeed, processorCount, totalRam } = base + const { cpuRate, ramRate } = use + + const diffDays = nowMoment.diff(this.systemStart, 'days') + const diff = + diffDays + ':' + moment(nowMoment.diff(this.systemStart)).utc().format('HH:mm:ss') + + return ( + <> + + + +
CPU
+
{cpuName}
+
+
+ + + + + + + + + {((cpuBaseSpeed || 0) / 1000).toFixed(2)} GHz + + + {processorCount || 0} + + + + +
+ + + + +
内存
+
{((totalRam || 0) / 1024).toFixed(1)} GB
+
+
+ + + + + + +
+ + + ) + } +} diff --git a/web-react/src/pages/system/menu/form.jsx b/web-react/src/pages/system/menu/form.jsx index 83f9b8f..bbc7dea 100644 --- a/web-react/src/pages/system/menu/form.jsx +++ b/web-react/src/pages/system/menu/form.jsx @@ -7,29 +7,31 @@ import { api } from 'common/api' import { EMPTY_ID } from 'util/global' const initialValues = { - type: '1', - openType: '1', + type: 1, + openType: 1, + weight: '2', visible: true, - sort: 100 + sort: 100, } export default class form extends Component { - state = { // 加载状态 loading: true, codes: { menuType: [], - openType: [] + openType: [], + menuWeight: [], }, options: { appList: [], - parentTreeData: [] + parentTreeData: [], }, + addType: [], type: initialValues.type, openType: initialValues.openType, - icon: '' + icon: '', } // 表单实例 @@ -51,45 +53,49 @@ export default class form extends Component { * 填充数据 * 可以在设置this.record之后对其作出数据结构调整 * [异步,必要] - * @param {*} params + * @param {*} params */ async fillData(params) { + const form = this.form.current this.record = cloneDeep(params.record) //#region 从后端转换成前段所需格式 - const { menuType, openType } = await getDictData('menu_type', 'open_type') + const codes = await getDictData('menu_type', 'open_type', 'menu_weight') const appList = await this.onLoadSysApplist() let parentTreeData = [] if (params.isParent) { parentTreeData = await this.onLoadMenuTree(params.parent.application) - } else if (params.record) { + } + + if (params.record) { parentTreeData = await this.onLoadMenuTree(params.record.application) + } else { + this.setState({ addType: params.addType }) + if (params.addType.length) { + form.setFieldsValue({ + type: params.addType[0], + }) + } } const icon = params.record && params.record.icon this.setState({ - codes: { - menuType, - openType - }, + codes, options: { appList, - parentTreeData + parentTreeData, }, - icon + icon, }) //#endregion - const form = this.form.current if (params.isParent) { form.setFieldsValue({ pid: params.parent.id, - application: params.parent.application + application: params.parent.application, }) } else { form.setFieldsValue(this.record) } - this.setState({ - loading: false - }) + this.setState({ loading: false }) this.onTypeChange() } @@ -98,7 +104,7 @@ export default class form extends Component { * 获取数据 * 可以对postData进行数据结构调整 * [异步,必要] - * @returns + * @returns */ async getData() { const form = this.form.current @@ -123,30 +129,31 @@ export default class form extends Component { async onLoadMenuTree(application) { const { data } = await api.getMenuTree({ application }) - return [{ - id: EMPTY_ID, - parentId: undefined, - title: '顶级', - value: EMPTY_ID, - pid: undefined, - children: data, - }] + return [ + { + id: EMPTY_ID, + parentId: undefined, + title: '顶级', + value: EMPTY_ID, + pid: undefined, + children: data, + }, + ] } - onTypeChange() { this.onTypeChangeGroup() - const form = this.form.current - const { type } = form.getFieldsValue() - if (['0', '2'].includes(type)) { - form.setFieldsValue({ - openType: '0' - }) - } else { - form.setFieldsValue({ - openType: '1' - }) - } + // const form = this.form.current + // const { type } = form.getFieldsValue() + // if ([0, 2].includes(type)) { + // form.setFieldsValue({ + // openType: 0, + // }) + // } else { + // form.setFieldsValue({ + // openType: 1, + // }) + // } } onOpenTypeChange() { @@ -168,178 +175,221 @@ export default class form extends Component { this.setState({ type, - openType + openType, }) } async onApplicationChange(value) { this.setState({ - loading: true + loading: true, }) const parentTreeData = await this.onLoadMenuTree(value) this.setState({ loading: false, options: { ...this.state.options, - parentTreeData - } + parentTreeData, + }, }) this.form.current.setFieldsValue({ - pid: undefined + pid: undefined, }) } onSelectIcon(icon) { this.form.current.setFieldsValue({ - icon + icon, }) this.setState({ icon }) } //#endregion render() { + const { loading, codes, options, addType, type, openType, icon } = this.state + return ( -
- }> + + }>

基本信息

- 目录:默认添加在顶级 -
菜单: -
按钮: + 目录:一级菜单,默认添加在顶级 +
+ 菜单:二级菜单 +
+ 按钮:菜单中对应到接口的功能 } rules={[{ required: true, message: '请选择菜单类型' }]} > - this.onTypeChange(e)}> - { - this.state.codes.menuType.map(item => { - return ( - {item.value} - ) - }) - } + this.onTypeChange(e)}> + {codes.menuType.map(item => { + return ( + + {item.value} + + ) + })}
- + - - this.onApplicationChange(value)} + > + {options.appList.map(item => { + return ( + + {item.name} + + ) + })} - { - this.state.type != 0 && - + {type != 0 && ( + - } + )} + + 系统权重:菜单/功能任何角色可用 +
+ 业务权重:菜单/功能为超级管理员不可用,可防止管理员误操作 + + } + rules={[{ required: true, message: '请选择权重' }]} + > + + {codes.menuWeight.map(item => { + return ( + + {item.value} + + ) + })} + +

扩展信息

- { - this.state.type == 1 && + {type == 1 && ( - this.onOpenTypeChange(e)}> - { - this.state.codes.openType.map(item => { - return ( - {item.value} - ) - }) - } + this.onOpenTypeChange(e)}> + {codes.openType.map(item => { + return ( + + {item.value} + + ) + })} - } - { - this.state.type == 1 && this.state.openType == 1 && - + )} + {type == 1 && openType == 1 && ( + - } - { - this.state.type == 1 && this.state.openType == 2 && - + )} + {type == 1 && openType == 2 && ( + - } - { - this.state.type == 1 && this.state.openType == 3 && - + )} + {type == 1 && openType == 3 && ( + - } - { - this.state.type == 2 && - + )} + {type == 2 && ( + - } - { - this.state.type == 2 && - + )} + {type == 2 && ( + - } + )} - { - this.state.type != 2 && + {type != 2 && ( - } + addonBefore={icon && } addonAfter={ - this - .iconSelector - .current - .open(this.form.current.getFieldValue('icon')) + this.iconSelector.current.open( + this.form.current.getFieldValue('icon') + ) } /> } /> - } + )} - this.onSelectIcon(icon)} /> + this.onSelectIcon(icon)} /> ) } diff --git a/web-react/src/pages/system/menu/index.jsx b/web-react/src/pages/system/menu/index.jsx index c4f44d8..a7566f3 100644 --- a/web-react/src/pages/system/menu/index.jsx +++ b/web-react/src/pages/system/menu/index.jsx @@ -1,5 +1,5 @@ import React, { Component } from 'react' -import { Button, Table, Card, Form, Input, Popconfirm, message as Message, Tag } from 'antd' +import { Button, Table, Card, Popconfirm, message as Message, Row, Col, Tooltip, Tag } from 'antd' import { isEqual } from 'lodash' import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components' import { api } from 'common/api' @@ -24,6 +24,7 @@ export default class index extends Component { codes: { menuType: [], menuWeight: [], + openType: [], }, } @@ -55,16 +56,36 @@ export default class index extends Component { render: text => text && , }, { - title: '前端组件', + title: '连接', width: 220, - dataIndex: 'component', - ellipsis: true, - }, - { - title: '权限标识', - width: 220, - dataIndex: 'permission', - ellipsis: true, + dataIndex: 'openType', + render: (text, record) => { + switch (text) { + case 1: + return ( + <> + {this.bindCodeValue(text, 'open_type')}{' '} + {record.component} + + ) + case 2: + return ( + <> + {this.bindCodeValue(text, 'open_type')}{' '} + {record.link} + + ) + case 3: + return ( + <> + {this.bindCodeValue(text, 'open_type')}{' '} + {record.redirect} + + ) + default: + return '' + } + }, }, { title: '排序', @@ -85,19 +106,14 @@ export default class index extends Component { if (flag) { this.columns.push({ title: '操作', - width: 150, + width: 220, dataIndex: 'actions', render: (text, record) => ( - {record.type < 2 && ( - - this.onOpen(this.addForm, record, true)}> - 新增子菜单 - - - )} - this.onOpen(this.editForm, record)}>编辑 + this.onOpen({ modal: this.editForm, record })}> + 编辑 + 删除 + {record.type < 2 && ( + + + this.onOpen({ + modal: this.addForm, + record, + isParent: true, + addType: record.type == 0 ? [1] : [2], + }) + } + > + {record.type == 0 ? '新增子菜单' : '新增功能'} + + + )} ), }) @@ -132,7 +164,7 @@ export default class index extends Component { */ componentDidMount() { this.table.current.onLoading() - getDictData('menu_type', 'menu_weight').then(res => { + getDictData('menu_type', 'menu_weight', 'open_type').then(res => { this.setState( { codes: res, @@ -156,27 +188,8 @@ export default class index extends Component { ...params, ...query, }) - return this.onfilterdata(data) - //return data - } - - onfilterdata(data) { - this.findlastChildren(data) return data } - findlastChildren(data) { - data.forEach(s => { - if (s.children && s.children.length > 0) { - s.rowExpandable = true - s.children.forEach(p => { - if (p.children && p.children.length === 0) { - p.rowExpandable = false - } - }) - this.findlastChildren(s.children) - } - }) - } /** * 绑定字典数据 @@ -188,7 +201,7 @@ export default class index extends Component { name = toCamelCase(name) const codes = this.state.codes[name] if (codes) { - const c = codes.find(p => p.code === code) + const c = codes.find(p => p.code == code) if (c) { return c.value } @@ -201,15 +214,17 @@ export default class index extends Component { * @param {*} modal * @param {*} record */ - onOpen(modal, record, isParent = false) { + onOpen({ modal, record, isParent = false, addType = [] }) { const params = isParent ? { parent: record, isParent, + addType, } : { record, isParent, + addType, } modal.current.open(params) @@ -240,64 +255,106 @@ export default class index extends Component { this.onAction(apiAction.delete(record), '删除成功') } + //#region 自定义方法 /** * 绘制新的扩展行 * @param {*} record */ onRowRender(record) { - var arr = [] - var istag = false - record.children.map(s => { - if (!s.rowExpandable && s.type == 2) { - istag = true - var temp = ( - - {s.name} |{' '} - - this.onOpen(this.editForm, s)}>编辑 - {' '} - |{' '} - - this.onDelete(s)} - > - 删除 - - - + const grids = [] + let isFunction = false + record.children.map(item => { + if (!(item.children && item.children.length) && item.type == 2) { + isFunction = true + const grid = ( + +

+ {item.visibleParent && ( + + + + )} + {item.name} +

+ {item.permission} +
+ + + + + this.onOpen({ modal: this.editForm, record: item }) + } + > + + + + + + this.onDelete(item)} + > + + + + + + + + +
+
) - arr.push(temp) - } else if (s.rowExpandable || (!s.rowExpandable && s.type == 1)) { - arr.push(s) + grids.push(grid) + } else if ( + (item.children && item.children.length) || + (!(item.children && item.children.length) && item.type == 1) + ) { + grids.push(item) } }) - if (istag) { - return arr + if (isFunction) { + grids.push( + + this.onOpen({ modal: this.addForm, record, isParent: true, addType: [2] }) + } + > +
+ +
+ 新增功能 +
+ ) + return {grids} } else { return ( record.id} expandable={{ expandedRowRender: record => this.onRowRender(record), - rowExpandable: record => record.rowExpandable === true, + rowExpandable: record => record.children && record.children.length, }} - childrenColumnName="11" + childrenColumnName="none" bordered={true} pagination={false} /> ) } } - - //#region 自定义方法 - async onSetDefault(record) { - this.onAction(apiAction.setDefault(record), '设置成功') - } //#endregion render() { @@ -306,23 +363,25 @@ export default class index extends Component {
this.onRowRender(record), - rowExpandable: record => record.rowExpandable === true, + rowExpandable: record => record.children && record.children.length, }} operator={ } diff --git a/web-react/src/pages/system/org/index.jsx b/web-react/src/pages/system/org/index.jsx index ad00a84..431e063 100644 --- a/web-react/src/pages/system/org/index.jsx +++ b/web-react/src/pages/system/org/index.jsx @@ -1,6 +1,14 @@ import React, { Component } from 'react' import { Button, Card, Form, Input, message as Message, Popconfirm } from 'antd' -import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions, QueryTreeLayout } from 'components' +import { + AntIcon, + Auth, + Container, + ModalForm, + QueryTable, + QueryTableActions, + QueryTreeLayout, +} from 'components' import { api } from 'common/api' import auth from 'components/authorized/handler' import { toCamelCase } from 'util/format' @@ -13,17 +21,16 @@ const apiAction = { page: api.getOrgPage, add: api.sysOrgAdd, edit: api.sysOrgEdit, - delete: api.sysOrgDelete + delete: api.sysOrgDelete, } const name = '机构' export default class index extends Component { - state = { codes: { - orgType: [] - } + orgType: [], + }, } // 树框架实例 @@ -44,13 +51,13 @@ export default class index extends Component { columns = [ { title: '机构名称', - width: '400px', + width: 400, dataIndex: 'name', sorter: true, }, { title: '唯一编码', - width: '200px', + width: 200, dataIndex: 'code', sorter: true, }, @@ -58,24 +65,26 @@ export default class index extends Component { title: '机构类型', dataIndex: 'type', sorter: true, - render: text => (<>{this.bindCodeValue(text, 'org_type')}) + render: text => <>{this.bindCodeValue(text, 'org_type')}, }, { title: '排序', - width: '80px', + width: 80, dataIndex: 'sort', sorter: true, + defaultSortOrder: 'ascend', }, { title: '备注', dataIndex: 'remark', + width: 400, sorter: true, }, ] /** * 构造函数,在渲染前动态添加操作字段等 - * @param {*} props + * @param {*} props */ constructor(props) { super(props) @@ -87,20 +96,22 @@ export default class index extends Component { title: '操作', width: 150, dataIndex: 'actions', - render: (text, record) => ( - - this.onOpen(this.editForm, record)}>编辑 - - - this.onDelete(record)} - > - 删除 - - - ) + render: (text, record) => ( + + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + + ), }) } } @@ -109,9 +120,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -124,26 +135,28 @@ export default class index extends Component { componentDidMount() { this.table.current.onLoading() getDictData('org_type').then(res => { - this.setState({ - codes: res - }, () => { - this.table.current.onLoadData() - }) + this.setState( + { + codes: res, + }, + () => { + this.table.current.onLoadData() + } + ) }) } /** * 调用加载数据接口,可在调用前对query进行处理 * [异步,必要] - * @param {*} params - * @param {*} query - * @returns + * @param {*} params + * @param {*} query + * @returns */ loadData = async (params, query) => { - query = { ...query, - pid: this.selectId + pid: this.selectId, } const { data } = await apiAction.page({ @@ -166,7 +179,7 @@ export default class index extends Component { /** * 树节点选中事件 * [必要] - * @param {*} id + * @param {*} id */ onSelectTree(id) { this.selectId = id @@ -175,15 +188,15 @@ export default class index extends Component { /** * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns + * @param {*} code + * @param {*} name + * @returns */ bindCodeValue(code, name) { name = toCamelCase(name) const codes = this.state.codes[name] if (codes) { - const c = codes.find((p) => p.code === code) + const c = codes.find(p => p.code == code) if (c) { return c.value } @@ -193,21 +206,21 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record + * @param {*} modal + * @param {*} record */ onOpen(modal, record) { modal.current.open({ orgId: this.selectId, - record + record, }) } /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { this.table.current.onLoading() @@ -227,13 +240,10 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } //#region 自定义方法 @@ -245,7 +255,7 @@ export default class index extends Component { ref={this.treeLayout} loadData={this.loadTreeData} defaultExpanded={true} - onSelect={(key) => this.onSelectTree(key)} + onSelect={key => this.onSelectTree(key)} > @@ -265,7 +275,9 @@ export default class index extends Component { + > + 新增{name} + } /> diff --git a/web-react/src/pages/system/pos/index.jsx b/web-react/src/pages/system/pos/index.jsx index c12f74c..e59dda6 100644 --- a/web-react/src/pages/system/pos/index.jsx +++ b/web-react/src/pages/system/pos/index.jsx @@ -2,24 +2,22 @@ import React, { Component } from 'react' import { Button, Card, Form, Input, Popconfirm, message as Message } from 'antd' import { isEqual } from 'lodash' import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components' -import { api } from "common/api" +import { api } from 'common/api' import auth from 'components/authorized/handler' import FormBody from './form' - // 配置页面所需接口函数 const apiAction = { page: api.sysPosPage, add: api.sysPosAdd, edit: api.sysPosEdit, - delete: api.sysPosDelete + delete: api.sysPosDelete, } // 用于弹窗标题 const name = '职位' export default class index extends Component { - // 表格实例 table = React.createRef() @@ -32,29 +30,34 @@ export default class index extends Component { { title: '职位名称', dataIndex: 'name', + width: 400, sorter: true, }, { title: '唯一编码', dataIndex: 'code', + width: 400, sorter: true, }, { title: '排序', dataIndex: 'sort', + width: 80, sorter: true, + defaultSortOrder: 'ascend', }, { title: '备注', dataIndex: 'remark', + width: 400, sorter: true, }, ] /** - * 构造函数,在渲染前动态添加操作字段等 - * @param {*} props - */ + * 构造函数,在渲染前动态添加操作字段等 + * @param {*} props + */ constructor(props) { super(props) @@ -65,20 +68,22 @@ export default class index extends Component { title: '操作', width: 150, dataIndex: 'actions', - render: (text, record) => ( - - this.onOpen(this.editForm, record)}>编辑 - - - this.onDelete(record)} - > - 删除 - - - ) + render: (text, record) => ( + + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + + ), }) } } @@ -87,9 +92,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -98,9 +103,9 @@ export default class index extends Component { /** * 调用加载数据接口,可在调用前对query进行处理 * [异步,必要] - * @param {*} params - * @param {*} query - * @returns + * @param {*} params + * @param {*} query + * @returns */ loadData = async (params, query) => { const { data } = await apiAction.page({ @@ -111,21 +116,21 @@ export default class index extends Component { } /** - * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record - */ + * 打开新增/编辑弹窗 + * @param {*} modal + * @param {*} record + */ onOpen(modal, record) { modal.current.open({ - record + record, }) } /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { this.table.current.onLoading() @@ -140,13 +145,10 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } render() { @@ -172,11 +174,11 @@ export default class index extends Component { + > + 新增{name} + } - > - - + > ) } -} \ No newline at end of file +} diff --git a/web-react/src/pages/system/role/data.jsx b/web-react/src/pages/system/role/data.jsx index 74a094d..d6c08ec 100644 --- a/web-react/src/pages/system/role/data.jsx +++ b/web-react/src/pages/system/role/data.jsx @@ -10,7 +10,6 @@ const { SHOW_PARENT } = TreeSelect const initialValues = {} export default class data extends Component { - state = { // 加载状态 loading: true, @@ -19,7 +18,7 @@ export default class data extends Component { arerTreeData: [], orgCheckedKeys: [], - isDefine: false + isDefine: false, } // 表单实例 @@ -39,10 +38,9 @@ export default class data extends Component { * 填充数据 * 可以在设置this.record之后对其作出数据结构调整 * [异步,必要] - * @param {*} params + * @param {*} params */ async fillData(params) { - this.record = cloneDeep(params.record) //#region 从后端转换成前段所需格式 const { dataScopeType } = await getDictData('data_scope_type') @@ -53,17 +51,17 @@ export default class data extends Component { dataScopeType, orgTreeData, arerTreeData, - orgCheckedKeys + orgCheckedKeys, }) //#endregion this.form.current.setFieldsValue({ - dataScopeType: this.record.dataScopeType.toString() + dataScopeType: this.record.dataScopeType.toString(), }) this.onChange(this.record.dataScopeType) this.setState({ - loading: false + loading: false, }) } @@ -71,7 +69,7 @@ export default class data extends Component { * 获取数据 * 可以对postData进行数据结构调整 * [异步,必要] - * @returns + * @returns */ async getData() { const form = this.form.current @@ -107,11 +105,11 @@ export default class data extends Component { onChange(value) { if (value == 5) { this.setState({ - isDefine: true + isDefine: true, }) } else { this.setState({ - isDefine: false + isDefine: false, }) } } @@ -119,29 +117,24 @@ export default class data extends Component { render() { return ( -
+ }>
- this.onChange(value)} + > + {this.state.dataScopeType.map(item => { + return ( + + {item.value} + + ) + })} - { - this.state.isDefine && + {this.state.isDefine && ( <> - + - } + )}
diff --git a/web-react/src/pages/system/role/index.jsx b/web-react/src/pages/system/role/index.jsx index e9edb04..865db78 100644 --- a/web-react/src/pages/system/role/index.jsx +++ b/web-react/src/pages/system/role/index.jsx @@ -16,14 +16,13 @@ const apiAction = { delete: api.sysRoleDelete, grantMenu: api.sysRoleGrantMenu, - grantData: api.sysRoleGrantData + grantData: api.sysRoleGrantData, } // 用于弹窗标题 const name = '角色' export default class index extends Component { - // 表格实例 table = React.createRef() @@ -39,23 +38,27 @@ export default class index extends Component { { title: '角色名', dataIndex: 'name', + width: 400, sorter: true, }, { title: '唯一编码', dataIndex: 'code', + width: 400, sorter: true, }, { title: '排序', dataIndex: 'sort', + width: 80, sorter: true, - } + defaultSortOrder: 'ascend', + }, ] /** * 构造函数,在渲染前动态添加操作字段等 - * @param {*} props + * @param {*} props */ constructor(props) { super(props) @@ -67,44 +70,58 @@ export default class index extends Component { title: '操作', width: 150, dataIndex: 'actions', - render: (text, record) => ( - - this.onOpen(this.editForm, record)}>编辑 - - - this.onDelete(record)} - > - 删除 - - - - - - - this.onOpen(this.menuForm, record)}>授权菜单 - - - - - this.onOpen(this.dataForm, record)}>授权数据 - - - - } - > - - 授权 - - - - - ) + render: (text, record) => ( + + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + + + + + + this.onOpen(this.menuForm, record) + } + > + 授权菜单 + + + + + + + this.onOpen(this.dataForm, record) + } + > + 授权数据 + + + + + } + > + + 授权 + + + + + + ), }) } } @@ -113,9 +130,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -124,9 +141,9 @@ export default class index extends Component { /** * 调用加载数据接口,可在调用前对query进行处理 * [异步,必要] - * @param {*} params - * @param {*} query - * @returns + * @param {*} params + * @param {*} query + * @returns */ loadData = async (params, query) => { const { data } = await apiAction.page({ @@ -138,20 +155,20 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record + * @param {*} modal + * @param {*} record */ onOpen(modal, record) { modal.current.open({ - record + record, }) } /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { this.table.current.onLoading() @@ -166,13 +183,10 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } //#region 自定义方法 @@ -202,7 +216,9 @@ export default class index extends Component { + > + 新增{name} + } /> diff --git a/web-react/src/pages/system/user/data.jsx b/web-react/src/pages/system/user/data.jsx new file mode 100644 index 0000000..2e5d0b7 --- /dev/null +++ b/web-react/src/pages/system/user/data.jsx @@ -0,0 +1,115 @@ +import React, { Component } from 'react' +import { Form, Spin, TreeSelect } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep } from 'lodash' +import { api } from 'common/api' + +export default class data extends Component { + state = { + // 加载状态 + loading: true, + + options: { + orgData: [], + areaData: [], + orgCheckedKeys: [], + }, + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + id = '' + + /** + * mount后回调 + */ + componentDidMount() { + this.props.created && this.props.created(this) + } + async fillData(params) { + this.id = params.id + //#region 从后端转换成前段所需格式 + const orgData = await this.loadOrgData() + const areaData = await this.loadAreaData() + const orgCheckedKeys = await this.loadMemberOwn(this.id) + this.setState({ + options: { + orgData, + areaData, + orgCheckedKeys, + }, + }) + this.form.current.setFieldsValue({ + id: this.id, + grantOrgIdList: orgCheckedKeys, + grantAreaCodeList: [], + }) + + this.setState({ + loading: false, + }) + } + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + if (this.id) { + postData.id = this.id + } + //#region 从前段转换后端所需格式 + //#endregion + return postData + } + } + + //#region 自定义方法 + async loadOrgData() { + const { data } = await api.getOrgTree() + return data + } + + async loadAreaData() { + const { data } = await api.getAreaTree() + return data + } + async loadMemberOwn(id) { + const { data } = await api.sysUserOwnData({ id }) + return data + } + render() { + return ( +
+ }> +
+ + + + + + +
+
+ + ) + } +} diff --git a/web-react/src/pages/system/user/form.jsx b/web-react/src/pages/system/user/form.jsx index b8eed13..204c88a 100644 --- a/web-react/src/pages/system/user/form.jsx +++ b/web-react/src/pages/system/user/form.jsx @@ -1,5 +1,17 @@ import React, { Component } from 'react' -import { Button, Row, Col, Form, Input, DatePicker, Radio, Table, Select, Spin, TreeSelect } from 'antd' +import { + Button, + Row, + Col, + Form, + Input, + DatePicker, + Radio, + Table, + Select, + Spin, + TreeSelect, +} from 'antd' import { AntIcon } from 'components' import { cloneDeep } from 'lodash' import getDictData from 'util/dic' @@ -9,25 +21,24 @@ import moment from 'moment' const initialValues = { sex: 0, - sysEmpParam: {} + sysEmpParam: {}, } export default class form extends Component { - state = { // 加载状态 loading: true, codes: { - orgType: [] + orgType: [], }, options: { orgData: [], - posData: [] + posData: [], }, sysEmpParam: { - extIds: [] - } + extIds: [], + }, } extColumns = [ { @@ -45,7 +56,7 @@ export default class form extends Component { placeholder="请选择附加组织机构" /> - ) + ), }, { title: '附属岗位', @@ -56,33 +67,28 @@ export default class form extends Component { - ) + ), }, { title: '操作', key: 'action', width: '70px', render: (text, record) => ( - - ) + ), }, ] // 表单实例 @@ -102,11 +108,13 @@ export default class form extends Component { * 填充数据 * 可以在设置this.record之后对其作出数据结构调整 * [异步,必要] - * @param {*} params + * @param {*} params */ async fillData(params) { - this.record = cloneDeep(params.record || {}) //#region 从后端转换成前段所需格式 + if (params.id) { + this.record = (await api.sysUserDetail({ id: params.id })).data + } const orgData = await this.loadOrgData() const posData = await this.loadPosData() const codes = await getDictData('org_type') @@ -118,17 +126,17 @@ export default class form extends Component { // 提交的时候是"param",而获取下来却是"info",在这里转换一下 if (this.record.sysEmpInfo) { - this.record.sysEmpParam = this.record.sysEmpInfo; - delete this.record.sysEmpInfo; + this.record.sysEmpParam = this.record.sysEmpInfo + delete this.record.sysEmpInfo } else if (!this.record.sysEmpParam) { this.record.sysEmpParam = { extIds: [], - }; + } } // 转换职位信息列表 if (this.record.sysEmpParam.positions) { - this.record.sysEmpParam.posIdList = this.record.sysEmpParam.positions.map((p) => p.posId); + this.record.sysEmpParam.posIdList = this.record.sysEmpParam.positions.map(p => p.posId) } // 附加信息 @@ -138,12 +146,12 @@ export default class form extends Component { key: i, orgId: p.orgId, posId: p.posId, - }; - }); + } + }) } if (params.orgId) { - this.record.sysEmpParam.orgId = params.orgId; + this.record.sysEmpParam.orgId = params.orgId } this.setState({ @@ -154,18 +162,18 @@ export default class form extends Component { posData, }, sysEmpParam: { - ...this.record.sysEmpParam - } + ...this.record.sysEmpParam, + }, }) this.record = { - ...this.record + ...this.record, } //#endregion this.form.current.setFieldsValue(this.record) this.setState({ - loading: false + loading: false, }) } @@ -173,7 +181,7 @@ export default class form extends Component { * 获取数据 * 可以对postData进行数据结构调整 * [异步,必要] - * @returns + * @returns */ async getData() { const form = this.form.current @@ -206,24 +214,26 @@ export default class form extends Component { const record = { key: extIds.length > 0 ? extIds[extIds.length - 1].key + 1 : 0, orgId: undefined, - posId: undefined + posId: undefined, } - this.setState({ - sysEmpParam: { - extIds: [...extIds, record] + this.setState( + { + sysEmpParam: { + extIds: [...extIds, record], + }, + }, + () => { + console.log(this.form.current.getFieldsValue()) } - }, () => { - console.log(this.form.current.getFieldsValue()) - }) - + ) } onRemoveExtData(record) { const ext = this.state.sysEmpParam.extIds, - remove = ext.find((p) => p.key === record.key), - index = ext.indexOf(remove); + remove = ext.find(p => p.key === record.key), + index = ext.indexOf(remove) - ext.splice(index, 1); + ext.splice(index, 1) console.log(ext) // this.form.current.setFieldsValue({ @@ -232,14 +242,16 @@ export default class form extends Component { // } // }) - this.setState({ - sysEmpParam: { - extIds: ext + this.setState( + { + sysEmpParam: { + extIds: ext, + }, + }, + () => { + //console.log(this.form.current.getFieldsValue()) } - }, () => { - //console.log(this.form.current.getFieldsValue()) - }) - + ) } //#endregion renderExtInfoTable() { @@ -251,79 +263,100 @@ export default class form extends Component { pagination={false} size="small" bordered - rowKey={(record) => record.key} - footer={ - () => - - } - > -
+ rowKey={record => record.key} + footer={() => ( + + )} + > ) } render() { return ( -
- }> + + }>

基本信息

- + - + - {this.props.mode == 'add' && <> - - - - - - - - } - + {this.props.mode == 'add' && ( + <> + + + + + + + + )} + - + - - + + 保密 - + - + - + - + - +
@@ -332,7 +365,8 @@ export default class form extends Component { + rules={[{ required: true, message: '所属组织机构' }]} + > - - + + - + {this.state.options.posData.map(item => { + return ( + + {item.name} + + ) + })}
diff --git a/web-react/src/pages/system/user/index.jsx b/web-react/src/pages/system/user/index.jsx index 24dd2c7..080b4e0 100644 --- a/web-react/src/pages/system/user/index.jsx +++ b/web-react/src/pages/system/user/index.jsx @@ -1,11 +1,27 @@ import React, { Component } from 'react' -import { Button, Card, Descriptions, Form, Input, List, message as Message, Popconfirm, Select, Switch } from 'antd' +import { + Button, + Card, + Descriptions, + Form, + Input, + List, + message as Message, + Popconfirm, + Select, + Switch, + Dropdown, + Menu, +} from 'antd' import { AntIcon, Auth, Container, Image, ModalForm, QueryList, QueryTreeLayout } from 'components' import { api } from 'common/api' import { toCamelCase } from 'util/format' import { isEqual } from 'lodash' import getDictData from 'util/dic' import FormBody from './form' +import RoleForm from './role' +import DataForm from './data' +import auth from 'components/authorized/handler' // 配置页面所需接口函数 const apiAction = { @@ -16,19 +32,21 @@ const apiAction = { delete: api.sysUserDelete, changeStatus: api.sysUserChangeStatus, - resetPwd: api.sysUserResetPwd + resetPwd: api.sysUserResetPwd, + + grantRole: api.sysUserGrantRole, + grantData: api.sysUserGrantData, } // 用于弹窗标题 const name = '用户' export default class index extends Component { - state = { codes: { sex: [], - commonStatus: [] - } + commonStatus: [], + }, } // 表格实例 @@ -39,6 +57,8 @@ export default class index extends Component { // 编辑窗口实例 editForm = React.createRef() + roleForm = React.createRef() + dataForm = React.createRef() // 树选中节点 selectId = undefined @@ -46,9 +66,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -61,28 +81,30 @@ export default class index extends Component { componentDidMount() { this.list.current.onLoading() getDictData('sex', 'common_status').then(res => { - this.setState({ - codes: res - }, () => { - this.list.current.onLoadData() - }) + this.setState( + { + codes: res, + }, + () => { + this.list.current.onLoadData() + } + ) }) } /** * 调用加载数据接口,可在调用前对query进行处理 * [异步,必要] - * @param {*} params - * @param {*} query - * @returns + * @param {*} params + * @param {*} query + * @returns */ loadData = async (params, query) => { - query = { ...query, sysEmpParam: { - orgId: this.selectId - } + orgId: this.selectId, + }, } const { data } = await apiAction.page({ @@ -105,7 +127,7 @@ export default class index extends Component { /** * 树节点选中事件 * [必要] - * @param {*} id + * @param {*} id */ onSelectTree(id) { this.selectId = id @@ -114,15 +136,15 @@ export default class index extends Component { /** * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns + * @param {*} code + * @param {*} name + * @returns */ bindCodeValue(code, name) { name = toCamelCase(name) const codes = this.state.codes[name] if (codes) { - const c = codes.find((p) => p.code == code) + const c = codes.find(p => p.code == code) if (c) { return c.value } @@ -132,21 +154,21 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record + * @param {*} modal + * @param {*} id */ - onOpen(modal, record) { + onOpen(modal, id) { modal.current.open({ orgId: this.selectId, - record + id, }) } /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { this.list.current.onLoading() @@ -161,39 +183,63 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } //#region 自定义方法 renderItem(record) { + const { id, account, name, nickName, avatar, sex, phone, email, status } = record return ( - this.onOpen(this.editForm, record)}>编辑 - , - - this.onDelete(record)} - > - 删除 - - , - - this.onResetPassword(record)}>重置密码 - - ] - } + key={id} + actions={[ + + this.onOpen(this.editForm, id)}>编辑 + , + + this.onDelete(id)} + > + 删除 + + , + + this.onResetPassword(id)}>重置密码 + , + + + {auth('sysUser:grantRole') && ( + + this.onOpen(this.roleForm, id)}> + 授权角色 + + + )} + {auth('sysUser:grantData') && ( + + this.onOpen(this.dataForm, id)}> + 授权额外数据 + + + )} + + } + > + + 授权 + + + + , + ]} > } /> } - title={record.nickName || record.name} - description={record.account} + title={nickName || name} + description={account} /> - {this.bindCodeValue(record.sex, 'sex')} - {record.phone || '未设置'} - {record.email || '未设置'} + + {this.bindCodeValue(sex, 'sex')} + + {phone || '未设置'} + {email || '未设置'}
this.onSetUserStatus(record, checked)} + onChange={checked => this.onSetUserStatus(id, checked)} />
) - } - onSetUserStatus(record, checked) { - this.onAction( - apiAction.changeStatus({ id: record.id, status: +!checked }), - '设置成功' - ) + onSetUserStatus(id, checked) { + this.onAction(apiAction.changeStatus({ id, status: +!checked }), '设置成功') } - onResetPassword(record) { - this.onAction( - apiAction.resetPwd(record), - '重置成功' - ) + onResetPassword(id) { + this.onAction(apiAction.resetPwd({ id }), '重置成功') } //#endregion @@ -251,7 +291,7 @@ export default class index extends Component { this.onSelectTree(key)} + onSelect={key => this.onSelectTree(key)} > @@ -273,14 +313,13 @@ export default class index extends Component { placeholder="请选择状态" className="w-200" > - { - this.state.codes.commonStatus.map(item => { - return {item.value} - }) - } + {this.state.codes.commonStatus.map(item => { + return ( + + {item.value} + + ) + })} @@ -289,9 +328,11 @@ export default class index extends Component { + > + 新增{name} + } - renderItem={(record) => this.renderItem(record)} + renderItem={record => this.renderItem(record)} /> @@ -313,6 +354,24 @@ export default class index extends Component { > + + this.list.current.onReloadData()} + > + + + + this.list.current.onReloadData()} + > + + ) } diff --git a/web-react/src/pages/system/user/role.jsx b/web-react/src/pages/system/user/role.jsx new file mode 100644 index 0000000..131cc68 --- /dev/null +++ b/web-react/src/pages/system/user/role.jsx @@ -0,0 +1,99 @@ +import React, { Component } from 'react' +import { Form, Spin, Select } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep } from 'lodash' +import { api } from 'common/api' + +export default class role extends Component { + state = { + // 加载状态 + loading: true, + + options: { + roleData: [], + }, + roles: [], + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + id = '' + + /** + * mount后回调 + */ + componentDidMount() { + this.props.created && this.props.created(this) + } + async fillData(params) { + this.id = params.id + //#region 从后端转换成前段所需格式 + const roleData = await this.loadRoleData() + const roles = await this.loadRole(this.id) + this.setState({ + options: { + roleData, + }, + roles, + }) + this.form.current.setFieldsValue({ + id: this.id, + grantRoleIdList: roles, + }) + + this.setState({ + loading: false, + }) + } + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + if (this.id) { + postData.id = this.id + } + //#region 从前段转换后端所需格式 + //#endregion + return postData + } + } + async loadRoleData() { + const { data } = await api.getRolePage() + return data.items + } + async loadRole(id) { + const { data } = await api.sysUserOwnRole({ id }) + return data + } + render() { + return ( + + }> +
+ + + +
+
+ + ) + } +} diff --git a/web-react/src/store/reducer/layout.js b/web-react/src/store/reducer/layout.js index 7f2fa41..1441e84 100644 --- a/web-react/src/store/reducer/layout.js +++ b/web-react/src/store/reducer/layout.js @@ -1,6 +1,21 @@ -const layout = (state = { - siderCollapsed: false -}, action) => { +import { SETTING_KEY } from "common/storage" +import { SIDER_BREAK_POINT } from "util/global" + +const defaultState = { + siderCollapsed: false, + allowSiderCollapsed: true +} + +const localStorageState = () => { + return JSON.parse(window.localStorage.getItem(SETTING_KEY)) || {} +} + +const mergeState = { + ...defaultState, + ...localStorageState() +} + +const layout = (state = mergeState, action) => { switch (action.type) { // 打开窗口 case 'OPEN_WINDOW': @@ -13,8 +28,23 @@ const layout = (state = { return state // 侧边收起状态 case 'TOGGLE_COLLAPSED': - const _state = { ...state, siderCollapsed: action.siderCollapsed } - return _state + { + if (window.innerWidth <= SIDER_BREAK_POINT) { + return state + } + const _state = { ...state, siderCollapsed: action.siderCollapsed } + window.localStorage.setItem(SETTING_KEY, JSON.stringify(_state)) + return _state + } + case 'AUTO_TOGGLE_COLLAPSED': + { + const _state = { + ...state, + siderCollapsed: localStorageState().siderCollapsed || action.siderCollapsed, + allowSiderCollapsed: !action.siderCollapsed + } + return _state + } default: return state } diff --git a/web-react/src/util/global/index.js b/web-react/src/util/global/index.js index 2543e20..374ca42 100644 --- a/web-react/src/util/global/index.js +++ b/web-react/src/util/global/index.js @@ -36,4 +36,9 @@ export const RSA_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQU /** * 城市名称 */ -export const CITY = '黄石市' \ No newline at end of file +export const CITY = '黄石市' + +/** + * 响应式响应宽度 + */ +export const SIDER_BREAK_POINT = 1366 \ No newline at end of file diff --git a/web-react/src/util/query/index.js b/web-react/src/util/query/index.js index 51d312f..2aa58ae 100644 --- a/web-react/src/util/query/index.js +++ b/web-react/src/util/query/index.js @@ -1,3 +1,41 @@ +import moment from 'moment' + +/** + * 从键值对的query类型转换成数组类型 + * 键:自动作为field值 + * 值:得到一个数组作为value的值 + * queryType:一个json类型,已query的键为键,QueryType为值. 如果是一个QueryType的数组,则自动对应到value中的各个值 + * 示例: + * + getSearchInfo({ + query: { + value: '123', + text: '123', + code: 'abc', + check: ['1', '2', '3'], + range: [1, 10] + }, + queryType: { + text: QueryType.Equal, + code: QueryType.Like, + check: QueryType.Equal, + range: [QueryType.GreaterThanOrEqual, QueryType.LessThan] + } + }) + + => + + [ + { field: 'value', value: ['123'] }, + { field: 'text', value: ['123'], type: '=' }, + { field: 'code', value: ['abc'], type: 'like' }, + { field: 'check', value: ['1', '2', '3'], type: '=' }, + { field: 'range', value: [1], type: '>=' }, + { field: 'range', value: [10], type: '<' } + ] + * @param {*} param0 + * @returns [{ field: '', value: [], type: '' } ...] + */ export const getSearchInfo = ({ query, queryType }) => { const searchInfo = [] Object.keys(query).forEach((p) => { @@ -38,4 +76,32 @@ export const getSearchInfo = ({ query, queryType }) => { }) return searchInfo +} + +/** + * 获取查询用时间范围数组 + * 在这里会自动将第二个时间增加1天 + * 如果选择的日期范围为2021-01-01~2021-01-10,最终需要取得 >=2021-01-01 and <2021-01-11 的结果 + * @param {*} range 时间范围数组 + * @param {*} format 格式化 + * @returns + */ +export const getSearchDateRange = (range, format = 'YYYY-MM-DD', unit = 'days') => { + if (Array.isArray(range) && range.length === 2) { + range[1] = moment(range[1]).add(1, unit) + range = range.map(p => moment(p).format(format)) + } + return range +} + +/** + * 查询条件类型 + */ +export const QueryType = { + GreaterThan: '>', + GreaterThanOrEqual: '>=', + LessThan: '<', + LessThanOrEqual: '<=', + Like: 'LIKE', + Equal: '=' } \ No newline at end of file diff --git a/web-react/src/views/main/_layout/content/index.jsx b/web-react/src/views/main/_layout/content/index.jsx index f9aadc7..e52b045 100644 --- a/web-react/src/views/main/_layout/content/index.jsx +++ b/web-react/src/views/main/_layout/content/index.jsx @@ -20,7 +20,6 @@ class ComponentDynamic extends Component { if (this.props.onRef) { this.props.onRef(this) } - return true } @@ -73,6 +72,40 @@ class ComponentDynamic extends Component { } } +class Iframe extends Component { + shouldComponentUpdate() { + if (this.props.onRef) { + this.props.onRef(this) + } + return true + } + + componentDidMount() { + if (this.props.onRef) { + this.props.onRef(this) + } + this.loadComponent() + } + + loadComponent() { + NProgress.start() + const iframe = this.refs.content + iframe.onload = () => { + NProgress.done() + } + iframe.onerror = () => { + NProgress.done() + } + iframe.src = this.props.src + } + + render() { + const { title } = this.props + + return