commit 1845017b67b3de3d12fad0238f5493235f70a93d Author: izory <791736813@qq.com> Date: Mon Aug 23 16:57:25 2021 +0800 first commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c516947 --- /dev/null +++ b/.gitignore @@ -0,0 +1,264 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc +/ZRAdmin/Properties/launchSettings.json +/ZRAdmin/Properties/PublishProfiles +/ZR.Admin.WebApi/appsettings.Stage.json diff --git a/Infrastructure/App/App.cs b/Infrastructure/App/App.cs new file mode 100644 index 0000000..2806111 --- /dev/null +++ b/Infrastructure/App/App.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Security.Claims; + +namespace Infrastructure +{ + public static class App + { + /// + /// 服务提供器 + /// + public static IServiceProvider ServiceProvider => HttpContext?.RequestServices ?? InternalApp.InternalServices.BuildServiceProvider(); + /// + /// 获取请求上下文 + /// + public static HttpContext HttpContext => HttpContextLocal.Current(); + /// + /// 获取请求上下文用户 + /// + public static ClaimsPrincipal User => HttpContext?.User; + + /// + /// 获取请求生命周期的服务 + /// + /// + /// + public static TService GetService() + where TService : class + { + return GetService(typeof(TService)) as TService; + } + + /// + /// 获取请求生命周期的服务 + /// + /// + /// + public static object GetService(Type type) + { + return ServiceProvider.GetService(type); + } + + /// + /// 获取请求生命周期的服务 + /// + /// + /// + public static TService GetRequiredService() + where TService : class + { + return GetRequiredService(typeof(TService)) as TService; + } + + /// + /// 获取请求生命周期的服务 + /// + /// + /// + public static object GetRequiredService(Type type) + { + return ServiceProvider.GetRequiredService(type); + } + } +} diff --git a/Infrastructure/App/Web/HttpContextLocal.cs b/Infrastructure/App/Web/HttpContextLocal.cs new file mode 100644 index 0000000..8c0a96d --- /dev/null +++ b/Infrastructure/App/Web/HttpContextLocal.cs @@ -0,0 +1,59 @@ +using Microsoft.AspNetCore.Http; +using System; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading; + +namespace Microsoft.AspNetCore.Http +{ + public static class HttpContextLocal + { + private static Func _asyncLocalAccessor; + private static Func _holderAccessor; + private static Func _httpContextAccessor; + + /// + /// 获取当前 HttpContext 对象 + /// + /// + public static HttpContext Current() + { + var asyncLocal = (_asyncLocalAccessor ??= CreateAsyncLocalAccessor())(); + if (asyncLocal == null) return null; + + var holder = (_holderAccessor ??= CreateHolderAccessor(asyncLocal))(asyncLocal); + if (holder == null) return null; + + return (_httpContextAccessor ??= CreateHttpContextAccessor(holder))(holder); + + // 创建异步本地访问器 + static Func CreateAsyncLocalAccessor() + { + var fieldInfo = typeof(HttpContextAccessor).GetField("_httpContextCurrent", BindingFlags.Static | BindingFlags.NonPublic); + var field = Expression.Field(null, fieldInfo); + return Expression.Lambda>(field).Compile(); + } + + // 创建常驻 HttpContext 访问器 + static Func CreateHolderAccessor(object asyncLocal) + { + var holderType = asyncLocal.GetType().GetGenericArguments()[0]; + var method = typeof(AsyncLocal<>).MakeGenericType(holderType).GetProperty("Value").GetGetMethod(); + var target = Expression.Parameter(typeof(object)); + var convert = Expression.Convert(target, asyncLocal.GetType()); + var getValue = Expression.Call(convert, method); + return Expression.Lambda>(getValue, target).Compile(); + } + + // 获取 HttpContext 访问器 + static Func CreateHttpContextAccessor(object holder) + { + var target = Expression.Parameter(typeof(object)); + var convert = Expression.Convert(target, holder.GetType()); + var field = Expression.Field(convert, "Context"); + var convertAsResult = Expression.Convert(field, typeof(HttpContext)); + return Expression.Lambda>(convertAsResult, target).Compile(); + } + } + } +} diff --git a/Infrastructure/Attribute/AppServiceAttribute.cs b/Infrastructure/Attribute/AppServiceAttribute.cs new file mode 100644 index 0000000..f9ccd02 --- /dev/null +++ b/Infrastructure/Attribute/AppServiceAttribute.cs @@ -0,0 +1,34 @@ +using System; + +namespace Infrastructure.Attribute +{ + /// + /// 参考地址:https://www.cnblogs.com/kelelipeng/p/10643556.html + /// 标记服务 + /// 如何使用? + /// 1、如果服务是本身 直接在类上使用[AppService] + /// 2、如果服务是接口 在类上使用 [AppService(ServiceType = typeof(实现接口))] + /// + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + public class AppServiceAttribute : System.Attribute + { + /// + /// 服务声明周期 + /// 不给默认值的话注册的是AddSingleton + /// + public LifeTime ServiceLifetime { get; set; } = LifeTime.Scoped; + /// + /// 指定服务类型 + /// + public Type ServiceType { get; set; } + /// + /// 是否可以从第一个接口获取服务类型 + /// + public bool InterfaceServiceType { get; set; } + } + + public enum LifeTime + { + Transient, Scoped, Singleton + } +} diff --git a/Infrastructure/Attribute/LogAttribute.cs b/Infrastructure/Attribute/LogAttribute.cs new file mode 100644 index 0000000..9250287 --- /dev/null +++ b/Infrastructure/Attribute/LogAttribute.cs @@ -0,0 +1,25 @@ +using Infrastructure.Enums; + +namespace Infrastructure.Attribute +{ + /// + /// 自定义操作日志记录注解 + /// + public class LogAttribute : System.Attribute + { + public string Title { get; set; } + public BusinessType BusinessType { get; set; } + + public LogAttribute() { } + + public LogAttribute(string name) + { + Title = name; + } + public LogAttribute(string name, BusinessType businessType) + { + Title = name; + BusinessType = businessType; + } + } +} diff --git a/Infrastructure/ConfigUtils.cs b/Infrastructure/ConfigUtils.cs new file mode 100644 index 0000000..617875d --- /dev/null +++ b/Infrastructure/ConfigUtils.cs @@ -0,0 +1,75 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System; + +/// +/// 需要引用包 +/// Microsoft.Extensions.Configuration; +/// Microsoft.Extensions.DependencyInjection; +namespace Infrastructure +{ + public class ConfigUtils + { + #region 单例访问 + + static ConfigUtils() + { + Config = App.ServiceProvider.GetRequiredService(); + + if (Instance == null) + Instance = new ConfigUtils(); + } + + public static ConfigUtils Instance { get; private set; } + #endregion + + private static IConfiguration Config { get; set; } + + /// + /// 泛型读取配置文件 + /// 目前还不能绑定到实体类 + /// + /// 获取不到配置文件设定默认值 + /// 要获取的配置文件节点名称 + /// + //public T GetConfig(string key, T defaultValue = default) + //{ + // //GetValue扩展包需要安装Microsoft.Extensions.Configuration + // var setting = Config.GetValue(key, defaultValue); + + // Console.WriteLine($"获取配置文件值key={key},value={setting}"); + // return setting; + //} + + public T GetAppConfig(string key, T defaultValue = default(T)) + { + T setting = (T)Convert.ChangeType(Config[key], typeof(T)); + var value = setting; + if (setting == null) + value = defaultValue; + //Console.WriteLine($"获取配置文件值key={key},value={value}"); + return value; + } + + /// + /// 获取配置文件 + /// + /// eg: WeChat:Token + /// + public string GetConfig(string key) + { + return Config[key]; + } + + /// + /// 获取数据库字符串连接串 + /// + /// + /// + public string GetConnectionStrings(string key) + { + return Config.GetConnectionString(key); + + } + } +} diff --git a/Infrastructure/Constant/HttpStatus.cs b/Infrastructure/Constant/HttpStatus.cs new file mode 100644 index 0000000..3643b82 --- /dev/null +++ b/Infrastructure/Constant/HttpStatus.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Infrastructure.Constant +{ + public class HttpStatus + { + /// + /// 操作成功 + /// + public static readonly int SUCCESS = 200; + /// + /// 对象创建成功 + /// + public static readonly int CREATED = 201; + + /// + /// 请求已经被接受 + /// + public static readonly int ACCEPTED = 202; + + /// + /// 操作已经执行成功,但是没有返回数据 + /// + public static readonly int NO_CONTENT = 204; + + /// + /// 资源已被移除 + /// + public static readonly int MOVED_PERM = 301; + + /// + /// 重定向 + /// + public static readonly int SEE_OTHER = 303; + + /// + /// 资源没有被修改 + /// + public static readonly int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static readonly int BAD_REQUEST = 400; + + /// + /// 未授权 + /// + public static readonly int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static readonly int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static readonly int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static readonly int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static readonly int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static readonly int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static readonly int ERROR = 500; + + /** + * 接口未实现 + */ + public static readonly int NOT_IMPLEMENTED = 501; + } +} diff --git a/Infrastructure/CustomException/CustomException.cs b/Infrastructure/CustomException/CustomException.cs new file mode 100644 index 0000000..19f8fc5 --- /dev/null +++ b/Infrastructure/CustomException/CustomException.cs @@ -0,0 +1,37 @@ +using System; + +namespace Infrastructure +{ + public class CustomException : Exception + { + public int Code { get; set; } + public string Msg { get; set; } + public string LogMsg { get; set; } + + public CustomException(string msg) : base(msg) + { + } + public CustomException(int code, string msg) : base(msg) + { + Code = code; + Msg = msg; + } + + public CustomException(ResultCode resultCode, string msg) : base(msg) + { + Code = (int)resultCode; + } + + /// + /// 自定义异常 + /// + /// + /// + /// 用于记录详细日志到输出介质 + public CustomException(ResultCode resultCode, string msg, object errorMsg) : base(msg) + { + Code = (int)resultCode; + LogMsg = errorMsg.ToString(); + } + } +} \ No newline at end of file diff --git a/Infrastructure/CustomException/ResultCode.cs b/Infrastructure/CustomException/ResultCode.cs new file mode 100644 index 0000000..4c670da --- /dev/null +++ b/Infrastructure/CustomException/ResultCode.cs @@ -0,0 +1,51 @@ +using System.ComponentModel; + +namespace Infrastructure +{ + public enum ResultCode + { + [Description("success")] + SUCCESS = 200, + + [Description("参数错误")] + PARAM_ERROR = 101, + + [Description("验证码错误")] + CAPTCHA_ERROR = 103, + + [Description("登录错误")] + LOGIN_ERROR = 105, + + [Description("NO_DATA")] + NODATA = 106, + + FAIL = 1, + + [Description("服务端出错啦")] + GLOBAL_ERROR = 500, + + [Description("请先绑定手机号")] + NOBIND_PHONENUM = 102, + + [Description("自定义异常")] + CUSTOM_ERROR = 110, + + [Description("非法签名")] + SIGN_ERROR = 110, + + [Description("非法请求")] + INVALID_REQUEST = 116, + + [Description("授权失败")] + OAUTH_FAIL = 201, + + [Description("未授权")] + DENY = 401, + + [Description("授权访问失败")] + FORBIDDEN = 403, + + [Description("Bad Request")] + BAD_REQUEST = 400 + } +} diff --git a/Infrastructure/Enums/BusinessType.cs b/Infrastructure/Enums/BusinessType.cs new file mode 100644 index 0000000..5f4195a --- /dev/null +++ b/Infrastructure/Enums/BusinessType.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Infrastructure.Enums +{ + /** + * 业务操作类型 + * 0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据 + * @author zrry + */ + public enum BusinessType + { + /** + * 其它 + */ + OTHER = 0, + + /** + * 新增 + */ + INSERT = 1, + + /** + * 修改 + */ + UPDATE = 2, + + /** + * 删除 + */ + DELETE = 3, + + /** + * 授权 + */ + GRANT = 4, + + /** + * 导出 + */ + EXPORT = 5, + + /** + * 导入 + */ + IMPORT = 6, + + /** + * 强退 + */ + FORCE = 7, + + /** + * 生成代码 + */ + GENCODE = 8, + + /** + * 清空数据 + */ + CLEAN = 9, + } +} diff --git a/Infrastructure/Extensions/AppServiceExtensions.cs b/Infrastructure/Extensions/AppServiceExtensions.cs new file mode 100644 index 0000000..98545c7 --- /dev/null +++ b/Infrastructure/Extensions/AppServiceExtensions.cs @@ -0,0 +1,71 @@ +using Infrastructure.Attribute; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Linq; +using System.Reflection; + +namespace Infrastructure.Extensions +{ + public static class AppServiceExtensions + { + /// + /// 注册引用程序域中所有有AppService标记的类的服务 + /// + /// + public static void AddAppService(this IServiceCollection services) + { + //var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + string []cls = new string[] { "ZR.Repository", "ZR.Service", "ZR.Tasks" }; + foreach (var item in cls) + { + Assembly assembly = Assembly.Load(item); + Register(services, assembly); + } + } + + private static void Register(IServiceCollection services, Assembly assembly) + { + foreach (var type in assembly.GetTypes()) + { + var serviceAttribute = type.GetCustomAttribute(); + + if (serviceAttribute != null) + { + var serviceType = serviceAttribute.ServiceType; + //情况1 适用于依赖抽象编程,注意这里只获取第一个 + if (serviceType == null && serviceAttribute.InterfaceServiceType) + { + serviceType = type.GetInterfaces().FirstOrDefault(); + } + //情况2 不常见特殊情况下才会指定ServiceType,写起来麻烦 + if (serviceType == null) + { + serviceType = type; + } + + switch (serviceAttribute.ServiceLifetime) + { + case LifeTime.Singleton: + services.AddSingleton(serviceType, type); + break; + case LifeTime.Scoped: + services.AddScoped(serviceType, type); + break; + case LifeTime.Transient: + services.AddTransient(serviceType, type); + break; + default: + services.AddTransient(serviceType, type); + break; + } + Console.WriteLine($"注册:{serviceType}"); + } + else + { + //Console.WriteLine($"注册:{serviceType}"); + } + } + } + } +} diff --git a/Infrastructure/Extensions/StringExtension.cs b/Infrastructure/Extensions/StringExtension.cs new file mode 100644 index 0000000..75885c8 --- /dev/null +++ b/Infrastructure/Extensions/StringExtension.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Infrastructure.Extensions +{ + public static class StringExtension + { + + /// + /// SQL条件拼接 + /// + /// + /// + /// + public static string If(this string str, bool condition) + { + return condition ? str : string.Empty; + } + /// + /// 判断是否为空 + /// + /// + /// + public static bool IfNotEmpty(this string str) + { + return !string.IsNullOrEmpty(str); + } + } +} diff --git a/Infrastructure/GlobalConstant.cs b/Infrastructure/GlobalConstant.cs new file mode 100644 index 0000000..6139938 --- /dev/null +++ b/Infrastructure/GlobalConstant.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Infrastructure +{ + /// + /// 全局静态常量 + /// + public class GlobalConstant + { + /// + /// 管理员权限 + /// + public static string AdminPerm = "*:*:*"; + } +} diff --git a/Infrastructure/Infrastructure.csproj b/Infrastructure/Infrastructure.csproj new file mode 100644 index 0000000..d878cb3 --- /dev/null +++ b/Infrastructure/Infrastructure.csproj @@ -0,0 +1,14 @@ + + + + net5.0 + + + + + + + + + + diff --git a/Infrastructure/InternalApp.cs b/Infrastructure/InternalApp.cs new file mode 100644 index 0000000..302f855 --- /dev/null +++ b/Infrastructure/InternalApp.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Infrastructure +{ + public static class InternalApp + { + /// + /// 应用服务 + /// + internal static IServiceCollection InternalServices; + + /// + /// 全局配置构建器 + /// + internal static IConfigurationBuilder ConfigurationBuilder; + + /// + /// 获取Web主机环境 + /// + //internal static IWebHostEnvironment WebHostEnvironment; + + /// + /// 获取泛型主机环境 + /// + internal static IHostEnvironment HostEnvironment; + } +} diff --git a/Infrastructure/Model/ApiResult.cs b/Infrastructure/Model/ApiResult.cs new file mode 100644 index 0000000..672b0dc --- /dev/null +++ b/Infrastructure/Model/ApiResult.cs @@ -0,0 +1,120 @@ +using Infrastructure.Constant; +using Newtonsoft.Json; + +namespace Infrastructure.Model +{ + public class ApiResult + { + public int Code { get; set; } + public string Msg { get; set; } + /// + /// 如果data值为null,则忽略序列化将不会返回data字段 + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public object Data { get; set; } + + /// + /// 初始化一个新创建的APIResult对象,使其表示一个空消息 + /// + public ApiResult() + { + } + + /// + /// 初始化一个新创建的 ApiResult 对象 + /// + /// + /// + public ApiResult(int code, string msg) + { + Code = code; + Msg = msg; + } + + /// + /// 初始化一个新创建的 ApiResult 对象 + /// + /// + /// + public ApiResult(int code, string msg, object data) + { + Code = code; + Msg = msg; + if (data != null) + { + Data = data; + } + } + + /// + /// 返回成功消息 + /// + /// + public ApiResult Success() + { + Code = (int)ResultCode.SUCCESS; + Msg = "success"; + return this; + } + + /// + /// 返回成功消息 + /// + /// 数据对象 + /// 成功消息 + public static ApiResult Success(object data) { return new ApiResult(HttpStatus.SUCCESS, "success", data); } + + /// + /// 返回成功消息 + /// + /// 返回内容 + /// 成功消息 + public static ApiResult Success(string msg) { return new ApiResult(HttpStatus.SUCCESS, msg, null); } + + /// + /// 返回成功消息 + /// + /// 返回内容 + /// 数据对象 + /// 成功消息 + public static ApiResult Success(string msg, object data) { return new ApiResult(HttpStatus.SUCCESS, msg, data); } + + /// + /// 访问被拒 + /// + /// + /// + public ApiResult On401() + { + Code = (int)ResultCode.DENY; + Msg = "access denyed"; + return this; + } + public ApiResult Error(ResultCode resultCode, string msg = "") + { + Code = (int)resultCode; + Msg = msg; + return this; + } + + /// + /// 返回失败消息 + /// + /// + /// + /// + public static ApiResult Error(int code, string msg) { return new ApiResult(code, msg); } + + /// + /// 返回失败消息 + /// + /// + /// + public static ApiResult Error(string msg) { return new ApiResult((int)ResultCode.CUSTOM_ERROR, msg); } + } + + public class ApiResult : ApiResult + { + public T Result { get; set; } + } +} diff --git a/Infrastructure/Model/PagedInfo.cs b/Infrastructure/Model/PagedInfo.cs new file mode 100644 index 0000000..ff5c285 --- /dev/null +++ b/Infrastructure/Model/PagedInfo.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Infrastructure.Model +{ + /// + /// 分页参数 + /// + public class PagedInfo + { + /// + /// 每页行数 + /// + public int PageSize { get; set; } = 10; + /// + /// 当前页 + /// + public int PageIndex { get; set; } = 1; + /// + /// 排序列 + /// + //public string Sort { get; set; } + /// + /// 排序类型 + /// + //public string SortType { get; set; } + /// + /// 总记录数 + /// + public int TotalCount { get; set; } + /// + /// 总页数 + /// + //public int TotalPage + //{ + // get + // { + // if (TotalCount > 0) + // { + // return TotalCount % this.PageSize == 0 ? TotalCount / this.PageSize : TotalCount / this.PageSize + 1; + // } + // else + // { + // return 0; + // } + // } + // set { } + //} + public int TotalPage { get; set; } + public List Result { get; set; } + + public PagedInfo() + { + } + + + public PagedInfo(List source, int pageIndex, int pageSize) + { + PageIndex = pageIndex; + PageSize = pageSize; + TotalCount = source.Count; + TotalPage = (int)Math.Ceiling(TotalCount / (double)PageSize);//计算总页数 + } + + } +} diff --git a/Infrastructure/OptionsSetting.cs b/Infrastructure/OptionsSetting.cs new file mode 100644 index 0000000..4aa2b2e --- /dev/null +++ b/Infrastructure/OptionsSetting.cs @@ -0,0 +1,59 @@ + +namespace Infrastructure +{ + /// + /// 获取配置文件POCO实体类 + /// + public class OptionsSetting + { + public static string ConnAdmin = "Conn_admin"; + public static string DbType = "DbType"; + public static string DbKey = "DbKey"; + + public string Conn_Admin { get; set; } + + public string AppName { get; set; } + /// + /// 主库 + /// + public string Master { get; set; } + + public string Redis { get; set; } + + public string Database { get; set; } + /// + /// 是否演示模式 + /// + public bool DemoMode { get; set; } + public MailOptions MailOptions { get; set; } + public Upload Upload { get; set; } + public ALYUN_OCS ALYUN_OCS { get; set; } + } + /// + /// 发送邮件数据配置 + /// + public class MailOptions + { + public string From { get; set; } + public string Password { get; set; } + public string Host { get; set; } + public int Port { get; set; } + } + /// + /// 上传 + /// + public class Upload + { + public string UploadDirectory { get; set; } + public string UploadUrl { get; set; } + } + /// + /// 阿里云存储 + /// + public class ALYUN_OCS + { + public string REGIONID { get; set; } + public string KEY { get; set; } + public string SECRET { get; set; } + } +} diff --git a/Infrastructure/Startups/HostBuilderExtensions.cs b/Infrastructure/Startups/HostBuilderExtensions.cs new file mode 100644 index 0000000..9f6a6ef --- /dev/null +++ b/Infrastructure/Startups/HostBuilderExtensions.cs @@ -0,0 +1,54 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Infrastructure.Startups +{ + /// + /// Program.cs里面的HostBuilder扩展 + /// + public static class HostBuilderExtensions + { + /// + /// Web 主机注入 + /// + /// Web主机构建器 + /// 外部程序集名称,如果HostingStartup存在多个程序集中可以使用;分隔,比如HostStartupLib;HostStartupLib2 + /// IWebHostBuilder + public static IWebHostBuilder Init(this IWebHostBuilder hostBuilder, string assemblyName) + { + hostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, assemblyName); + return hostBuilder; + } + + /// + /// 初始化程序扩展 + /// + /// + public static void UseAppStartup(this IWebHostBuilder hostBuilder) + { + System.Console.WriteLine("启动类执行了"); + // 自动装载配置 + hostBuilder.ConfigureAppConfiguration((hostingContext, config) => + { + // 存储环境对象 + InternalApp.HostEnvironment = (IHostEnvironment)hostingContext.HostingEnvironment; + + // 加载配置 + //InternalApp.AddConfigureFiles(config, InternalApp.WebHostEnvironment); + }); + // 自动注入 AddApp() 服务 + hostBuilder.ConfigureServices((services) => + { + // 注册 Startup 过滤器 + //services.AddTransient(); + + // 添加全局配置和存储服务提供器 + InternalApp.InternalServices = services; + + // 初始化应用服务 + //services.AddApp(); + }); + } + } +} diff --git a/Infrastructure/Startups/HostingStartup.cs b/Infrastructure/Startups/HostingStartup.cs new file mode 100644 index 0000000..fbc99f3 --- /dev/null +++ b/Infrastructure/Startups/HostingStartup.cs @@ -0,0 +1,40 @@ +using Infrastructure.Startups; +using Microsoft.AspNetCore.Hosting; + +//通过HostingStartup指定要启动的类型 +[assembly: HostingStartup(typeof(HostingStartup))] + +namespace Infrastructure.Startups +{ + public class HostingStartup : IHostingStartup + { + public void Configure(IWebHostBuilder builder) + { + System.Console.WriteLine("启动类执行了"); + // 自动装载配置 + builder.ConfigureAppConfiguration((hostingContext, config) => + { + // 存储环境对象 + //InternalApp.HostEnvironment = hostingContext.HostingEnvironment; + //InternalApp.HostEnvironment = (Microsoft.Extensions.Hosting.IHostEnvironment)hostingContext.HostingEnvironment; + // 加载配置 + //InternalApp.AddConfigureFiles(config, InternalApp.WebHostEnvironment); + InternalApp.ConfigurationBuilder = config; + }); + + // 自动注入 AddApp() 服务 + builder.ConfigureServices(services => + { + // 注册 Startup 过滤器 + //services.AddTransient(); + + // 添加全局配置和存储服务提供器 + InternalApp.InternalServices = services; + + // 初始化应用服务 + //services.AddApp(); + }); + + } + } +} diff --git a/Quartz.NET.WindowsService/App.config b/Quartz.NET.WindowsService/App.config new file mode 100644 index 0000000..1e4951b --- /dev/null +++ b/Quartz.NET.WindowsService/App.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Quartz.NET.WindowsService/Install.bat b/Quartz.NET.WindowsService/Install.bat new file mode 100644 index 0000000..5e8a60f --- /dev/null +++ b/Quartz.NET.WindowsService/Install.bat @@ -0,0 +1,5 @@ +%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil.exe G:\live_zhaorui\ZrryAdmin\ZrryAdmin_api\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe + +Net Start Quartz.Net + +sc config Quartz.Net start= auto \ No newline at end of file diff --git a/Quartz.NET.WindowsService/InstallUtil.InstallLog b/Quartz.NET.WindowsService/InstallUtil.InstallLog new file mode 100644 index 0000000..207894f --- /dev/null +++ b/Quartz.NET.WindowsService/InstallUtil.InstallLog @@ -0,0 +1,316 @@ + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +在“安装”阶段发生异常。 +System.ComponentModel.Win32Exception: 指定的服务已存在。 + +正在开始安装的“回退”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“回退”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +在“安装”阶段发生异常。 +System.ComponentModel.Win32Exception: 指定的服务已存在。 + +正在开始安装的“回退”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“回退”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +在“安装”阶段发生异常。 +System.ComponentModel.Win32Exception: 指定的服务已存在。 + +正在开始安装的“回退”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“回退”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“安装”阶段已成功完成,正在开始“提交”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“提交”阶段已成功完成。 + +已完成事务处理安装。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +在“安装”阶段发生异常。 +System.ComponentModel.Win32Exception: 指定的服务已存在。 + +正在开始安装的“回退”阶段。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“回退”阶段已成功完成。 + +已完成事务处理安装。 + + +正在开始卸载。 +查看日志文件的内容以获得 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +卸载完成。 + +正在运行事务处理安装。 + +正在开始安装的“安装”阶段。 +查看日志文件的内容以获得 G:\live_zhaorui\ZrryAdmin\ZrryAdmin_api\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 G:\live_zhaorui\ZrryAdmin\ZrryAdmin_api\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +在“安装”阶段发生异常。 +System.Security.SecurityException: 未找到源,但未能搜索某些或全部事件日志。 不可访问的日志: Security。 + +正在开始安装的“回退”阶段。 +查看日志文件的内容以获得 G:\live_zhaorui\ZrryAdmin\ZrryAdmin_api\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe 程序集的进度。 +该文件位于 G:\live_zhaorui\ZrryAdmin\ZrryAdmin_api\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.InstallLog。 + +“回退”阶段已成功完成。 + +已完成事务处理安装。 diff --git a/Quartz.NET.WindowsService/Program.cs b/Quartz.NET.WindowsService/Program.cs new file mode 100644 index 0000000..41d4d3b --- /dev/null +++ b/Quartz.NET.WindowsService/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.ServiceProcess; +using System.Text; +using System.Threading.Tasks; + +namespace Quartz.NET.WindowsService +{ + static class Program + { + /// + /// 应用程序的主入口点。 + /// + static void Main() + { + ServiceBase[] ServicesToRun; + ServicesToRun = new ServiceBase[] + { + new Service1() + }; + ServiceBase.Run(ServicesToRun); + } + } +} diff --git a/Quartz.NET.WindowsService/ProjectInstaller.Designer.cs b/Quartz.NET.WindowsService/ProjectInstaller.Designer.cs new file mode 100644 index 0000000..16c343d --- /dev/null +++ b/Quartz.NET.WindowsService/ProjectInstaller.Designer.cs @@ -0,0 +1,57 @@ +namespace Quartz.NET.WindowsService +{ + partial class ProjectInstaller + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller(); + this.Quartz定时作业 = new System.ServiceProcess.ServiceInstaller(); + // + // serviceProcessInstaller1 + // + this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalService; + this.serviceProcessInstaller1.Password = null; + this.serviceProcessInstaller1.Username = null; + // + // Quartz定时作业 + // + this.Quartz定时作业.ServiceName = "Quartz.Net"; + // + // ProjectInstaller + // + this.Installers.AddRange(new System.Configuration.Install.Installer[] { + this.serviceProcessInstaller1, + this.Quartz定时作业}); + + } + + #endregion + + private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1; + private System.ServiceProcess.ServiceInstaller Quartz定时作业; + } +} \ No newline at end of file diff --git a/Quartz.NET.WindowsService/ProjectInstaller.cs b/Quartz.NET.WindowsService/ProjectInstaller.cs new file mode 100644 index 0000000..44e798c --- /dev/null +++ b/Quartz.NET.WindowsService/ProjectInstaller.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration.Install; +using System.Linq; +using System.Threading.Tasks; + +namespace Quartz.NET.WindowsService +{ + [RunInstaller(true)] + public partial class ProjectInstaller : System.Configuration.Install.Installer + { + public ProjectInstaller() + { + InitializeComponent(); + } + } +} diff --git a/Quartz.NET.WindowsService/ProjectInstaller.resx b/Quartz.NET.WindowsService/ProjectInstaller.resx new file mode 100644 index 0000000..4cff6ae --- /dev/null +++ b/Quartz.NET.WindowsService/ProjectInstaller.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 243, 17 + + + False + + \ No newline at end of file diff --git a/Quartz.NET.WindowsService/Properties/AssemblyInfo.cs b/Quartz.NET.WindowsService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..77e1722 --- /dev/null +++ b/Quartz.NET.WindowsService/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("Quartz.NET.WindowsService")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Quartz.NET.WindowsService")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("3040dd22-663e-44cb-909b-3d0043867361")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 +// 方法是按如下所示使用“*”: : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Quartz.NET.WindowsService/Quartz.NET.WindowsService.csproj b/Quartz.NET.WindowsService/Quartz.NET.WindowsService.csproj new file mode 100644 index 0000000..8cfb182 --- /dev/null +++ b/Quartz.NET.WindowsService/Quartz.NET.WindowsService.csproj @@ -0,0 +1,108 @@ + + + + + Debug + AnyCPU + {3040DD22-663E-44CB-909B-3D0043867361} + WinExe + Quartz.NET.WindowsService + Quartz.NET.WindowsService + v4.6.1 + 512 + true + true + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + true + 0 + 1.0.0.%2a + false + true + SAK + SAK + SAK + SAK + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + Component + + + ProjectInstaller.cs + + + Component + + + Service1.cs + + + + + + + + + + + ProjectInstaller.cs + + + + + False + Microsoft .NET Framework 4.6.1 %28x86 和 x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + \ No newline at end of file diff --git a/Quartz.NET.WindowsService/Service1.Designer.cs b/Quartz.NET.WindowsService/Service1.Designer.cs new file mode 100644 index 0000000..6e8203e --- /dev/null +++ b/Quartz.NET.WindowsService/Service1.Designer.cs @@ -0,0 +1,37 @@ +namespace Quartz.NET.WindowsService +{ + partial class Service1 + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + this.ServiceName = "Service1"; + } + + #endregion + } +} diff --git a/Quartz.NET.WindowsService/Service1.cs b/Quartz.NET.WindowsService/Service1.cs new file mode 100644 index 0000000..a346aac --- /dev/null +++ b/Quartz.NET.WindowsService/Service1.cs @@ -0,0 +1,94 @@ +using System; +using System.Configuration; +using System.IO; +using System.Net; +using System.ServiceProcess; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Quartz.NET.WindowsService +{ + public partial class Service1 : ServiceBase + { + public Service1() + { + InitializeComponent(); + } + + protected override void OnStart(string[] args) + { + Task.Run(() => + { + Start(); + }); + } + private void Start() + { + string path = AppDomain.CurrentDomain.BaseDirectory + "\\log\\"; + //默认1分钟调用一次 + int interval = 1; + string message = ""; + string url = ""; + while (true) + { + try + { + ConfigurationManager.RefreshSection("appSettings"); + url = ConfigurationManager.AppSettings["url"]; + interval = Convert.ToInt32(ConfigurationManager.AppSettings["interval"]); + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + Stream responseStream = response.GetResponseStream(); + StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8); + message = streamReader.ReadToEnd(); + } + } + catch (Exception ex) + { + message= url+ ",interval:["+ interval + "],"+ ex.Message; + } + finally + { + try + { + message = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "__" + message + "\r\n"; + WriteFile(path, DateTime.Now.ToString("yyyy-MM-dd") + ".txt", message, true); + } + catch (Exception ex) + { + } + Thread.Sleep(new TimeSpan(0,interval,0)); + } + } + } + + private static void WriteFile(string path, string fileName, string content, bool appendToLast = false) + { + if (!Directory.Exists(path))//如果不存在就创建file文件夹 + { + Directory.CreateDirectory(path); + } + + using (FileStream stream = File.Open(path + fileName, FileMode.OpenOrCreate, FileAccess.Write)) + { + byte[] by = Encoding.Default.GetBytes(content); + if (appendToLast) + { + stream.Position = stream.Length; + } + else + { + stream.SetLength(0); + } + stream.Write(by, 0, by.Length); + } + } + + protected override void OnStop() + { + } + } +} diff --git a/Quartz.NET.WindowsService/Uninstall.bat b/Quartz.NET.WindowsService/Uninstall.bat new file mode 100644 index 0000000..6890df3 --- /dev/null +++ b/Quartz.NET.WindowsService/Uninstall.bat @@ -0,0 +1 @@ +%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil.exe /u E:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe \ No newline at end of file diff --git a/Quartz.NET.WindowsService/packages.config b/Quartz.NET.WindowsService/packages.config new file mode 100644 index 0000000..3dc6218 --- /dev/null +++ b/Quartz.NET.WindowsService/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Quartz.NET.WindowsService/使用说明.txt b/Quartz.NET.WindowsService/使用说明.txt new file mode 100644 index 0000000..2eda9c4 --- /dev/null +++ b/Quartz.NET.WindowsService/使用说明.txt @@ -0,0 +1,9 @@ +Èç¹ûÄãֻÊǽ«quartz.net·¢²¼µ½IIS£¬IISӦÓóػØÊպó¾Ͳ»ÄÜִÐÐÈÎÎñÁˣ¬ÄÇô¿ÉÒÔʹÓôËwindows·þÎñÆ÷¶¨ʱµ÷ÓÃquartz.netվµã + +1¡¢Ð޸ÄApp.configÖеÄurlÊôÐÔvalueֵΪÄ㵱ǰ²¿ÊðµÄquertz.netµÄվµã+/Health/index£¨עÒ⣺Èç¹ûÊDZàÒëºóµÄÏîĿ£¬һ¶¨ҪÐ޸Äbin/Debug/Quartz.NET.WindowsService.exe.configµÄÅäÖÃurl£© +2¡¢ÓüÇʱ¾´ò¿ªInstall.batÎļþÐ޸ÄE:\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe +¸ÄΪ£º(ÕâÀïÌîдÄãÏîĿËùÔÚ·¾¶)\Quartz.NET\Quartz.NET.WindowsService\bin\Debug\Quartz.NET.WindowsService.exe +3¡¢±àÒëÏîĿ +4¡¢µã»÷Install.bat°²װwindows·þÎñ + +жÔطþÎñ£ºÔËÐÐUninstall.bat(ÔËÐÐǰ°´ÉÏÃæһÑù½«Ð޸ÄQuartz.NET.WindowsService.exe·¾¶) diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c6c790 --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ +# ZRAdmin.NET + +## 🍟概述 +* 本项目适合有一定NetCore和 vue基础的开发人员 +* 基于.NET 5实现的通用权限管理平台(RBAC模式)。整合最新技术高效快速开发,前后端分离模式,开箱即用。 +* 模块化架构设计,层次清晰,业务层推荐写到单独模块,框架升级不影响业务! +* 代码量少、通俗易懂、功能强大、易扩展,轻松开发从现在开始! +* 前端采用Vue2.0、Element UI。 +* 后端采用Net5、Sqlsugar、MySQL。 +* 权限认证使用Jwt,支持多终端认证系统。 +* 支持加载动态权限菜单,多方式轻松权限控制 +* 基于若依vue分离版本(java版本)修改 + +~ 如果对您有帮助,您可以点右上角 “Star” 收藏一下 ,获取第一时间更新,谢谢!~ + +## 🍿在线体验 +体验地址:http://www.izory.cn:8080/ +管理员:admin +密 码:123456 + +**由于是个人项目,资金有限,体验服是低配,请大家爱惜,轻戳,不胜感激!!!** + +## 🥼前端技术 +Vue版前端技术栈 :基于vue、vuex、vue-router 、vue-cli 、axios 和 element-ui,前端采用vscode工具开发 + +## 🥼后端技术 +核心框架:.Net5.0 + Web API + sqlsugar + swagger + +定时计划任务:Quartz.Net组件 + +安全支持:过滤器、Sql注入、请求伪造 + +日志管理:NLog、登录日志、操作日志 + +工具类:验证码、丰富公共功能 + +## 🍄快速启动 +需要安装:VS2019(最新版)、npm或yarn(最新版) +准备工作:将document文件夹下面的admin.sql脚本导入到数据库中,修改ZRAdmin项目里面的Conn_Admin数据库连接字符串以及DbType选择对应的数据库类型,目前仅MySQL验证了 +启动后台:打开项目根目录的startup.bat即可启动(数据库默认MySQL) +启动前端:打开ZR.Vue文件夹,运行npm install命令,再运行npm run serve启动 +浏览器访问:http://localhost:8887 (默认前端端口为:8887,后台端口为:8888) + + +## 🍖内置功能 + +1. [X] 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 +2. [X] 部门管理:配置系统组织机构(公司、部门、小组),树结构展现。 +3. [X] 岗位管理:配置系统用户所属担任职务。 +4. [X] 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 +5. [X] 角色管理:角色菜单权限分配。 +6. [X] 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 +7. [ ] 参数管理:对系统动态配置常用参数。 +8. [X] 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 +9. [X] 登录日志:系统登录日志记录查询包含登录异常。 +10. [ ] 在线用户:当前系统中活跃用户状态监控。 +11. [X] 系统接口:使用swagger生成相关api接口文档。 +12. [X] 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 +13. [X] 在线构建器:拖动表单元素生成相应的VUE代码。 +14. [X] 任务系统:基于Quartz.NET定时任务执行。 +15. [X] 文章管理:可以写文章记录。 + +## 🍎演示图 + + + + + + + + + + + + + + + + + + + + + + +
+ +## 🎉优势 + +1. 前台系统不用编写登录、授权、认证模块;只负责编写业务模块即可 +2. 后台系统无需任何二次开发,直接发布即可使用 +3. 前台与后台系统分离,分别为不同的系统(域名可独立) +4. 全局异常统一处理 + +## 💐 特别鸣谢 +- 👉Ruoyi.vue:[Ruoyi](http://www.ruoyi.vip/) +- 👉SqlSugar:https://gitee.com/dotnetchina/SqlSugar + +## 🍻参与贡献 +- Fork 本项目 +- 新建 Feat_xxx 分支 +- 提交代码 +- 新建 Pull Request + +## 🎀捐赠 +如果这个项目对您有所帮助,请扫下方二维码打赏一杯咖啡。 + \ No newline at end of file diff --git a/ZR.Admin.WebApi/.config/dotnet-tools.json b/ZR.Admin.WebApi/.config/dotnet-tools.json new file mode 100644 index 0000000..c735fef --- /dev/null +++ b/ZR.Admin.WebApi/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "5.0.0", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/ZR.Admin.WebApi/Controllers/BaseController.cs b/ZR.Admin.WebApi/Controllers/BaseController.cs new file mode 100644 index 0000000..49413f1 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/BaseController.cs @@ -0,0 +1,101 @@ +using Infrastructure; +using Infrastructure.Model; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using ZR.Admin.WebApi.Filters; + +namespace ZR.Admin.WebApi.Controllers +{ + [LogActionFilter] + public class BaseController : ControllerBase + { + private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + public static string TIME_FORMAT_FULL = "yyyy-MM-dd HH:mm:ss"; + public static string TIME_FORMAT_FULL_2 = "MM-dd HH:mm:ss"; + + protected IActionResult SUCCESS(object data, string timeFormatStr = "MM-dd HH:mm:ss") + { + string jsonStr = GetJsonStr(GetApiResult(data != null ? ResultCode.SUCCESS : ResultCode.FAIL, data), timeFormatStr); + return Content(jsonStr, "application/json"); + } + + /// + /// json输出带时间格式的 + /// + /// + /// + /// + protected IActionResult OutputJson(ApiResult apiResult, string timeFormatStr = "MM-dd HH:mm:ss") + { + string jsonStr = GetJsonStr(apiResult, timeFormatStr); + + return Content(jsonStr, "application/json"); + } + protected IActionResult OutputJson(long rows, string timeFormatStr = "MM-dd HH:mm:ss") + { + string jsonStr = GetJsonStr(ToJson(rows), timeFormatStr); + + return Content(jsonStr, "application/json"); + } + protected string SerializeObject(object obj) + { + return JsonConvert.SerializeObject(obj); + } + + /// + /// 响应返回结果 + /// + /// 受影响行数 + /// + protected ApiResult ToJson(long rows) + { + return rows > 0 ? GetApiResult(ResultCode.SUCCESS) : GetApiResult(ResultCode.FAIL); + } + protected ApiResult ToJson(long rows, object data) + { + return rows > 0 ? GetApiResult(ResultCode.SUCCESS, data) : GetApiResult(ResultCode.FAIL); + } + /// + /// 全局Code使用 + /// + /// + /// + /// + protected ApiResult GetApiResult(ResultCode resultCode, object data = null) + { + var apiResult = new ApiResult((int)resultCode, resultCode.ToString()) + { + Data = data + }; + + return apiResult; + } + protected ApiResult GetApiResult(ResultCode resultCode, string msg) + { + return new ApiResult((int)resultCode, msg); + } + private static string GetJsonStr(ApiResult apiResult, string timeFormatStr) + { + if (string.IsNullOrEmpty(timeFormatStr)) + { + timeFormatStr = TIME_FORMAT_FULL; + } + var serializerSettings = new JsonSerializerSettings + { + // 设置为驼峰命名 + ContractResolver = new CamelCasePropertyNamesContractResolver(), + DateFormatString = timeFormatStr + }; + + return JsonConvert.SerializeObject(apiResult, Formatting.Indented, serializerSettings); + } + + protected IActionResult CustomError(ResultCode resultCode, string msg = "") + { + return OutputJson(GetApiResult(resultCode, msg)); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/HomeController.cs b/ZR.Admin.WebApi/Controllers/HomeController.cs new file mode 100644 index 0000000..96bb587 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/HomeController.cs @@ -0,0 +1,53 @@ +using Infrastructure; +using Infrastructure.Attribute; +using Microsoft.AspNetCore.Mvc; +using System; + +namespace ZR.Admin.WebApi.Controllers +{ + public class HomeController : BaseController + { + /// + /// 心跳 + /// + /// + [HttpGet, Route("health/index")] + [Log()] + public IActionResult Health() + { + return SUCCESS(true); + } + + /// + /// 加密 + /// + /// + /// + public IActionResult Encrypt(string content) + { + if (string.IsNullOrEmpty(content)) + { + throw new Exception("content不能为空"); + } + string key = ConfigUtils.Instance.GetConfig("DbKey"); + string encryptTxt = NETCore.Encrypt.EncryptProvider.DESEncrypt(content, key); + return Ok(new { content, encryptTxt }); + } + + /// + /// 解密 + /// + /// + /// + public IActionResult Decrypt(string content) + { + if (string.IsNullOrEmpty(content)) + { + throw new Exception("content不能为空"); + } + string key = ConfigUtils.Instance.GetConfig("DbKey"); + string encryptTxt = NETCore.Encrypt.EncryptProvider.DESDecrypt(content, key); + return Ok(new { content, encryptTxt }); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/ArticleController.cs b/ZR.Admin.WebApi/Controllers/System/ArticleController.cs new file mode 100644 index 0000000..4cd19b3 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/ArticleController.cs @@ -0,0 +1,169 @@ + +//------------------------------------------------------------------------------ +// +// 此代码已从模板生成。 +// +// 手动更改此文件可能导致应用程序出现意外的行为。 +// 如果重新生成代码,将覆盖对此文件的手动更改。 +// +//------------------------------------------------------------------------------ + +using Infrastructure.Attribute; +using Microsoft.AspNetCore.Mvc; +using ZR.Admin.WebApi.Filters; +using ZR.Model.System; +using ZR.Service.IService; +using Infrastructure.Model; +using SqlSugar; +using Mapster; +using ZR.Model.Dto.System; +using Infrastructure.Enums; +using Infrastructure; +using ZR.Admin.WebApi.Extensions; +using System.Reflection; +using System; + +namespace ZR.Admin.WebApi.Controllers +{ + /// + /// T4代码自动生成 + /// + [Verify] + [Route("article")] + public class ArticleController : BaseController + { + /// + /// 文章接口 + /// + private readonly IArticleService _ArticleService; + private readonly IArticleCategoryService _ArticleCategoryService; + + public ArticleController(IArticleService ArticleService, IArticleCategoryService articleCategoryService) + { + _ArticleService = ArticleService; + _ArticleCategoryService = articleCategoryService; + } + + /// + /// 查询文章列表 + /// + /// + [HttpGet("list")] + [ActionPermissionFilter(Permission = "system:article:list")] + public IActionResult Query([FromQuery] ArticleQueryDto parm) + { + //开始拼装查询条件 + var predicate = Expressionable.Create
(); + + //搜索条件 + predicate = predicate.AndIF(!string.IsNullOrEmpty(parm.Title), m => m.Title.Contains(parm.Title)); + predicate = predicate.AndIF(!string.IsNullOrEmpty(parm.Status), m => m.Status == parm.Status); + + var response = _ArticleService.GetPages(predicate.ToExpression(), parm, f => f.Cid, "Desc"); + + return SUCCESS(response); + } + + /// + /// 获取文章目录,前端没用到 + /// + /// + [HttpGet("CategoryList")] + public IActionResult CategoryList() + { + var response = _ArticleCategoryService.GetAll(); + return SUCCESS(response); + } + + /// + /// 获取文章目录树 + /// + /// + [HttpGet("CategoryTreeList")] + public IActionResult CategoryTreeList() + { + var response = _ArticleCategoryService.BuildCategoryTree(_ArticleCategoryService.GetAll()); + return SUCCESS(response); + } + + /// + /// 查询文章详情 + /// + /// + /// + [HttpGet("{id}")] + public IActionResult Get(int id) + { + var response = _ArticleService.GetId(id); + + return SUCCESS(response); + } + + /// + /// 添加文章 + /// + /// + [HttpPost("add")] + [ActionPermissionFilter(Permission = "system:article:add")] + [Log(Title = "文章添加", BusinessType = BusinessType.INSERT)] + public IActionResult Create([FromBody] Article parm) + { + if (parm == null) + { + throw new CustomException("请求参数错误"); + } + //从 Dto 映射到 实体 + var addModel = parm.Adapt
().ToCreate(context: HttpContext); + addModel.AuthorName = User.Identity.Name; + + return SUCCESS(_ArticleService.Add(addModel)); + } + + /// + /// 更新文章 + /// + /// + [HttpPut("edit")] + [ActionPermissionFilter(Permission = "system:article:update")] + [Log(Title = "文章修改", BusinessType = BusinessType.UPDATE)] + public IActionResult Update([FromBody] Article parm) + { + //从 Dto 映射到 实体 + var addModel = parm.Adapt
().ToCreate(context: HttpContext); + addModel.AuthorName = User.Identity.Name; + + var response = _ArticleService.Update(it => it.Cid == addModel.Cid, + f => new Article + { + Title = addModel.Title, + Content = addModel.Content, + Tags = addModel.Tags, + Category_id = addModel.Category_id, + UpdateTime = addModel.UpdateTime + }).ToCreate(); + + return SUCCESS(response); + } + + /// + /// 删除文章 + /// + /// + [HttpDelete("{id}")] + [ActionPermissionFilter(Permission = "system:article:delete")] + [Log(Title = "文章删除", BusinessType = BusinessType.DELETE)] + public IActionResult Delete(int id = 0) + { + if (id <= 0) + { + return OutputJson(ApiResult.Error($"删除失败Id 不能为空")); + } + + // 删除文章 + var response = _ArticleService.Delete(id); + + return SUCCESS(response); + } + + } +} \ No newline at end of file diff --git a/ZR.Admin.WebApi/Controllers/System/SysConfigController.cs b/ZR.Admin.WebApi/Controllers/System/SysConfigController.cs new file mode 100644 index 0000000..a29eccd --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysConfigController.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ZR.Admin.WebApi.Filters; + +namespace ZR.Admin.WebApi.Controllers.System +{ + /// + /// 配置文件 + /// + [Verify] + [Route("system/config")] + public class SysConfigController : BaseController + { + [HttpGet("list")] + public IActionResult Index() + { + return SUCCESS(1); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysDeptController.cs b/ZR.Admin.WebApi/Controllers/System/SysDeptController.cs new file mode 100644 index 0000000..8dec679 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysDeptController.cs @@ -0,0 +1,149 @@ +using Infrastructure; +using Infrastructure.Attribute; +using Infrastructure.Enums; +using Microsoft.AspNetCore.Mvc; +using System.Collections; +using ZR.Admin.WebApi.Filters; +using ZR.Common; +using ZR.Model.System; +using ZR.Service; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.System +{ + /// + /// 部门 + /// + [Verify] + [Route("system/dept")] + public class SysDeptController : BaseController + { + public ISysDeptService DeptService; + public ISysUserService UserService; + public SysDeptController(ISysDeptService deptService + , ISysUserService userService) + { + DeptService = deptService; + UserService = userService; + } + + /// + /// 获取部门列表 + /// + /// + [ActionPermissionFilter(Permission = "system:dept:list")] + [HttpGet("list")] + public IActionResult List([FromQuery] SysDept dept) + { + return SUCCESS(DeptService.GetSysDepts(dept), TIME_FORMAT_FULL); + } + + /// + /// 查询部门列表(排除节点) + /// + /// + /// + [HttpGet("list/exclude/{deptId}")] + public IActionResult ExcludeChild(long deptId) + { + var depts = DeptService.GetSysDepts(new SysDept()); + + for (int i = 0; i < depts.Count; i++) + { + SysDept d = depts[i]; + long[] deptIds = Tools.SpitLongArrary(d.Ancestors); + if (d.DeptId == deptId || ((IList)deptIds).Contains(deptId)) + { + depts.Remove(d); + } + } + return SUCCESS(depts); + } + + /// + /// 获取部门下拉树列表 + /// + /// + /// + [HttpGet("treeselect")] + public IActionResult TreeSelect(SysDept dept) + { + var depts = DeptService.GetSysDepts(dept); + + return SUCCESS(DeptService.BuildDeptTreeSelect(depts), TIME_FORMAT_FULL); + } + + /// + /// 根据部门编号获取详细信息 + /// + /// + [HttpGet("{deptId}")] + [ActionPermissionFilter(Permission = "system:dept:query")] + public IActionResult GetInfo(long deptId) + { + var info = DeptService.GetFirst(f => f.DeptId == deptId); + return SUCCESS(info); + } + + /// + /// 新增部门 + /// + /// + /// + [HttpPost] + [Log(Title = "部门管理", BusinessType = BusinessType.INSERT)] + [ActionPermissionFilter(Permission = "system:dept:add")] + public IActionResult Add([FromBody] SysDept dept) + { + if (UserConstants.NOT_UNIQUE.Equals(DeptService.CheckDeptNameUnique(dept))) + { + return OutputJson(GetApiResult(ResultCode.CUSTOM_ERROR, $"新增部门{dept.DeptName}失败,部门名称已存在")); + } + dept.Create_by = User.Identity.Name; + return OutputJson(ToJson(DeptService.InsertDept(dept))); + } + + /// + /// 修改部门 + /// + /// + /// + [HttpPut] + [Log(Title = "部门管理", BusinessType = BusinessType.UPDATE)] + [ActionPermissionFilter(Permission = "system:dept:update")] + public IActionResult Update([FromBody] SysDept dept) + { + if (UserConstants.NOT_UNIQUE.Equals(DeptService.CheckDeptNameUnique(dept))) + { + return OutputJson(GetApiResult(ResultCode.CUSTOM_ERROR, $"修改部门{dept.DeptName}失败,部门名称已存在")); + } + else if (dept.ParentId.Equals(dept.DeptId)) + { + return OutputJson(GetApiResult(ResultCode.CUSTOM_ERROR, $"修改部门{dept.DeptName}失败,上级部门不能是自己")); + } + dept.Update_by = User.Identity.Name; + return OutputJson(ToJson(DeptService.UpdateDept(dept))); + } + + /// + /// 删除部门 + /// + /// + [HttpDelete("{deptId}")] + [ActionPermissionFilter(Permission = "system:dept:remove")] + [Log(Title = "部门管理", BusinessType = BusinessType.DELETE)] + public IActionResult Remove(long deptId) + { + if (DeptService.GetCount(it => it.ParentId == deptId && it.DelFlag == "0") > 0) + { + return OutputJson(GetApiResult(ResultCode.CUSTOM_ERROR, $"存在下级部门,不允许删除")); + } + if (UserService.GetCount(it => it.DeptId == deptId && it.DelFlag == "0") > 0) + { + return OutputJson(GetApiResult(ResultCode.CUSTOM_ERROR, $"部门存在用户,不允许删除")); + } + + return SUCCESS(DeptService.Delete(deptId)); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysDictDataController.cs b/ZR.Admin.WebApi/Controllers/System/SysDictDataController.cs new file mode 100644 index 0000000..d0968af --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysDictDataController.cs @@ -0,0 +1,115 @@ +using Infrastructure.Attribute; +using Infrastructure.Enums; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System; +using ZR.Admin.WebApi.Filters; +using ZR.Model; +using ZR.Model.System; +using ZR.Model.Vo; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.System +{ + /// + /// 数据字典信息 + /// @author zr + /// + [Verify] + [Route("system/dict/data")] + public class SysDictDataController : BaseController + { + private readonly ISysDictDataService SysDictDataService; + private readonly ISysDictService SysDictService; + + public SysDictDataController(ISysDictService sysDictService, ISysDictDataService sysDictDataService) + { + SysDictService = sysDictService; + SysDictDataService = sysDictDataService; + } + + /// + /// 搜索 + /// + /// + /// + /// + [ActionPermissionFilter(Permission = "system:dict:list")] + [HttpGet("list")] + public IActionResult List([FromQuery] SysDictData dictData, [FromQuery] PagerInfo pagerInfo) + { + var list = SysDictDataService.SelectDictDataList(dictData); + pagerInfo.TotalNum = list.Count; + var vm = new VMPageResult(list, pagerInfo); + return SUCCESS(vm); + } + + /// + /// 根据字典类型查询字典数据信息 + /// + /// + /// + [AllowAnonymous] + [HttpGet("type/{dictType}")] + public IActionResult DictType(string dictType) + { + return SUCCESS(SysDictDataService.SelectDictDataByType(dictType)); + } + + /// + /// 查询字典数据详细 + /// + /// + /// + [AllowAnonymous] + [HttpGet("info/{dictCode}")] + public IActionResult GetInfo(long dictCode) + { + return SUCCESS(SysDictDataService.SelectDictDataById(dictCode)); + } + + /// + /// 添加 + /// + /// + /// + [ActionPermissionFilter(Permission = "system:dict:add")] + [Log(Title = "字典数据", BusinessType = BusinessType.INSERT)] + [HttpPost()] + public IActionResult Add([FromBody] SysDictData dict) + { + dict.Create_by = User.Identity.Name; + dict.Create_time = DateTime.Now; + return SUCCESS(SysDictDataService.InsertDictData(dict)); + } + + /// + /// 修改 + /// + /// + /// + [ActionPermissionFilter(Permission = "system:dict:edit")] + [Log(Title = "字典数据", BusinessType = BusinessType.UPDATE)] + [HttpPut()] + public IActionResult Edit([FromBody] SysDictData dict) + { + dict.Update_by = User.Identity.Name; + return SUCCESS(SysDictDataService.UpdateDictData(dict)); + } + + /// + /// 删除字典类型 + /// + /// + /// + [ActionPermissionFilter(Permission = "system:dict:remove")] + [Log(Title = "字典类型", BusinessType = BusinessType.DELETE)] + [HttpDelete("{dictCode}")] + public IActionResult Remove(string dictCode) + { + long[] dictCodes = ZR.Common.Tools.SpitLongArrary(dictCode); + + return SUCCESS(SysDictDataService.DeleteDictDataByIds(dictCodes)); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysDictTypeController.cs b/ZR.Admin.WebApi/Controllers/System/SysDictTypeController.cs new file mode 100644 index 0000000..d9c1289 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysDictTypeController.cs @@ -0,0 +1,111 @@ +using Infrastructure.Attribute; +using Infrastructure.Enums; +using Infrastructure.Model; +using Microsoft.AspNetCore.Mvc; +using System; +using ZR.Admin.WebApi.Filters; +using ZR.Common; +using ZR.Model; +using ZR.Model.System; +using ZR.Model.Vo; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.System +{ + /// + /// 数据字典信息 + /// + [Verify] + [Route("system/dict/type")] + public class SysDictTypeController : BaseController + { + private readonly ISysDictService SysDictService; + + public SysDictTypeController(ISysDictService sysDictService) + { + SysDictService = sysDictService; + } + + /// + /// 查询 + /// + /// + /// + /// + [ActionPermissionFilter(Permission = "system:dict:list")] + [HttpGet("list")] + public IActionResult List([FromQuery] SysDictType dict, [FromQuery] PagerInfo pagerInfo) + { + var list = SysDictService.SelectDictTypeList(dict); + pagerInfo.TotalNum = list.Count; + var vm = new VMPageResult(list, pagerInfo); + return SUCCESS(vm, TIME_FORMAT_FULL); + } + + /// + /// 查询字典类型详细 + /// + /// + /// + [HttpGet("{dictId}")] + [ActionPermissionFilter(Permission = "system:dict:query")] + public IActionResult GetInfo(long dictId = 0) + { + return SUCCESS(SysDictService.SelectDictTypeById(dictId)); + } + + /// + /// 添加字典类型 + /// + /// + /// + [ActionPermissionFilter(Permission = "system:dict:add")] + [Log(Title = "字典操作", BusinessType = BusinessType.INSERT)] + [HttpPost("edit")] + public IActionResult Add([FromBody] SysDictType dict) + { + if (UserConstants.NOT_UNIQUE.Equals(SysDictService.CheckDictTypeUnique(dict))) + { + return OutputJson(ApiResult.Error($"新增字典'{dict.DictName}'失败,字典类型已存在")); + } + //设置添加人 + dict.Create_by = HttpContext.User.Identity.Name; + dict.Create_time = DateTime.Now; + return SUCCESS(SysDictService.InsertDictType(dict)); + } + + /// + /// 修改字典类型 + /// + /// + /// + [ActionPermissionFilter(Permission = "system:dict:edit")] + [Log(Title = "字典操作", BusinessType = BusinessType.UPDATE)] + [Route("edit")] + [HttpPut] + public IActionResult Edit([FromBody] SysDictType dict) + { + if (UserConstants.NOT_UNIQUE.Equals(SysDictService.CheckDictTypeUnique(dict))) + { + return OutputJson(ApiResult.Error($"修改字典'{dict.DictName}'失败,字典类型已存在")); + } + //设置添加人 + dict.Update_by = HttpContext.User.Identity.Name; + return SUCCESS(SysDictService.UpdateDictType(dict)); + } + + /// + /// 删除字典类型 + /// + /// + [ActionPermissionFilter(Permission = "system:dict:remove")] + [Log(Title = "删除字典类型", BusinessType = BusinessType.DELETE)] + [HttpDelete("{ids}")] + public IActionResult Remove(string ids) + { + long[] idss = Tools.SpitLongArrary(ids); + + return SUCCESS(SysDictService.DeleteDictTypeByIds(idss)); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysLoginController.cs b/ZR.Admin.WebApi/Controllers/System/SysLoginController.cs new file mode 100644 index 0000000..b363ab8 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysLoginController.cs @@ -0,0 +1,148 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using ZR.Admin.WebApi.Extensions; +using ZR.Admin.WebApi.Filters; +using ZR.Admin.WebApi.Framework; +using Infrastructure.Model; +using Infrastructure; +using Infrastructure.Attribute; +using ZR.Model.System; +using ZR.Model.Dto.System; +using ZR.Service.IService; +using Hei.Captcha; +using ZR.Common; + +namespace ZR.Admin.WebApi.Controllers.System +{ + /// + /// 登录 + /// + public class SysLoginController : BaseController + { + static readonly NLog.Logger logger = NLog.LogManager.GetLogger("LoginController"); + private readonly IHttpContextAccessor httpContextAccessor; + private readonly ISysUserService sysUserService; + private readonly ISysMenuService sysMenuService; + private readonly ISysLoginService sysLoginService; + private readonly ISysPermissionService permissionService; + private readonly SecurityCodeHelper SecurityCodeHelper; + + public SysLoginController( + IHttpContextAccessor contextAccessor, + ISysMenuService sysMenuService, + ISysUserService sysUserService, + ISysLoginService sysLoginService, + ISysPermissionService permissionService, + SecurityCodeHelper captcha) + { + httpContextAccessor = contextAccessor; + SecurityCodeHelper = captcha; + this.sysMenuService = sysMenuService; + this.sysUserService = sysUserService; + this.sysLoginService = sysLoginService; + this.permissionService = permissionService; + } + + + /// + /// 登录 + /// + /// 登录对象 + /// + [Route("login")] + [HttpPost] + [Log(Title = "登录")] + public IActionResult Login([FromBody] LoginBodyDto loginBody) + { + if (loginBody == null) { throw new CustomException("请求参数错误"); } + loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext); + if (CacheHelper.Get(loginBody.Uuid) is string str && !str.ToLower().Equals(loginBody.Code.ToLower())) + { + throw new CustomException(ResultCode.CAPTCHA_ERROR, "验证码错误"); + } + + var user = sysLoginService.Login(loginBody, AsyncFactory.RecordLogInfo(httpContextAccessor.HttpContext, "0", "login")); + #region 存入cookie Action校验权限使用 + //角色集合 eg: admin,yunying,common + List roles = permissionService.GetRolePermission(user); + //权限集合 eg *:*:*,system:user:list + List permissions = permissionService.GetMenuPermission(user); + #endregion + LoginUser loginUser = new LoginUser(user.UserId, user.UserName, roles, permissions); + + return SUCCESS(JwtUtil.GenerateJwtToken(HttpContext.WriteCookies(loginUser))); + } + + /// + /// 注销 + /// + /// + [Log(Title = "注销")] + [HttpPost("logout")] + public IActionResult LogOut() + { + Task.Run(async () => + { + //注销登录的用户,相当于ASP.NET中的FormsAuthentication.SignOut + await HttpContext.SignOutAsync(); + }).Wait(); + + return SUCCESS(1); + } + + /// + /// 获取用户信息 + /// + /// + [Verify] + [HttpGet("getInfo")] + public IActionResult GetUserInfo() + { + long userid = HttpContext.GetUId(); + var user = sysUserService.SelectUserById(userid); + + //前端校验按钮权限使用 + //角色集合 eg: admin,yunying,common + List roles = permissionService.GetRolePermission(user); + //权限集合 eg *:*:*,system:user:list + List permissions = permissionService.GetMenuPermission(user); + + return SUCCESS(new { user, roles, permissions }); + } + + /// + /// 获取路由信息 + /// + /// + [Verify] + [HttpGet("getRouters")] + public IActionResult GetRouters() + { + long uid = HttpContext.GetUId(); + var menus = sysMenuService.SelectMenuTreeByUserId(uid); + + return OutputJson(ToJson(1, sysMenuService.BuildMenus(menus))); + } + + /// + /// 生成图片验证码 + /// + /// + [HttpGet("captchaImage")] + public ApiResult CaptchaImage() + { + string uuid = Guid.NewGuid().ToString().Replace("-", ""); + var code = SecurityCodeHelper.GetRandomEnDigitalText(4); + var imgByte = SecurityCodeHelper.GetGifEnDigitalCodeByte(code); + string base64Str = Convert.ToBase64String(imgByte); + CacheHelper.SetCache(uuid, code); + var obj = new { uuid, img = base64Str };// File(stream, "image/png") + + return ToJson(1, obj); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysMenuController.cs b/ZR.Admin.WebApi/Controllers/System/SysMenuController.cs new file mode 100644 index 0000000..27dd846 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysMenuController.cs @@ -0,0 +1,181 @@ +using Infrastructure; +using Infrastructure.Attribute; +using Infrastructure.Enums; +using Infrastructure.Model; +using Microsoft.AspNetCore.Mvc; +using ZR.Admin.WebApi.Extensions; +using ZR.Admin.WebApi.Filters; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Service; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.System +{ + [Verify] + [Route("/system/menu")] + public class SysMenuController : BaseController + { + private readonly ISysRoleService sysRoleService; + private readonly ISysMenuService sysMenuService; + + public SysMenuController( + ISysRoleService sysRoleService, + ISysMenuService sysMenuService) + { + this.sysRoleService = sysRoleService; + this.sysMenuService = sysMenuService; + } + + /// + /// 获取菜单列表 √ + /// + /// + [ActionPermissionFilter(Permission = "system:menu:list")] + [HttpGet("list")] + public IActionResult MenuList([FromQuery] SysMenu menu) + { + long userId = HttpContext.GetUId(); + return SUCCESS(sysMenuService.SelectMenuList(menu, userId), "yyyy-MM-dd HH:mm:ss"); + } + + /// + /// 根据菜单编号获取详细信息 √ + /// + /// + /// + [HttpGet("{menuId}")] + [ActionPermissionFilter(Permission = "system:menu:query")] + public IActionResult GetMenuInfo(int menuId = 0) + { + return SUCCESS(sysMenuService.GetMenuByMenuId(menuId), "yyyy-MM-dd HH:mm:ss"); + } + + /// + /// 获取菜单下拉树列表(分配角色所需菜单) + /// + /// + [HttpGet("treeSelect")] + public IActionResult TreeSelect() + { + long userId = HttpContext.GetUId(); + var list = sysMenuService.SelectMenuList(new SysMenu(), userId).FindAll(f => f.visible == "0"); + var treeMenus = sysMenuService.BuildMenuTreeSelect(list); + + return SUCCESS(treeMenus); + } + + /// + /// 获取角色菜单信息 + /// 加载对应角色菜单列表树 + /// + /// + /// + [HttpGet("roleMenuTreeselect/{roleId}")] + public IActionResult RoleMenuTreeselect(int roleId) + { + long userId = HttpContext.GetUId(); + var menus = sysMenuService.SelectMenuList(userId); + var checkedKeys = sysRoleService.SelectUserRoleMenus(roleId); + return SUCCESS(new + { + checkedKeys, + menus = sysMenuService.BuildMenuTreeSelect(menus), + }); + } + + /// + /// 修改菜单 √ + /// + /// + /// + [HttpPost("edit")] + [Log(Title = "菜单管理", BusinessType = BusinessType.UPDATE)] + [ActionPermissionFilter(Permission = "system:menu:edit")] + public IActionResult MenuEdit([FromBody] SysMenu MenuDto) + { + if (MenuDto == null) { return OutputJson(ApiResult.Error(101, "请求参数错误")); } + //if (UserConstants.NOT_UNIQUE.Equals(sysMenuService.CheckMenuNameUnique(MenuDto))) + //{ + // return OutputJson(ApiResult.Error($"修改菜单'{MenuDto.menuName}'失败,菜单名称已存在")); + //} + if (UserConstants.YES_FRAME.Equals(MenuDto.isFrame) && !MenuDto.path.StartsWith("http")) + { + return OutputJson(ApiResult.Error($"修改菜单'{MenuDto.menuName}'失败,地址必须以http(s)://开头")); + } + if (MenuDto.menuId.Equals(MenuDto.parentId)) + { + return OutputJson(ApiResult.Error($"修改菜单'{MenuDto.menuName}'失败,上级菜单不能选择自己")); + } + MenuDto.Update_by = User.Identity.Name; + int result = sysMenuService.EditMenu(MenuDto); + + return OutputJson(result); + } + + /// + /// 添加菜单 √ + /// + /// + /// + [HttpPut("add")] + [Log(Title = "菜单管理", BusinessType = BusinessType.INSERT)] + [ActionPermissionFilter(Permission = "system:menu:add")] + public IActionResult MenuAdd([FromBody] SysMenu MenuDto) + { + if (MenuDto == null) { return OutputJson(ApiResult.Error(101, "请求参数错误")); } + if (UserConstants.NOT_UNIQUE.Equals(sysMenuService.CheckMenuNameUnique(MenuDto))) + { + return OutputJson(ApiResult.Error($"新增菜单'{MenuDto.menuName}'失败,菜单名称已存在")); + } + if (UserConstants.YES_FRAME.Equals(MenuDto.isFrame) && !MenuDto.path.StartsWith("http")) + { + return OutputJson(ApiResult.Error($"新增菜单'{MenuDto.menuName}'失败,地址必须以http(s)://开头")); + } + + MenuDto.Create_by = User.Identity.Name; + int result = sysMenuService.AddMenu(MenuDto); + + return OutputJson(result); + } + + /// + /// 菜单删除 √ + /// + /// + /// + [HttpDelete("{menuId}")] + [Log(Title = "菜单管理", BusinessType = BusinessType.DELETE)] + [ActionPermissionFilter(Permission = "system:menu:remove")] + public IActionResult Remove(int menuId = 0) + { + if (sysMenuService.HasChildByMenuId(menuId)) + { + return CustomError(ResultCode.CUSTOM_ERROR, "存在子菜单,不允许删除"); + } + if (sysMenuService.CheckMenuExistRole(menuId)) + { + return CustomError(ResultCode.CUSTOM_ERROR, "菜单已分配,不允许删除"); + } + int result = sysMenuService.DeleteMenuById(menuId); + + return OutputJson(result); + } + + /// + /// 保存排序 + /// + /// + /// + [ActionPermissionFilter(Permission = "system:menu:update")] + [HttpPost("ChangeSort")] + [Log(Title = "保存排序", BusinessType = BusinessType.UPDATE)] + public IActionResult ChangeSort([FromBody] MenuDto MenuDto) + { + if (MenuDto == null) { return OutputJson(ApiResult.Error(101, "请求参数错误")); } + + int result = sysMenuService.ChangeSortMenu(MenuDto); + return OutputJson(result); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysNoticeController.cs b/ZR.Admin.WebApi/Controllers/System/SysNoticeController.cs new file mode 100644 index 0000000..4608c15 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysNoticeController.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ZR.Admin.WebApi.Controllers.System +{ + [Route("system/notice")] + public class SysNoticeController : BaseController + { + [HttpGet("list")] + public IActionResult Index() + { + return SUCCESS(null); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysPostController.cs b/ZR.Admin.WebApi/Controllers/System/SysPostController.cs new file mode 100644 index 0000000..477ee60 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysPostController.cs @@ -0,0 +1,123 @@ +using Microsoft.AspNetCore.Mvc; +using SqlSugar; +using System.Collections.Generic; +using ZR.Admin.WebApi.Filters; +using ZR.Model; +using ZR.Model.System; +using Infrastructure.Extensions; +using Infrastructure.Attribute; +using Infrastructure.Enums; +using Infrastructure; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.System +{ + /// + /// 岗位管理 + /// + [Verify] + [Route("system/post")] + public class SysPostController : BaseController + { + private readonly ISysPostService PostService; + public SysPostController(ISysPostService postService) + { + PostService = postService; + } + + /// + /// 岗位列表查询 + /// + /// + [HttpGet("list")] + [ActionPermissionFilter(Permission = "system:post:list")] + public IActionResult List([FromQuery] SysPost post, [FromQuery] PagerInfo pagerInfo) + { + //开始拼装查询条件 + var predicate = Expressionable.Create(); + predicate = predicate.AndIF(post.Status.IfNotEmpty(), it => it.Status == post.Status); + var list = PostService.GetPages(predicate.ToExpression(), pagerInfo); + + return SUCCESS(list); + } + + /// + /// 岗位查询 + /// + /// + /// + [HttpGet("{postId}")] + [ActionPermissionFilter(Permission = "system:post:query")] + public IActionResult Query(long postId = 0) + { + return SUCCESS(PostService.GetId(postId)); + } + + /// + /// 岗位管理 + /// + /// + /// + [HttpPost] + [ActionPermissionFilter(Permission = "system:post:add")] + [Log(Title = "岗位添加", BusinessType = BusinessType.INSERT)] + public IActionResult Add([FromBody] SysPost post) + { + if (UserConstants.NOT_UNIQUE.Equals(PostService.CheckPostNameUnique(post))) + { + throw new CustomException($"修改岗位{post.PostName}失败,岗位名已存在"); + } + if (UserConstants.NOT_UNIQUE.Equals(PostService.CheckPostCodeUnique(post))) + { + throw new CustomException($"修改岗位{post.PostName}失败,岗位编码已存在"); + } + post.Update_by = User.Identity.Name; + return OutputJson(ToJson(PostService.Add(post))); + } + + /// + /// 岗位管理 + /// + /// + /// + [HttpPut] + [ActionPermissionFilter(Permission = "system:post:edit")] + [Log(Title = "岗位编辑", BusinessType = BusinessType.UPDATE)] + public IActionResult Update([FromBody] SysPost post) + { + if (UserConstants.NOT_UNIQUE.Equals(PostService.CheckPostNameUnique(post))) + { + throw new CustomException($"修改岗位{post.PostName}失败,岗位名已存在"); + } + if (UserConstants.NOT_UNIQUE.Equals(PostService.CheckPostCodeUnique(post))) + { + throw new CustomException($"修改岗位{post.PostName}失败,岗位编码已存在"); + } + post.Update_by = User.Identity.Name; + return OutputJson(ToJson(PostService.Update(post))); + } + + /// + /// 岗位删除 + /// + /// + /// + [HttpDelete("{id}")] + [ActionPermissionFilter(Permission = "system:post:remove")] + [Log(Title = "岗位删除", BusinessType = BusinessType.DELETE)] + public IActionResult Delete(int id = 0) + { + return OutputJson(ToJson(PostService.Delete(id))); + } + + /// + /// 获取岗位选择框列表 + /// + [HttpGet("optionselect")] + public IActionResult Optionselect() + { + List posts = PostService.GetAll(); + return SUCCESS(posts); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysProfileController.cs b/ZR.Admin.WebApi/Controllers/System/SysProfileController.cs new file mode 100644 index 0000000..d1e2172 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysProfileController.cs @@ -0,0 +1,155 @@ +using Infrastructure; +using Infrastructure.Attribute; +using Infrastructure.Enums; +using Infrastructure.Model; +using Mapster; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using System; +using System.IO; +using ZR.Admin.WebApi.Extensions; +using ZR.Admin.WebApi.Filters; +using ZR.Common; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.System +{ + [Verify] + [Route("system/user/profile")] + public class SysProfileController : BaseController + { + private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + private readonly ISysUserService UserService; + private readonly ISysRoleService RoleService; + private readonly ISysUserPostService UserPostService; + private readonly ISysDeptService DeptService; + private OptionsSetting OptionsSetting; + private IWebHostEnvironment hostEnvironment; + + public SysProfileController( + ISysUserService userService, + ISysRoleService roleService, + ISysUserPostService postService, + ISysDeptService deptService, + IOptions options, + IWebHostEnvironment hostEnvironment) + { + UserService = userService; + RoleService = roleService; + UserPostService = postService; + DeptService = deptService; + OptionsSetting = options.Value; + this.hostEnvironment = hostEnvironment; + } + + /// + /// 个人中心用户信息获取 + /// + /// + [HttpGet] + public IActionResult Profile() + { + long userId = HttpContext.GetUId(); + var user = UserService.SelectUserById(userId); + + user.Roles = RoleService.SelectUserRoleListByUserId(userId); + var roles = RoleService.SelectUserRoleNames(userId); + var postGroup = UserPostService.GetPostsStrByUserId(userId); + var deptInfo = DeptService.GetFirst(f => f.DeptId == user.DeptId); + user.DeptName = deptInfo?.DeptName; + + return SUCCESS(new { user, roles, postGroup }, TIME_FORMAT_FULL); + } + + /// + /// 修改用户 + /// + /// + [HttpPut] + [ActionPermissionFilter(Permission = "system")] + [Log(Title = "修改信息", BusinessType = BusinessType.UPDATE)] + public IActionResult UpdateProfile([FromBody] SysUserDto userDto) + { + if (userDto == null) + { + throw new CustomException(ResultCode.PARAM_ERROR, "请求参数错误"); + } + //从 Dto 映射到 实体 + var user = userDto.Adapt(); + user.Update_by = User.Identity.Name; + user.Update_time = DateTime.Now; + + int result = UserService.ChangeUser(user); + return OutputJson(result); + } + + /// + /// 修改密码 + /// + /// + [HttpPut("updatePwd")] + [ActionPermissionFilter(Permission = "system")] + [Log(Title = "修改密码", BusinessType = BusinessType.UPDATE)] + public IActionResult UpdatePwd(string oldPassword, string newPassword) + { + LoginUser loginUser = Framework.JwtUtil.GetLoginUser(HttpContext); + + SysUser user = UserService.SelectUserById(loginUser.UserId); + string oldMd5 = NETCore.Encrypt.EncryptProvider.Md5(oldPassword); + string newMd5 = NETCore.Encrypt.EncryptProvider.Md5(newPassword); + if (user.Password != oldMd5) + { + return OutputJson(ApiResult.Error("修改密码失败,旧密码错误")); + } + if (user.Password == newMd5) + { + return OutputJson(ApiResult.Error("新密码不能和旧密码相同")); + } + if (UserService.ResetPwd(loginUser.UserId, newMd5) > 0) + { + //TODO 更新缓存 + + return SUCCESS(1); + } + + return OutputJson(ApiResult.Error("修改密码异常,请联系管理员")); + } + + /// + /// 修改头像 + /// + /// + [HttpPost("Avatar")] + [ActionPermissionFilter(Permission = "system")] + //[Log(Title = "修改头像", BusinessType = BusinessType.UPDATE)] + public IActionResult Avatar([FromForm(Name = "picture")] IFormFile formFile) + { + LoginUser loginUser = Framework.JwtUtil.GetLoginUser(HttpContext); + + if (formFile == null) throw new CustomException("请选择文件"); + + string fileExt = Path.GetExtension(formFile.FileName); + string savePath = Path.Combine("wwwroot", FileUtil.GetdirPath("uploads")); + + Console.WriteLine(savePath); + if (!Directory.Exists(savePath)) { Directory.CreateDirectory(savePath); } + + string fileName = FileUtil.HashFileName() + fileExt; + string finalFilePath = savePath + fileName; + + using (var stream = new FileStream(finalFilePath, FileMode.Create)) + { + formFile.CopyToAsync(stream); + } + string accessUrl = $"{OptionsSetting.Upload.UploadUrl}/{FileUtil.GetdirPath("uploads")}{fileName}"; + + UserService.UpdatePhoto(new SysUser() { Avatar = accessUrl, UserId = loginUser.UserId }); + logger.Info("修改头像:" + accessUrl); + return SUCCESS(new { imgUrl = accessUrl }); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysRoleController.cs b/ZR.Admin.WebApi/Controllers/System/SysRoleController.cs new file mode 100644 index 0000000..1b92d24 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysRoleController.cs @@ -0,0 +1,172 @@ +using Infrastructure; +using Infrastructure.Attribute; +using Infrastructure.Enums; +using Infrastructure.Model; +using Microsoft.AspNetCore.Mvc; +using ZR.Common; +using ZR.Admin.WebApi.Filters; +using ZR.Model; +using ZR.Model.System; +using ZR.Model.Vo; +using ZR.Service.IService; +using ZR.Admin.WebApi.Extensions; + +namespace ZR.Admin.WebApi.Controllers.System +{ + /// + /// 角色信息 + /// + [Verify] + [Route("system/role")] + public class SysRoleController : BaseController + { + readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); + + private readonly ISysRoleService sysRoleService; + + public SysRoleController( + ISysRoleService sysRoleService) + { + this.sysRoleService = sysRoleService; + } + + /// + /// 获取系统角色管理 + /// + /// + [ActionPermissionFilter(Permission = "system:role:list")] + [HttpGet("list")] + public IActionResult List([FromQuery] SysRole role, [FromQuery] PagerInfo pager) + { + var list = sysRoleService.SelectRoleList(role); + + VMPageResult vMPageResult = new(list, pager); + + return SUCCESS(vMPageResult, TIME_FORMAT_FULL); + } + + /// + /// 根据角色编号获取详细信息 + /// + /// + /// + [HttpGet("{roleId}")] + public IActionResult GetInfo(long roleId = 0) + { + var info = sysRoleService.SelectRoleById(roleId); + + return SUCCESS(info, TIME_FORMAT_FULL); + } + + /// + /// 添加角色 √ + /// + /// + /// + [HttpPost] + [ActionPermissionFilter(Permission = "system:role:add")] + [Log(Title = "角色管理", BusinessType = BusinessType.INSERT)] + [Route("edit")] + public IActionResult RoleAdd([FromBody] SysRole sysRoleDto) + { + if (sysRoleDto == null) return OutputJson(ApiResult.Error(101, "请求参数错误")); + + if (UserConstants.NOT_UNIQUE.Equals(sysRoleService.CheckRoleKeyUnique(sysRoleDto))) + { + return OutputJson(ApiResult.Error((int)ResultCode.CUSTOM_ERROR, $"新增角色'{sysRoleDto.RoleName}'失败,角色权限已存在")); + } + + sysRoleDto.Create_by = User.Identity.Name; + long roleId = sysRoleService.InsertRole(sysRoleDto); + + return OutputJson(ToJson(roleId)); + } + + /// + /// 修改角色 √ + /// + /// + /// + [HttpPut] + [ActionPermissionFilter(Permission = "system:role:edit")] + [Log(Title = "角色管理", BusinessType = BusinessType.UPDATE)] + [Route("edit")] + public IActionResult RoleEdit([FromBody] SysRole sysRoleDto) + { + if (sysRoleDto == null || sysRoleDto.RoleId <= 0 || string.IsNullOrEmpty(sysRoleDto.RoleKey)) + { + return OutputJson(ApiResult.Error(101, "请求参数错误")); + } + sysRoleService.CheckRoleAllowed(sysRoleDto); + var info = sysRoleService.SelectRoleById(sysRoleDto.RoleId); + if (info != null && info.RoleKey != sysRoleDto.RoleKey) + { + if (UserConstants.NOT_UNIQUE.Equals(sysRoleService.CheckRoleKeyUnique(sysRoleDto))) + { + return OutputJson(ApiResult.Error($"编辑角色'{sysRoleDto.RoleName}'失败,角色权限已存在")); + } + } + + sysRoleDto.Update_by = User.Identity.Name; + int upResult = sysRoleService.UpdateRole(sysRoleDto); + if (upResult > 0) + { + //TODO 更新缓存用户权限信息 + + return SUCCESS(upResult); + } + return OutputJson(ApiResult.Error($"修改角色'{sysRoleDto.RoleName}'失败,请联系管理员")); + } + + /// + /// 根据角色分配菜单 + /// + /// + /// + [HttpPut("dataScope")] + [ActionPermissionFilter(Permission = "system:role:update")] + [Log(Title = "角色管理", BusinessType = BusinessType.UPDATE)] + public IActionResult DataScope([FromBody] SysRole sysRoleDto) + { + if (sysRoleDto == null || sysRoleDto.RoleId <= 0) return OutputJson(ApiResult.Error(101, "请求参数错误")); + + sysRoleDto.Create_by = HttpContextExtension.GetName(HttpContext); + //删除角色菜单 + sysRoleService.DeleteRoleMenuByRoleId(sysRoleDto.RoleId); + sysRoleService.InsertRoleMenu(sysRoleDto); + + return SUCCESS(true); + } + + /// + /// 角色删除 √ + /// + /// + /// + [HttpDelete("{roleId}")] + [Log(Title = "角色管理", BusinessType = BusinessType.DELETE)] + [ActionPermissionFilter(Permission = "system:role:remove")] + public IActionResult Remove(string roleId) + { + long[] roleIds = Tools.SpitLongArrary(roleId); + int result = sysRoleService.DeleteRoleByRoleId(roleIds); + + return OutputJson(ToJson(result)); + } + + /// + /// 修改角色状态 √ + /// + /// 角色对象 + /// + [HttpPut("changeStatus")] + [Log(Title = "修改角色状态", BusinessType = BusinessType.UPDATE)] + [ActionPermissionFilter(Permission = "system:role:update")] + public IActionResult ChangeStatus([FromBody] SysRole roleDto) + { + int result = sysRoleService.UpdateRoleStatus(roleDto); + + return OutputJson(ToJson(result)); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysUserController.cs b/ZR.Admin.WebApi/Controllers/System/SysUserController.cs new file mode 100644 index 0000000..649738f --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysUserController.cs @@ -0,0 +1,208 @@ +using Infrastructure.Attribute; +using Infrastructure.Enums; +using Infrastructure.Model; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Npoi.Mapper; +using System.Collections.Generic; +using System.IO; +using ZR.Admin.WebApi.Filters; +using ZR.Model; +using ZR.Model.System; +using ZR.Model.Vo; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.System +{ + [Verify] + [Route("system/user")] + public class SysUserController : BaseController + { + private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + private readonly ISysUserService UserService; + private readonly ISysRoleService RoleService; + private readonly ISysPostService PostService; + private readonly ISysUserPostService UserPostService; + + public SysUserController( + ISysUserService userService, + ISysRoleService roleService, + ISysPostService postService, + ISysUserPostService userPostService) + { + UserService = userService; + RoleService = roleService; + PostService = postService; + UserPostService = userPostService; + } + + /// + /// 用户管理 -> 获取用户 + /// /system/user/list + /// + /// + [ActionPermissionFilter(Permission = "system:user:list")] + [HttpGet("list")] + public IActionResult List([FromQuery] SysUser user, PagerInfo pager) + { + var list = UserService.SelectUserList(user, pager); + + var vm = new VMPageResult(list, pager); + + return SUCCESS(vm, TIME_FORMAT_FULL); + } + + /// + /// 用户管理 -> 编辑、添加用户获取用户,信息查询 + /// + /// + /// + [HttpGet("")] + [HttpGet("{userId:int=0}")] + public IActionResult GetInfo(int userId) + { + Dictionary dic = new(); + var roles = RoleService.SelectRoleAll(); + dic.Add("roles", roles); + dic.Add("posts", PostService.GetAll()); + + //编辑 + if (userId > 0) + { + dic.Add("user", UserService.SelectUserById(userId)); + dic.Add("postIds", UserPostService.GetUserPostsByUserId(userId)); + dic.Add("roleIds", RoleService.SelectUserRoles(userId)); + } + + return OutputJson(ApiResult.Success(dic)); + } + + /// + /// 添加用户 + /// + /// + /// + [HttpPost("edit")] + [Log(Title = "用户管理", BusinessType = BusinessType.INSERT)] + [ActionPermissionFilter(Permission = "system:user:add")] + public IActionResult AddUser([FromBody] SysUser user) + { + if (user == null) { return OutputJson(ApiResult.Error(101, "请求参数错误")); } + if (UserConstants.NOT_UNIQUE.Equals(UserService.CheckUserNameUnique(user.UserName))) + { + return OutputJson(ApiResult.Error($"新增用户 '{user.UserName}'失败,登录账号已存在")); + } + + user.Create_by = User.Identity.Name; + user.Password = NETCore.Encrypt.EncryptProvider.Md5(user.Password); + + return OutputJson(UserService.InsertUser(user)); + } + + /// + /// 修改用户 + /// + /// + /// + [HttpPut("edit")] + [Log(Title = "用户管理", BusinessType = BusinessType.UPDATE)] + [ActionPermissionFilter(Permission = "system:user:edit")] + public IActionResult UpdateUser([FromBody] SysUser user) + { + if (user == null || user.UserId <= 0) { return OutputJson(ApiResult.Error(101, "请求参数错误")); } + + user.Update_by = User.Identity.Name; + int upResult = UserService.UpdateUser(user); + + return OutputJson(upResult); + } + + /// + /// 改变用户状态 + /// + /// + /// + [HttpPut("changeStatus")] + [Log(Title = "修改用户状态", BusinessType = BusinessType.UPDATE)] + [ActionPermissionFilter(Permission = "system:user:update")] + public IActionResult ChangeStatus([FromBody] SysUser user) + { + if (user == null) { return OutputJson(ApiResult.Error(101, "请求参数错误")); } + + int result = UserService.ChangeUserStatus(user); + return OutputJson(ToJson(result)); + } + + /// + /// 删除用户 + /// + /// + /// + [HttpDelete("{userId}")] + [Log(Title = "用户管理", BusinessType = BusinessType.DELETE)] + [ActionPermissionFilter(Permission = "system:user:remove")] + public IActionResult Remove(int userid = 0) + { + if (userid <= 0) { return OutputJson(ApiResult.Error(101, "请求参数错误")); } + + int result = UserService.DeleteUser(userid); + + return OutputJson(ToJson(result)); + } + + /// + /// 重置密码 + /// + /// + [HttpPut("resetPwd")] + [Log(Title = "重置密码", BusinessType = BusinessType.UPDATE)] + [ActionPermissionFilter(Permission = "system:user:update")] + public IActionResult ResetPwd([FromBody] SysUser sysUser) + { + //密码md5 + sysUser.Password = NETCore.Encrypt.EncryptProvider.Md5(sysUser.Password); + + int result = UserService.ResetPwd(sysUser.UserId, sysUser.Password); + return OutputJson(ToJson(result)); + } + + /// + /// 导入 ok + /// + /// 使用IFromFile必须使用name属性否则获取不到文件 + /// + [HttpPost("importData")] + [Log(Title = "用户导入", BusinessType = BusinessType.IMPORT)] + [ActionPermissionFilter(Permission = "system:user:import")] + public IActionResult ImportData([FromForm(Name = "file")] IFormFile formFile) + { + var mapper = new Mapper(formFile.OpenReadStream());// 从流获取 + //读取的sheet信息 + var rows = mapper.Take(0); + foreach (var item in rows) + { + SysUser u = item.Value; + } + //TODO 业务逻辑 + return SUCCESS(1); + } + + /// + /// 用户模板 ok + /// + /// + [HttpGet("importTemplate")] + [Log(Title = "用户模板", BusinessType = BusinessType.EXPORT)] + [ActionPermissionFilter(Permission = "system:user:export")] + public IActionResult ImportTemplateExcel() + { + List user = new List(); + var mapper = new Mapper(); + MemoryStream stream = new MemoryStream(); + mapper.Save(stream, user, "sheel1", overwrite: true, xlsx: true); + //Response.Headers.Append("content-disposition", "attachment;filename=sysUser.xlsx"); + return File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "sysUser.xlsx"); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysUserRoleController.cs b/ZR.Admin.WebApi/Controllers/System/SysUserRoleController.cs new file mode 100644 index 0000000..a801832 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/SysUserRoleController.cs @@ -0,0 +1,94 @@ +using Infrastructure; +using Infrastructure.Attribute; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Linq; +using ZR.Admin.WebApi.Filters; +using ZR.Model; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.System +{ + [Verify] + [Route("system/userRole")] + public class SysUserRoleController : BaseController + { + private readonly ISysUserRoleService SysUserRoleService; + private readonly ISysUserService UserService; + + public SysUserRoleController( + ISysUserRoleService sysUserRoleService, + ISysUserService userService) + { + SysUserRoleService = sysUserRoleService; + UserService = userService; + } + + /// + /// 根据角色编号获取已分配的用户 + /// + /// + /// + [HttpGet("get/{roleId}")] + [ActionPermissionFilter(Permission = "system:roleusers:query")] + public IActionResult GetList(long roleId = 0) + { + var list = SysUserRoleService.GetSysUsersByRoleId(roleId); + + return SUCCESS(list, TIME_FORMAT_FULL); + } + + /// + /// 添加角色用户 + /// + /// + [HttpPost("create")] + [ActionPermissionFilter(Permission = "system:roleusers:add")] + [Log(Title = "添加角色用户", BusinessType = Infrastructure.Enums.BusinessType.INSERT)] + public IActionResult Create([FromBody] RoleUsersCreateDto roleUsersCreateDto) + { + var response = SysUserRoleService.InsertRoleUser(roleUsersCreateDto); + + return SUCCESS(response); + } + + /// + /// 删除角色用户 + /// + /// + /// + [HttpPost("delete")] + [ActionPermissionFilter(Permission = "system:roleusers:del")] + [Log(Title = "删除角色用户", BusinessType = Infrastructure.Enums.BusinessType.DELETE)] + public IActionResult Delete([FromBody] RoleUsersCreateDto roleUsersCreateDto) + { + return SUCCESS(SysUserRoleService.DeleteRoleUserByUserIds(roleUsersCreateDto.RoleId, roleUsersCreateDto.UserIds)); + } + + /// + /// 获取未分配用户角色 + /// + /// + /// + [HttpGet("GetExcludeUsers")] + public IActionResult GetExcludeUsers(long roleId = 0) + { + if (roleId <= 0) + { + throw new CustomException(ResultCode.PARAM_ERROR, "roleId不能为空"); + } + // 取得该角色所有添加的用户 + var userIds = SysUserRoleService.GetSysUsersByRoleId(roleId).Select(f => f.UserId); + + SysUser userQuery = new(); + userQuery.Status = "0"; + PagerInfo pager = new(1, 50); + + // 获取未添加用户 + var list = UserService.SelectUserList(userQuery, pager).Where(m => !userIds.Contains(m.UserId)); + return SUCCESS(list, TIME_FORMAT_FULL); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/TasksController.cs b/ZR.Admin.WebApi/Controllers/System/TasksController.cs new file mode 100644 index 0000000..ad60385 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/TasksController.cs @@ -0,0 +1,270 @@ +using Infrastructure; +using Infrastructure.Attribute; +using Infrastructure.Enums; +using Mapster; +using Microsoft.AspNetCore.Mvc; +using Quartz; +using SqlSugar; +using System; +using System.Threading.Tasks; +using ZR.Admin.WebApi.Extensions; +using ZR.Admin.WebApi.Filters; +using ZR.Model; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Service.IService; +using ZR.Tasks; + +namespace ZR.Admin.WebApi.Controllers +{ + /// + /// 计划任务 + /// + //[Verify] + [Route("system/Tasks")] + public class TasksController : BaseController + { + private ISysTasksQzService _tasksQzService; + private ITaskSchedulerServer _schedulerServer; + + public TasksController( + ISysTasksQzService sysTasksQzService, + ITaskSchedulerServer taskScheduler) + { + _tasksQzService = sysTasksQzService; + _schedulerServer = taskScheduler; + } + + /// + /// 查询计划任务列表 + /// + /// + [HttpGet("list")] + [ActionPermissionFilter(Permission = "system:task:list")] + public IActionResult Query([FromQuery] TasksQueryDto parm, [FromQuery] PagerInfo pager) + { + //开始拼装查询条件 + var predicate = Expressionable.Create(); + + predicate = predicate.AndIF(!string.IsNullOrEmpty(parm.QueryText), + m => m.Name.Contains(parm.QueryText) || + m.JobGroup.Contains(parm.QueryText) || + m.AssemblyName.Contains(parm.QueryText)); + + var response = _tasksQzService.GetPages(predicate.ToExpression(), pager, f => f.IsStart, "Desc"); + + return SUCCESS(response, TIME_FORMAT_FULL); + } + + /// + /// 查询单个计划任务 + /// + /// 编码 + /// + [HttpGet("{id}")] + public IActionResult Get(string id) + { + if (!string.IsNullOrEmpty(id)) + { + return SUCCESS(_tasksQzService.GetId(id)); + } + return SUCCESS(_tasksQzService.GetAll()); + } + + + /// + /// 添加任务 + /// + /// + [HttpPost("create")] + [ActionPermissionFilter(Permission = "system:task:add")] + [Log(Title = "添加任务", BusinessType = BusinessType.INSERT)] + public IActionResult Create([FromBody] TasksCreateDto parm) + { + //判断是否已经存在 + if (_tasksQzService.Any(m => m.Name == parm.Name)) + { + throw new CustomException($"添加 {parm.Name} 失败,该用任务存在,不能重复!"); + } + if (string.IsNullOrEmpty(parm.Cron) || !CronExpression.IsValidExpression(parm.Cron)) + { + throw new CustomException($"cron表达式不正确"); + } + + //从 Dto 映射到 实体 + var tasksQz = parm.Adapt().ToCreate(); + tasksQz.IsStart = false; + tasksQz.Create_by = User.Identity.Name; + + return SUCCESS(_tasksQzService.Add(tasksQz)); + } + + /// + /// 更新任务 + /// + /// + [HttpPost("update")] + [ActionPermissionFilter(Permission = "system:task:edit")] + [Log(Title = "修改任务", BusinessType = BusinessType.UPDATE)] + public async Task Update([FromBody] TasksUpdateDto parm) + { + //判断是否已经存在 + if (_tasksQzService.Any(m => m.Name == parm.Name && m.ID != parm.ID)) + { + throw new CustomException($"更新 {parm.Name} 失败,该用任务存在,不能重复!"); + } + if (string.IsNullOrEmpty(parm.Cron)) + { + throw new CustomException($"触发器 Corn 模式下,运行时间表达式必须填写"); + } + if (!CronExpression.IsValidExpression(parm.Cron)) + { + throw new CustomException($"cron表达式不正确"); + } + var tasksQz = _tasksQzService.GetFirst(m => m.ID == parm.ID); + + if (tasksQz.IsStart) + { + throw new CustomException($"该任务正在运行中,请先停止在更新"); + } + + var response = _tasksQzService.Update(m => m.ID == parm.ID, m => new SysTasksQz + { + Name = parm.Name, + JobGroup = parm.JobGroup, + Cron = parm.Cron, + AssemblyName = parm.AssemblyName, + ClassName = parm.ClassName, + Remark = parm.Remark, + TriggerType = parm.TriggerType, + IntervalSecond = parm.IntervalSecond, + JobParams = parm.JobParams, + Update_by = User.Identity.Name, + Update_time = DateTime.Now + }); + if (response > 0) + { + //先暂停原先的任务 + var respon = await _schedulerServer.UpdateTaskScheduleAsync(tasksQz, tasksQz.JobGroup); + } + + return SUCCESS(response); + } + + /// + /// 删除任务 + /// + /// + [HttpDelete("delete")] + [ActionPermissionFilter(Permission = "system:task:delete")] + [Log(Title = "删除任务", BusinessType = BusinessType.DELETE)] + public async Task Delete(string id) + { + if (string.IsNullOrEmpty(id)) + { + throw new CustomException("删除任务 Id 不能为空"); + } + + if (!_tasksQzService.Any(m => m.ID == id)) + { + throw new CustomException("任务不存在"); + } + + var tasksQz = _tasksQzService.GetFirst(m => m.ID == id); + var taskResult = await _schedulerServer.DeleteTaskScheduleAsync(tasksQz); + + if (taskResult.Code == 200) + { + _tasksQzService.Delete(id); + } + return OutputJson(taskResult); + } + + /// + /// 启动任务 + /// + /// + [HttpGet("start")] + [ActionPermissionFilter(Permission = "system:task:start")] + [Log(Title = "启动任务", BusinessType = BusinessType.OTHER)] + public async Task Start(string id) + { + if (string.IsNullOrEmpty(id)) + { + throw new CustomException("启动任务 Id 不能为空"); + } + + if (!_tasksQzService.Any(m => m.ID == id)) + { + throw new CustomException("任务不存在"); + } + + var tasksQz = _tasksQzService.GetFirst(m => m.ID == id); + var taskResult = await _schedulerServer.AddTaskScheduleAsync(tasksQz); + + if (taskResult.Code == 200) + { + tasksQz.IsStart = true; + _tasksQzService.Update(tasksQz); + } + + return OutputJson(taskResult); + } + + /// + /// 停止任务 + /// + /// + [HttpGet("stop")] + [ActionPermissionFilter(Permission = "system:task:stop")] + [Log(Title = "停止任务", BusinessType = BusinessType.OTHER)] + public async Task Stop(string id) + { + if (string.IsNullOrEmpty(id)) + { + throw new CustomException("停止任务 Id 不能为空"); + } + + if (!_tasksQzService.Any(m => m.ID == id)) + { + throw new CustomException("任务不存在"); + } + + var tasksQz = _tasksQzService.GetFirst(m => m.ID == id); + var taskResult = await _schedulerServer.DeleteTaskScheduleAsync(tasksQz);//await _schedulerServer.PauseTaskScheduleAsync(tasksQz); + + if (taskResult.Code == 200) + { + tasksQz.IsStart = false; + _tasksQzService.Update(tasksQz); + } + + return OutputJson(taskResult); + } + + /// + /// 定时任务立即执行一次 + /// + /// + /// + [HttpGet("run")] + [ActionPermissionFilter(Permission = "system:task:run")] + [Log(Title = "执行任务", BusinessType = BusinessType.OTHER)] + public async Task Run(string id) + { + if (!_tasksQzService.Any(m => m.ID == id)) + { + throw new CustomException("任务不存在"); + } + var tasksQz = _tasksQzService.GetFirst(m => m.ID == id); + var taskResult = await _schedulerServer.RunTaskScheduleAsync(tasksQz); + + if (taskResult.Code == 200) + { + //_tasksQzService.Update(tasksQz); + } + + return OutputJson(taskResult); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/TasksLogController.cs b/ZR.Admin.WebApi/Controllers/System/TasksLogController.cs new file mode 100644 index 0000000..ca842ca --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/TasksLogController.cs @@ -0,0 +1,79 @@ +using Microsoft.AspNetCore.Mvc; +using SqlSugar; +using ZR.Model; +using ZR.Model.Dto.System; +using ZR.Model.System; +using Infrastructure.Extensions; +using Infrastructure.Attribute; +using ZR.Admin.WebApi.Filters; +using ZR.Common; +using Infrastructure.Enums; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.System +{ + [Route("/monitor/jobLog")] + public class TasksLogController : BaseController + { + private readonly ISysTasksLogService tasksLogService; + + public TasksLogController(ISysTasksLogService tasksLogService) + { + this.tasksLogService = tasksLogService; + } + + /// + /// 查询日志 + /// + /// + /// + /// + [HttpGet("list")] + public IActionResult GetList([FromQuery] PagerInfo pager, [FromQuery] TasksLogQueryDto queryDto) + { + //开始拼装查询条件 + queryDto.BeginTime = DateTimeHelper.GetBeginTime(queryDto.BeginTime, -1); + queryDto.EndTime = DateTimeHelper.GetBeginTime(queryDto.EndTime, 1); + + var predicate = Expressionable.Create().And(it => it.CreateTime >= queryDto.BeginTime && it.CreateTime <= queryDto.EndTime); + predicate = predicate.AndIF(queryDto.JobName.IfNotEmpty(), m => m.JobName.Contains(queryDto.JobName)); + predicate = predicate.AndIF(queryDto.JobGroup.IfNotEmpty(), m => m.JobGroup == queryDto.JobGroup); + predicate = predicate.AndIF(queryDto.Status.IfNotEmpty(), m => m.Status == queryDto.Status); + predicate = predicate.AndIF(queryDto.JobId.IfNotEmpty(), m => m.JobId == queryDto.JobId); + + var response = tasksLogService.GetPages(predicate.ToExpression(), pager, m => m.CreateTime, "Desc"); + + return SUCCESS(response, TIME_FORMAT_FULL); + } + + /// + /// 删除定时任务调用日志 + /// + /// + /// + [HttpDelete("{jobIds}")] + [ActionPermissionFilter(Permission = "PRIV_JOBLOG_DELETE")] + [Log(Title = "删除任务日志", BusinessType = BusinessType.DELETE)] + public IActionResult Delete(string jobIds) + { + long[] jobIdArr = Tools.SpitLongArrary(jobIds); + + int result = tasksLogService.Delete(jobIdArr); + + return OutputJson(ToJson(result, result)); + } + + /// + /// 清空日志 + /// + /// + [HttpDelete("clean")] + [ActionPermissionFilter(Permission = "PRIV_JOBLOG_REMOVE")] + [Log(Title = "清空任务日志", BusinessType = BusinessType.CLEAN)] + public IActionResult Clean() + { + tasksLogService.DeleteTable(); + return SUCCESS(1); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/monitor/MonitorController.cs b/ZR.Admin.WebApi/Controllers/System/monitor/MonitorController.cs new file mode 100644 index 0000000..b5ea834 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/monitor/MonitorController.cs @@ -0,0 +1,81 @@ +using Infrastructure; +using Infrastructure.Model; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using ZR.Admin.WebApi.Filters; +using ZR.Common; + +namespace ZR.Admin.WebApi.Controllers.monitor +{ + //[Verify] + public class MonitorController : BaseController + { + private OptionsSetting Options; + private IWebHostEnvironment HostEnvironment; + + public MonitorController(IOptions options, IWebHostEnvironment hostEnvironment) + { + this.HostEnvironment = hostEnvironment; + this.Options = options.Value; + } + + /// + /// 获取缓存监控数据 + /// + /// + [HttpGet("monitor/cache")] + public ApiResult GetCache() + { + string redis_connString = Options.Redis; + var rs = new CSRedis.CSRedisClient(redis_connString); + + RedisHelper.Initialization(rs);//初始化Redis + + return ToJson(1); + } + + /// + /// 获取服务器信息 + /// + /// + [HttpGet("monitor/server")] + //[AllowAnonymous] + public IActionResult Server() + { + //核心数 + int cpuNum = Environment.ProcessorCount; + string computerName = Environment.MachineName; + string osName = RuntimeInformation.OSDescription; + string osArch = RuntimeInformation.OSArchitecture.ToString(); + string version = RuntimeInformation.FrameworkDescription; + string appRAM = ((double)Process.GetCurrentProcess().WorkingSet64 / 1048576).ToString("N2") + " MB"; + string startTime = Process.GetCurrentProcess().StartTime.ToString("yyyy-MM-dd HH:mm:ss"); + string runTime = Process.GetCurrentProcess().StartTime.ToString("yyyy-MM-dd HH:mm:ss"); + string serverIP = (Request.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString() + ":" + Request.HttpContext.Connection.LocalPort);//获取服务器IP + + var data = new + { + cpu = ComputerHelper.GetComputerInfo(), + sys = new { cpuNum, computerName, osName, osArch, serverIP }, + app = new + { + name = HostEnvironment.EnvironmentName, + rootPath = HostEnvironment.ContentRootPath, + webRootPath = HostEnvironment.WebRootPath, + version, + appRAM, + startTime, + runTime, + host = serverIP + } + }; + + return SUCCESS(data); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/monitor/SysLogininforController.cs b/ZR.Admin.WebApi/Controllers/System/monitor/SysLogininforController.cs new file mode 100644 index 0000000..74a4139 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/monitor/SysLogininforController.cs @@ -0,0 +1,71 @@ +using Infrastructure.Attribute; +using Microsoft.AspNetCore.Mvc; +using ZR.Admin.WebApi.Filters; +using ZR.Common; +using ZR.Model; +using ZR.Model.System; +using ZR.Model.Vo; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.monitor +{ + /// + /// 系统访问记录 + /// + [Verify] + [Route("/monitor/logininfor")] + public class SysLogininforController : BaseController + { + private ISysLoginService sysLoginService; + + public SysLogininforController(ISysLoginService sysLoginService) + { + this.sysLoginService = sysLoginService; + } + + /// + /// 查询登录日志 + /// /monitor/logininfor/list + /// + /// + /// + /// + [HttpGet("list")] + public IActionResult LoignLogList([FromQuery] SysLogininfor sysLogininfoDto, [FromQuery] PagerInfo pagerInfo) + { + var list = sysLoginService.GetLoginLog(sysLogininfoDto, pagerInfo); + var vMPage = new VMPageResult(list, pagerInfo); + + return OutputJson(ToJson(vMPage.TotalNum, vMPage), TIME_FORMAT_FULL_2); + } + + /// + /// 清空登录日志 + /// /monitor/logininfor/clean + /// + /// + [Log(Title = "清空登录日志")] + [ActionPermissionFilter(Permission = "monitor:logininfor:remove")] + [HttpDelete("clean")] + public IActionResult CleanLoginInfo() + { + sysLoginService.TruncateLogininfo(); + return SUCCESS(1); + } + + /// + /// /monitor/logininfor/1 + /// + /// + /// + [Log(Title = "删除登录日志")] + [HttpDelete("{infoIds}")] + [ActionPermissionFilter(Permission = "monitor:logininfor:remove")] + public IActionResult Remove(string infoIds) + { + long[] infoIdss = Tools.SpitLongArrary(infoIds); + return SUCCESS(sysLoginService.DeleteLogininforByIds(infoIdss)); + } + + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/monitor/SysOperlogController.cs b/ZR.Admin.WebApi/Controllers/System/monitor/SysOperlogController.cs new file mode 100644 index 0000000..fd76f8f --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/monitor/SysOperlogController.cs @@ -0,0 +1,71 @@ +using Infrastructure.Attribute; +using Infrastructure.Enums; +using Infrastructure.Model; +using Microsoft.AspNetCore.Mvc; +using ZR.Admin.WebApi.Filters; +using ZR.Common; +using ZR.Model; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Model.Vo; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Controllers.monitor +{ + [Verify] + [Route("/monitor/operlog")] + public class SysOperlogController : BaseController + { + private ISysOperLogService sysOperLogService; + + public SysOperlogController(ISysOperLogService sysOperLogService) + { + this.sysOperLogService = sysOperLogService; + } + + /// + /// 查询操作日志 + /// + /// + /// + [HttpGet("list")] + public IActionResult OperList([FromQuery] SysOperLogDto sysOperLog) + { + PagerInfo pagerInfo = new PagerInfo(sysOperLog.pageNum); + + var list = sysOperLogService.SelectOperLogList(sysOperLog, pagerInfo); + var vMPage = new VMPageResult(list, pagerInfo); + + return OutputJson(ToJson(vMPage.TotalNum, vMPage), TIME_FORMAT_FULL_2); + } + + /// + /// 删除操作日志 + /// + /// + /// + [Log(Title = "操作日志", BusinessType = BusinessType.DELETE)] + [ActionPermissionFilter(Permission = "monitor:operlog:delete")] + [HttpDelete("{operIds}")] + public IActionResult Remove(string operIds) + { + long[] operIdss = Tools.SpitLongArrary(operIds); + return SUCCESS(sysOperLogService.DeleteOperLogByIds(operIdss)); + } + + /// + /// 清空操作日志 + /// + /// + [Log(Title = "清空操作日志", BusinessType = BusinessType.CLEAN)] + [ActionPermissionFilter(Permission = "monitor:operlog:delete")] + [HttpDelete("clean")] + public ApiResult ClearOperLog() + { + sysOperLogService.CleanOperLog(); + + return ToJson(1); + } + + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/monitor/SysUserOnlineController.cs b/ZR.Admin.WebApi/Controllers/System/monitor/SysUserOnlineController.cs new file mode 100644 index 0000000..0134f54 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/System/monitor/SysUserOnlineController.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Mvc; +using ZR.Admin.WebApi.Filters; + +namespace ZR.Admin.WebApi.Controllers.monitor +{ + [Verify] + [Route("monitor/online")] + public class SysUserOnlineController : BaseController + { + [HttpGet("list")] + public IActionResult Index() + { + return SUCCESS(null); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/UploadController.cs b/ZR.Admin.WebApi/Controllers/UploadController.cs new file mode 100644 index 0000000..aa14a79 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/UploadController.cs @@ -0,0 +1,47 @@ +using Infrastructure; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using System; +using System.IO; +using ZR.Common; + +namespace ZR.Admin.WebApi.Controllers +{ + [Route("[controller]/[action]")] + public class UploadController : BaseController + { + private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + private OptionsSetting OptionsSetting; + + public UploadController(IOptions optionsSetting) + { + OptionsSetting = optionsSetting.Value; + } + /// + /// 存储文件 + /// + /// + /// + [HttpPost] + public IActionResult SaveFile([FromForm(Name = "file")] IFormFile formFile) + { + if (formFile == null) throw new CustomException(ResultCode.PARAM_ERROR, "上传图片不能为空"); + string fileExt = Path.GetExtension(formFile.FileName); + string savePath = Path.Combine("wwwroot", FileUtil.GetdirPath("uploads")); + + if (!Directory.Exists(savePath)) { Directory.CreateDirectory(savePath); } + + string fileName = FileUtil.HashFileName(Guid.NewGuid().ToString()).ToLower() + fileExt; + string finalFilePath = Path.Combine(savePath, fileName); + + using (var stream = new FileStream(finalFilePath, FileMode.Create)) + { + formFile.CopyToAsync(stream); + } + + string accessPath = $"{OptionsSetting.Upload.UploadUrl}/{finalFilePath.Replace("wwwroot", "").Replace("\\", "/")}"; + return OutputJson(ToJson(1, accessPath)); + } + } +} diff --git a/ZR.Admin.WebApi/DataProtection/key-b6498f45-218a-4853-a182-0e86942ab496.xml b/ZR.Admin.WebApi/DataProtection/key-b6498f45-218a-4853-a182-0e86942ab496.xml new file mode 100644 index 0000000..71c92b8 --- /dev/null +++ b/ZR.Admin.WebApi/DataProtection/key-b6498f45-218a-4853-a182-0e86942ab496.xml @@ -0,0 +1,16 @@ + + + 2021-08-02T06:58:41.3050198Z + 2021-08-02T06:58:41.2698484Z + 2021-10-31T06:58:41.2698484Z + + + + + + + gmFBOJQxBXoKTqyvLeEnS6IDsf61vG/s0krHG1MfcZwQlu/D2XUaCms+xzpFc2c2JGdaJh5LiLIyJuoRR20Mug== + + + + \ No newline at end of file diff --git a/ZR.Admin.WebApi/Dockerfile b/ZR.Admin.WebApi/Dockerfile new file mode 100644 index 0000000..91f4597 --- /dev/null +++ b/ZR.Admin.WebApi/Dockerfile @@ -0,0 +1,19 @@ +FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base +#´´½¨ /appÎļþ¼Ð +WORKDIR /app +#´´½¨¹ÒÔØĿ¼,ÓÃÓڽ«³ÌÐò²¿ÊðÔڷþÎñÆ÷±¾µØ +#VOLUME /app +#ÉèÖÃdockerÈÝÆ÷¶ÔÍⱩ¶¶˿Ú +EXPOSE 5000 +VOLUME /app/logs +#COPY bin/Release/net5.0/publish/ app/ +COPY . app/ + +#ÉèÖÃÈÝÆ÷ÄڵÄʱÇø£¬Èç¹û²»ÉèÖã¬ĬÈÏʱÇøÊDZê׼ʱ¼ä±ȱ±¾©ʱ¼äÍí8¸öСʱ +RUN echo "Asia/shanghai" > /etc/timezone +RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + +# ¸´ÖƷ¢²¼Îļþµ½¹¤×÷Ŀ¼ +#COPY . app/ + +ENTRYPOINT ["dotnet", "ZR.Admin.WebApi.dll"] \ No newline at end of file diff --git a/ZR.Admin.WebApi/Extensions/EntityExtension.cs b/ZR.Admin.WebApi/Extensions/EntityExtension.cs new file mode 100644 index 0000000..d518656 --- /dev/null +++ b/ZR.Admin.WebApi/Extensions/EntityExtension.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Http; +using Snowflake.Core; +using System; + +namespace ZR.Admin.WebApi.Extensions +{ + public static class EntityExtension + { + public static TSource ToCreate(this TSource source, HttpContext context = null) + { + var types = source.GetType(); + + var worker = new IdWorker(1, 1); + if (types.GetProperty("ID") != null) + { + long id = worker.NextId(); + + types.GetProperty("ID").SetValue(source, id.ToString(), null); + } + + if (types.GetProperty("CreateTime") != null) + { + types.GetProperty("CreateTime").SetValue(source, DateTime.Now, null); + } + + if (types.GetProperty("UpdateTime") != null) + { + types.GetProperty("UpdateTime").SetValue(source, DateTime.Now, null); + } + + if (types.GetProperty("Create_by") != null && context != null) + { + types.GetProperty("Create_by").SetValue(source, context.GetName(), null); + + // types.GetProperty("CreateName").SetValue(source, userSession.UserName, null); + } + if (types.GetProperty("UserId") != null && context != null) + { + types.GetProperty("UserId").SetValue(source, context.GetUId(), null); + } + //if (types.GetProperty("UpdateID") != null) + //{ + // types.GetProperty("UpdateID").SetValue(source, userSession.UserID, null); + + // types.GetProperty("UpdateName").SetValue(source, userSession.UserName, null); + //} + + + return source; + } + + //public static TSource ToUpdate(this TSource source, UserSessionVM userSession) + //{ + // var types = source.GetType(); + + // if (types.GetProperty("UpdateTime") != null) + // { + // types.GetProperty("UpdateTime").SetValue(source, DateTime.Now, null); + // } + + // if (types.GetProperty("UpdateID") != null) + // { + // types.GetProperty("UpdateID").SetValue(source, userSession.UserID, null); + // } + + // if (types.GetProperty("UpdateName") != null) + // { + // types.GetProperty("UpdateName").SetValue(source, userSession.UserName, null); + // } + + // return source; + //} + + } +} diff --git a/ZR.Admin.WebApi/Extensions/HttpContextExtension.cs b/ZR.Admin.WebApi/Extensions/HttpContextExtension.cs new file mode 100644 index 0000000..0720d47 --- /dev/null +++ b/ZR.Admin.WebApi/Extensions/HttpContextExtension.cs @@ -0,0 +1,160 @@ +using Infrastructure; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using UAParser; +using ZR.Model.System; + +namespace ZR.Admin.WebApi.Extensions +{ + /// + /// HttpContext扩展类 + /// + public static class HttpContextExtension + { + /// + /// 是否是ajax请求 + /// + /// + /// + public static bool IsAjaxRequest(this HttpRequest request) + { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + + //return request.Headers.ContainsKey("X-Requested-With") && + // request.Headers["X-Requested-With"].Equals("XMLHttpRequest"); + + return request.Headers["X-Requested-With"] == "XMLHttpRequest" || (request.Headers != null && request.Headers["X-Requested-With"] == "XMLHttpRequest"); + } + + /// + /// 获取客户端IP + /// + /// + /// + public static string GetClientUserIp(this HttpContext context) + { + if (context == null) return ""; + var result = context.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (string.IsNullOrEmpty(result)) + { + result = context.Connection.RemoteIpAddress.ToString(); + } + if (string.IsNullOrEmpty(result) || result.Contains("::1")) + result = "127.0.0.1"; + + result = result.Replace("::ffff:", "127.0.0.1"); + result = IsIP(result) ? result : "127.0.0.1"; + return result; + } + + public static bool IsIP(string ip) + { + return Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); + } + + public static long GetUId(this HttpContext context) + { + var uid = context.User.FindFirstValue(ClaimTypes.PrimarySid); + + return !string.IsNullOrEmpty(uid) ? long.Parse(uid) : 0 ; + } + public static string GetName(this HttpContext context) + { + var uid = context.User.Identity.Name; + + return uid; + } + //public static int GetRole(this HttpContext context) + //{ + // var roleid = context.User.FindFirstValue(ClaimTypes.Role) ?? "0"; + + // return int.Parse(roleid); + //} + + public static string GetUserAgent(this HttpContext context) + { + var str = context.Request.Headers["User-Agent"]; + + return str; + } + + /// + /// 获取请求令牌 + /// + /// + /// + public static string GetToken(this HttpContext context) + { + var str = context.Request.Headers["Token"]; + + return str; + } + + public static ClientInfo GetClientInfo(this HttpContext context) + { + var str = GetUserAgent(context); + var uaParser = Parser.GetDefault(); + ClientInfo c = uaParser.Parse(str); + + return c; + } + + public static string GetRequestUrl(this HttpContext context) + { + return context != null ? context.Request.Path.Value : ""; + } + + /// + /// 登录cookie写入 + /// + /// + /// + /// + public static List WriteCookies(this HttpContext context, LoginUser user) + { + //1、创建Cookie保存用户信息,使用claim + var claims = new List() + { + new Claim(ClaimTypes.PrimarySid, user.UserId.ToString()), + new Claim(ClaimTypes.Name, user.UserName), + new Claim(ClaimTypes.UserData, JsonConvert.SerializeObject(user)) + }; + if (user.RoleIds != null) + { + claims.Add(new Claim(ClaimTypes.Role, string.Join(",", user.RoleIds))); + } + if (user.Permissions != null) + { + claims.Add(new Claim("perm", string.Join(",", user.Permissions))); + } + //2.创建声明主题 指定认证方式 这里使用cookie + var claimsIdentity = new ClaimsIdentity(claims, "Login"); + + Task.Run(async () => + { + await context.SignInAsync( + CookieAuthenticationDefaults.AuthenticationScheme,//这里要注意的是HttpContext.SignInAsync(AuthenticationType,…) 所设置的Scheme一定要与前面的配置一样,这样对应的登录授权才会生效。 + new ClaimsPrincipal(claimsIdentity), + new AuthenticationProperties() + { + IsPersistent = true, + AllowRefresh = true, + ExpiresUtc = DateTimeOffset.Now.AddDays(1),//有效时间 + }); + }).Wait(); + return claims; + } + } + +} diff --git a/ZR.Admin.WebApi/Extensions/TasksExtension.cs b/ZR.Admin.WebApi/Extensions/TasksExtension.cs new file mode 100644 index 0000000..dda6ba7 --- /dev/null +++ b/ZR.Admin.WebApi/Extensions/TasksExtension.cs @@ -0,0 +1,53 @@ +using Infrastructure; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Quartz.Spi; +using System; +using ZR.Service.IService; +using ZR.Tasks; + +namespace ZR.Admin.WebApi.Extensions +{ + /// + /// 定时任务扩展方法 + /// + public static class TasksExtension + { + public static void AddTaskSchedulers(this IServiceCollection services) + { + if (services == null) throw new ArgumentNullException(nameof(services)); + + //添加Quartz服务 + services.AddSingleton(); + //添加我们的服务 + services.AddTransient(); + + services.AddTransient(); + } + + /// + /// 程序启动后添加任务计划 + /// + /// + /// + public static IApplicationBuilder UseAddTaskSchedulers(this IApplicationBuilder app) + { + var _tasksQzService = (ISysTasksQzService)App.GetRequiredService(typeof(ISysTasksQzService)); + + //此写法不通过有待研究 + //var _tasksQzService2 = (ISysTasksQzService)services.GetRequiredService(typeof(SysTasksQzService)); + ITaskSchedulerServer _schedulerServer = App.GetRequiredService(); + + var tasks = _tasksQzService.GetWhere(m => m.IsStart); + + //程序启动后注册所有定时任务 + foreach (var task in tasks) + { + _schedulerServer.AddTaskScheduleAsync(task); + } + + return app; + } + + } +} diff --git a/ZR.Admin.WebApi/Filters/ActionPermissionFilter.cs b/ZR.Admin.WebApi/Filters/ActionPermissionFilter.cs new file mode 100644 index 0000000..88258d7 --- /dev/null +++ b/ZR.Admin.WebApi/Filters/ActionPermissionFilter.cs @@ -0,0 +1,72 @@ +using Infrastructure; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ZR.Model.System; + +namespace ZR.Admin.WebApi.Filters +{ + /// + /// API授权判断 + /// + public class ActionPermissionFilter : ActionFilterAttribute//, IAsyncActionFilter + { + private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + /// + /// 权限字符串,例如 system:user:view + /// + public string Permission { get; set; } + private bool HasPermi { get; set; } + + public ActionPermissionFilter() { } + + public ActionPermissionFilter(string permission) + { + Permission = permission; + HasPermi = !string.IsNullOrEmpty(Permission); + } + + /// + /// 执行Action前校验是否有权限访问 + /// + /// + /// + /// + public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + LoginUser info = Framework.JwtUtil.GetLoginUser(context.HttpContext); + + if (info != null && info?.UserId > 0) + { + List perms = info.Permissions; + if (perms.Exists(f => f.Equals(GlobalConstant.AdminPerm))) + { + HasPermi = true; + } + else if (!string.IsNullOrEmpty(Permission)) + { + HasPermi = perms.Exists(f => f.ToLower() == Permission.ToLower()); + } + + bool isDemoMode = ConfigUtils.Instance.GetAppConfig("DemoMode", false); + + //演示公开环境屏蔽权限 + string[] denyPerms = new string[] { "update", "add", "remove", "add", "edit", "delete", "import", "run", "start", "stop", "clear" }; + if (isDemoMode && (denyPerms.Any(f => Permission.ToLower().Contains(f.ToLower())) || Permission.Equals("system"))) + { + context.Result = new JsonResult(new { code = ResultCode.FORBIDDEN, msg = "演示模式 , 不允许操作" }); + } + if (!HasPermi) + { + logger.Info($"用户{info.NickName}没有权限访问{context.HttpContext.Request.Path},当前权限[{Permission}]"); + context.Result = new JsonResult(new { code = ResultCode.FORBIDDEN, msg = "你没有权限访问" }); + } + } + + return base.OnActionExecutionAsync(context, next); + } + } +} diff --git a/ZR.Admin.WebApi/Filters/GlobalActionMonitor.cs b/ZR.Admin.WebApi/Filters/GlobalActionMonitor.cs new file mode 100644 index 0000000..7ed2ab1 --- /dev/null +++ b/ZR.Admin.WebApi/Filters/GlobalActionMonitor.cs @@ -0,0 +1,54 @@ +using Infrastructure; +using Infrastructure.Model; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using NLog; +using System; + +namespace ZR.Admin.WebApi.Filters +{ + public class GlobalActionMonitor : Attribute, IActionFilter + { + static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + /// + /// OnActionExecuting是在Action执行之前运行的方法。 + /// + /// + public void OnActionExecuting(ActionExecutingContext context) + { + if (context.ModelState.IsValid) return; + ApiResult response = new ApiResult(); + response.Code = (int)ResultCode.PARAM_ERROR; + + //var keys = context.ModelState.Keys; + var values = context.ModelState.Values; + foreach (var item in values) + { + foreach (var err in item.Errors) + { + if (err.ErrorMessage.Contains("JSON")) + { + return; + } + if (!string.IsNullOrEmpty(response.Msg)) + { + response.Msg += " | "; + } + + response.Msg += err.ErrorMessage; + } + } + logger.Info($"请求参数错误,{response.Msg}"); + context.Result = new JsonResult(response); + } + + /// + /// OnActionExecuted是在Action中的代码执行之后运行的方法。 + /// + /// + public void OnActionExecuted(ActionExecutedContext context) + { + } + } +} diff --git a/ZR.Admin.WebApi/Filters/GlobalExceptionFilter.cs b/ZR.Admin.WebApi/Filters/GlobalExceptionFilter.cs new file mode 100644 index 0000000..b0f205d --- /dev/null +++ b/ZR.Admin.WebApi/Filters/GlobalExceptionFilter.cs @@ -0,0 +1,67 @@ +using System; +using System.Diagnostics; +using Infrastructure; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace ZRAdmin.Filters +{ + /// + /// 全局异常捕获 + /// 暂时没用了,改为中间件 2021-2-26 + /// + public class GlobalExceptionFilter : IExceptionFilter + { + readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + private IWebHostEnvironment _hostingEnvironment; + + public GlobalExceptionFilter(IWebHostEnvironment environment) + { + _hostingEnvironment = environment; + } + + public void OnException(ExceptionContext context) + { + Exception ex = context.Exception; + + int code = (int)ResultCode.GLOBAL_ERROR; + string msg; + //自定义异常 + if (ex is CustomException customException) + { + code = customException.Code; + msg = customException.Message; + + logger.Info($"自定义异常code={code},msg={msg}"); + } + else + //参数异常 + if (ex is ArgumentException) + { + code = (int)ResultCode.PARAM_ERROR; + msg = ex.Message; + } + else + { + msg = "服务器好像出了点问题......"; + Console.WriteLine(ex.StackTrace); + // 获取异常堆栈 + var traceFrame = new StackTrace(ex, true).GetFrame(0); + // 获取出错的文件名 + var exceptionFileName = traceFrame.GetFileName(); + // 获取出错的行号 + var exceptionFileLineNumber = traceFrame.GetFileLineNumber(); + var errorMsg = $"{ex.Message}##{traceFrame}##{exceptionFileName}##行号:{exceptionFileLineNumber}"; + + logger.Error(ex, $"系统出错了error={ex.Message}#{errorMsg}"); + //context.Response.StatusCode = 500; + } + + context.Result = new JsonResult(new { code, msg }); + context.ExceptionHandled = true; + } + + } +} \ No newline at end of file diff --git a/ZR.Admin.WebApi/Filters/LogActionFilter.cs b/ZR.Admin.WebApi/Filters/LogActionFilter.cs new file mode 100644 index 0000000..0d9df95 --- /dev/null +++ b/ZR.Admin.WebApi/Filters/LogActionFilter.cs @@ -0,0 +1,131 @@ +using Infrastructure; +using Infrastructure.Attribute; +using IPTools.Core; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using NLog; +using System; +using System.IO; +using System.Linq; +using System.Text; +using ZR.Admin.WebApi.Extensions; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Filters +{ + /// + /// 记录请求,输出日志 + /// + public class LogActionFilter : ActionFilterAttribute + { + static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + /// + /// OnActionExecuted是在Action中的代码执行之后运行的方法。 + /// + /// + public override void OnResultExecuted(ResultExecutedContext context) + { + if (context.ActionDescriptor is not ControllerActionDescriptor controllerActionDescriptor) return; + + //获得注解信息 + LogAttribute logAttribute = GetLogAttribute(controllerActionDescriptor); + if (logAttribute == null) return; + + try + { + string method = context.HttpContext.Request.Method.ToUpper(); + // 获取当前的用户 + string userName = context.HttpContext.GetName(); + string jsonResult = string.Empty; + + if (context.Result is ContentResult result && result.ContentType == "application/json") + { + jsonResult = result.Content.Replace("\r\n", "").Trim(); + } + //获取当前执行方法的类名 + //string className = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name; + //获取当前成员的名称 + //string methodName = System.Reflection.MethodBase.GetCurrentMethod().Name; + string controller = context.RouteData.Values["Controller"].ToString(); + string action = context.RouteData.Values["Action"].ToString(); + + string ip = HttpContextExtension.GetClientUserIp(context.HttpContext); + var ip_info = IpTool.Search(ip); + + SysOperLog sysOperLog = new SysOperLog + { + status = 0, + operName = userName, + operIp = ip, + operUrl = HttpContextExtension.GetRequestUrl(context.HttpContext), + requestMethod = method, + jsonResult = jsonResult, + operLocation = ip_info.Province + " " + ip_info.City, + method = controller + "." + action + "()", + //Elapsed = _stopwatch.ElapsedMilliseconds, + operTime = DateTime.Now + }; + if (logAttribute != null) + { + sysOperLog.title = logAttribute?.Title; + sysOperLog.businessType = (int)logAttribute?.BusinessType; + } + GetRequestValue(sysOperLog, context.HttpContext); + + var sysOperLogService = (ISysOperLogService)App.GetRequiredService(typeof(ISysOperLogService)); + + sysOperLogService.InsertOperlog(sysOperLog); + + LogEventInfo ei = new(LogLevel.Info, "GlobalExceptionMiddleware", ""); + + ei.Properties["status"] = 0; + ei.Properties["jsonResult"] = !HttpMethods.IsGet(method) ? jsonResult : ""; + ei.Properties["requestParam"] = sysOperLog.operParam; + ei.Properties["user"] = context.HttpContext.User.Identity.Name; + + logger.Log(ei); + } + catch (Exception ex) + { + logger.Error(ex, $"记录操作日志出错了#{ex.Message}"); + } + } + + private LogAttribute GetLogAttribute(ControllerActionDescriptor controllerActionDescriptor) + { + var attribute = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true) + .FirstOrDefault(a => a.GetType().Equals(typeof(LogAttribute))); + + return (LogAttribute)attribute; + } + + /// + /// 设置请求参数 + /// + /// + /// + public static void GetRequestValue(SysOperLog operLog, HttpContext context) + { + string reqMethod = operLog.requestMethod; + string param; + + if (HttpMethods.IsPost(reqMethod) || HttpMethods.IsPut(reqMethod)) + { + context.Request.Body.Seek(0, SeekOrigin.Begin); + using var reader = new StreamReader(context.Request.Body, Encoding.UTF8); + //需要使用异步方式才能获取 + param = reader.ReadToEndAsync().Result; + } + else + { + param = context.Request.QueryString.Value.ToString(); + } + operLog.operParam = param; + } + + } +} \ No newline at end of file diff --git a/ZR.Admin.WebApi/Filters/VerifyAttribute.cs b/ZR.Admin.WebApi/Filters/VerifyAttribute.cs new file mode 100644 index 0000000..cbfaff1 --- /dev/null +++ b/ZR.Admin.WebApi/Filters/VerifyAttribute.cs @@ -0,0 +1,67 @@ +using Infrastructure; +using Infrastructure.Model; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using NLog; +using System; +using System.Linq; +using ZR.Admin.WebApi.Extensions; +using ZR.Model.System; + +namespace ZR.Admin.WebApi.Filters +{ + /// + /// 授权校验访问 + /// 如果跳过授权登录在Action 或controller加上 AllowAnonymousAttribute + /// + public class VerifyAttribute : Attribute, IAuthorizationFilter + { + static readonly Logger logger = LogManager.GetCurrentClassLogger(); + IWebHostEnvironment webHostEnvironment = (IWebHostEnvironment)App.ServiceProvider.GetService(typeof(IWebHostEnvironment)); + + //public VerifyAttribute() { } + + /// + /// 只判断token是否正确,不判断权限 + /// 如果需要判断权限的在Action上加上ApiActionPermission属性标识权限类别,ActionPermissionFilter作权限处理 + /// + /// + public void OnAuthorization(AuthorizationFilterContext context) + { + var noNeedCheck = false; + if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor) + { + noNeedCheck = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true) + .Any(a => a.GetType().Equals(typeof(AllowAnonymousAttribute))); + } + + if (noNeedCheck) return; + + string ip = HttpContextExtension.GetClientUserIp(context.HttpContext); + string url = context.HttpContext.Request.Path; + var isAuthed = context.HttpContext.User.Identity.IsAuthenticated; + // 检查登陆 - 在SignIn中判断用户合法性,将登陆信息保存在Cookie中,在SignOut中移除登陆信息 + var userName = context.HttpContext.User.Identity.Name; + + //使用jwt token校验2020-11-21 + //string token = context.HttpContext.Request.Headers["Token"]; + LoginUser info = Framework.JwtUtil.GetLoginUser(context.HttpContext); + + if (info != null && info.UserId > 0) + { + logger.Info($"[{info.UserId}-{userName}-{ip}]用户登录校验成功"); + } + else + { + string msg = $"请求访问:{url}授权认证失败,无法访问系统资源"; + logger.Info(msg); + + context.Result = new JsonResult(new ApiResult((int)ResultCode.DENY, msg)); + } + } + } +} diff --git a/ZR.Admin.WebApi/Framework/AsyncFactory.cs b/ZR.Admin.WebApi/Framework/AsyncFactory.cs new file mode 100644 index 0000000..07c10c1 --- /dev/null +++ b/ZR.Admin.WebApi/Framework/AsyncFactory.cs @@ -0,0 +1,35 @@ +using IPTools.Core; +using Microsoft.AspNetCore.Http; +using UAParser; +using ZR.Admin.WebApi.Extensions; +using ZR.Model.System; + +namespace ZR.Admin.WebApi.Framework +{ + public class AsyncFactory + { + /// + /// 记录用户登陆信息 + /// + /// + /// + /// + /// + public static SysLogininfor RecordLogInfo(HttpContext context, string status, string message) + { + ClientInfo clientInfo = context.GetClientInfo(); + SysLogininfor sysLogininfor = new SysLogininfor(); + sysLogininfor.browser = clientInfo.Device.Family; + sysLogininfor.os = clientInfo.OS.ToString(); + sysLogininfor.ipaddr = context.GetClientUserIp(); + sysLogininfor.msg = message; + sysLogininfor.userName = context.GetName(); + sysLogininfor.status = status; + var ip_info = IpTool.Search(sysLogininfor.ipaddr); + sysLogininfor.loginLocation = ip_info.Province + "-" + ip_info.City; + + return sysLogininfor; + } + + } +} diff --git a/ZR.Admin.WebApi/Framework/CookieUtil.cs b/ZR.Admin.WebApi/Framework/CookieUtil.cs new file mode 100644 index 0000000..eeb7e26 --- /dev/null +++ b/ZR.Admin.WebApi/Framework/CookieUtil.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace ZR.Admin.WebApi.Framework +{ + public class CookieUtil + { + + + } +} diff --git a/ZR.Admin.WebApi/Framework/JwtUtil.cs b/ZR.Admin.WebApi/Framework/JwtUtil.cs new file mode 100644 index 0000000..e75abff --- /dev/null +++ b/ZR.Admin.WebApi/Framework/JwtUtil.cs @@ -0,0 +1,114 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Text; +using ZR.Admin.WebApi.Extensions; +using ZR.Model.System; + +namespace ZR.Admin.WebApi.Framework +{ + /// + /// 2020-11-20 + /// + public class JwtUtil + { + public static readonly string KEY = "asdfghjklzxcvbnm"; + + /// + /// 获取用户身份信息 + /// + /// + /// + public static LoginUser GetLoginUser(HttpContext httpContext) + { + string token = HttpContextExtension.GetToken(httpContext); + if (!string.IsNullOrEmpty(token)) + { + return ValidateJwtToken(ParseToken(token)); + } + return null; + } + + /// + /// 生成token + /// + /// + /// + public static string GenerateJwtToken(List claims) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var key = Encoding.ASCII.GetBytes(KEY); + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(claims), + //Issuer = "", + //Audience = "", + Expires = DateTime.Now.AddDays(7), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + }; + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); + } + + /// + /// 从令牌中获取数据声明 + /// + /// 令牌 + /// + public static IEnumerable ParseToken(string token) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var key = Encoding.ASCII.GetBytes(KEY); + try + { + tokenHandler.ValidateToken(token, new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(key), + ValidateIssuer = false, + ValidateAudience = false, + // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later) + ClockSkew = TimeSpan.Zero + }, out SecurityToken validatedToken); + + //{{"alg":"HS256","typ":"JWT"}.{"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid":"2","unique_name":"ry","nameid":"2","given_name":"若依","nbf":1606654010,"exp":1606740410,"iat":1606654010}} + var jwtToken = (JwtSecurityToken)validatedToken; + return jwtToken.Claims; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + // return null if validation fails + return null; + } + } + + /// + /// jwt token校验 + /// + /// + /// + private static LoginUser ValidateJwtToken(IEnumerable jwtToken) + { + try + { + var userId = jwtToken.FirstOrDefault(x => x.Type == "primarysid").Value; + var userName = jwtToken.FirstOrDefault(x => x.Type == "unique_name").Value; + var userData = jwtToken.FirstOrDefault(x => x.Type == ClaimTypes.UserData).Value; + + LoginUser loginUser = JsonConvert.DeserializeObject(userData); + return loginUser; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return null; + } + } + } +} diff --git a/ZR.Admin.WebApi/Middleware/GlobalExceptionMiddleware.cs b/ZR.Admin.WebApi/Middleware/GlobalExceptionMiddleware.cs new file mode 100644 index 0000000..4bbe298 --- /dev/null +++ b/ZR.Admin.WebApi/Middleware/GlobalExceptionMiddleware.cs @@ -0,0 +1,103 @@ +using Infrastructure; +using Infrastructure.Model; +using IPTools.Core; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using NLog; +using System; +using System.Threading.Tasks; +using ZR.Admin.WebApi.Extensions; +using ZR.Admin.WebApi.Filters; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Admin.WebApi.Middleware +{ + /// + /// 全局异常处理中间件 + /// 调用 app.UseMiddleware(); + /// + public class GlobalExceptionMiddleware + { + private readonly RequestDelegate next; + private readonly ISysOperLogService SysOperLogService; + + static readonly Logger Logger = LogManager.GetCurrentClassLogger();//声明NLog变量 + + public GlobalExceptionMiddleware(RequestDelegate next, ISysOperLogService sysOperLog) + { + this.next = next; + this.SysOperLogService = sysOperLog; + } + + public async Task Invoke(HttpContext context) + { + try + { + await next(context); + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex); + } + } + + private async Task HandleExceptionAsync(HttpContext context, Exception ex) + { + LogLevel logLevel = LogLevel.Info; + int code = (int)ResultCode.GLOBAL_ERROR; + string msg; + string error = string.Empty; + //自定义异常 + if (ex is CustomException customException) + { + code = customException.Code; + msg = customException.Message; + error = customException.LogMsg; + } + else if (ex is ArgumentException)//参数异常 + { + code = (int)ResultCode.PARAM_ERROR; + msg = ex.Message; + } + else + { + msg = "服务器好像出了点问题......"; + error = $"{ex.Message}"; + logLevel = LogLevel.Error; + context.Response.StatusCode = 500; + } + + string responseResult = System.Text.Json.JsonSerializer.Serialize(new ApiResult(code, msg)).ToLower(); + string ip = HttpContextExtension.GetClientUserIp(context); + var ip_info = IpTool.Search(ip); + + SysOperLog sysOperLog = new() + { + status = 1, + operIp = ip, + operUrl = HttpContextExtension.GetRequestUrl(context), + requestMethod = context.Request.Method, + jsonResult = responseResult, + errorMsg = string.IsNullOrEmpty(error) ? msg : error, + operName = context.User.Identity.Name, + operLocation = ip_info.Province + " " + ip_info.City, + operTime = DateTime.Now + }; + LogActionFilter.GetRequestValue(sysOperLog, context); + LogEventInfo ei = new(logLevel, "GlobalExceptionMiddleware", error); + + ei.Exception = ex; + ei.Message = error; + ei.Properties["status"] = 1;//走正常返回都是通过走GlobalExceptionFilter不通过 + ei.Properties["jsonResult"] = responseResult; + ei.Properties["requestParam"] = sysOperLog.operParam; + ei.Properties["user"] = context.User.Identity.Name; + + Logger.Log(ei); + await context.Response.WriteAsync(responseResult); + + SysOperLogService.InsertOperlog(sysOperLog); + } + } +} diff --git a/ZR.Admin.WebApi/Middleware/RequestIPMiddleware.cs b/ZR.Admin.WebApi/Middleware/RequestIPMiddleware.cs new file mode 100644 index 0000000..0baa7b8 --- /dev/null +++ b/ZR.Admin.WebApi/Middleware/RequestIPMiddleware.cs @@ -0,0 +1,70 @@ +using Common; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace ZRAdmin.Middleware +{ + public class RequestIPMiddleware + { + private readonly RequestDelegate _next; + + static readonly Logger Logger = LogManager.GetCurrentClassLogger();//声明NLog变量 + + public RequestIPMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context) + { + var url = context.Request.Path.ToString().ToLower(); + string userip = Tools.GetRealIP(); + //string agent = HttpContextExtension.GetUserAgent(context); + + string[] urls = new string[] { "/css", "/js", "/images", "/lib", "/home/error", "/api" }; + + var strRegex = "(.jpg|.png|.gif|.php|.cfg|.ico)$"; //用于验证图片扩展名的正则表达式 + var re = new Regex(strRegex); + + //阻止.php访问往下请求 + if (new Regex("(.php)$").IsMatch(url)) + { + await context.Response.WriteAsync("hello"); + return; + } + + //var ip_info = IpTool.Search(userip); + + ////bool flag = ((IList)urls).Contains(url); + //if (!re.IsMatch(url) ) + //{ + Logger.Debug($"IP中间件请求访问,IP[{userip}]"); + //} + + //两种方式传递下去 一是invoke 一个直接next + //await _next.Invoke(context); + + await _next(context); + } + } + + public static class RequestIPMiddlewareExtensions + { + /// + /// 用于引用请求IP中间件 + /// + /// 扩展类型 + /// IApplicationBuilder + public static IApplicationBuilder UseRequestIPMiddleware(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} diff --git a/ZR.Admin.WebApi/NLog.config b/ZR.Admin.WebApi/NLog.config new file mode 100644 index 0000000..a9d6783 --- /dev/null +++ b/ZR.Admin.WebApi/NLog.config @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZR.Admin.WebApi/Program.cs b/ZR.Admin.WebApi/Program.cs new file mode 100644 index 0000000..07633fb --- /dev/null +++ b/ZR.Admin.WebApi/Program.cs @@ -0,0 +1,29 @@ +using Infrastructure.Startups; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NLog.Web; + +namespace ZR.Admin.WebApi +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) + { + var builder = Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder + .Init("Infrastructure") + .UseNLog() + .UseStartup(); + }); + + return builder; + } + } +} diff --git a/ZR.Admin.WebApi/Properties/PublishProfiles/FolderProfile.pubxml b/ZR.Admin.WebApi/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..48bf8f3 --- /dev/null +++ b/ZR.Admin.WebApi/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,20 @@ + + + + + True + False + True + Release + Any CPU + FileSystem + bin\Release\net5.0\publish\ + FileSystem + + net5.0 + ba176987-aabb-406a-b4a8-6b63b422b703 + false + + \ No newline at end of file diff --git a/ZR.Admin.WebApi/Properties/launchSettings.json b/ZR.Admin.WebApi/Properties/launchSettings.json new file mode 100644 index 0000000..c5c2293 --- /dev/null +++ b/ZR.Admin.WebApi/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:50509/", + "sslPort": 44365 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "ZRAdmin": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Stage" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/ZR.Admin.WebApi/Startup.cs b/ZR.Admin.WebApi/Startup.cs new file mode 100644 index 0000000..360469a --- /dev/null +++ b/ZR.Admin.WebApi/Startup.cs @@ -0,0 +1,127 @@ +using Hei.Captcha; +using Infrastructure; +using Infrastructure.Extensions; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Razor; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; +using System.IO; +using ZR.Admin.WebApi.Extensions; +using ZR.Admin.WebApi.Filters; +using ZR.Admin.WebApi.Middleware; + +namespace ZR.Admin.WebApi +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + public void ConfigureServices(IServiceCollection services) + { + string corsUrls = Configuration["sysConfig:cors"]; + + //ÅäÖÿçÓò + services.AddCors(c => + { + c.AddPolicy("Policy", policy => + { + policy.WithOrigins(corsUrls.Split(',', System.StringSplitOptions.RemoveEmptyEntries)) + .AllowAnyHeader()//ÔÊÐíÈÎÒâͷ + .AllowCredentials()//ÔÊÐícookie + .AllowAnyMethod();//ÔÊÐíÈÎÒⷽ·¨ + }); + }); + //Ïû³ýError unprotecting the session cookie¾¯¸æ + services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection")); + //ÆÕͨÑé֤Âë + services.AddHeiCaptcha(); + services.AddSession(); + services.AddHttpContextAccessor(); + + //Cookie ÈÏ֤ + services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(); + + //°ó¶¨Õû¸ö¶ÔÏóµ½ModelÉÏ + services.Configure(Configuration); + + InjectRepositories(services); + + services.AddMvc(options => + { + options.Filters.Add(typeof(GlobalActionMonitor));//ȫ¾Öע²áÒ쳣 + }) + .AddMvcLocalization() + .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix); + + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "ZrAdmin", Version = "v1" }); + }); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ZrAdmin v1")); + + //ʹ¿ÉÒԶà´ζàȥbodyÄÚÈÝ + app.Use((context, next) => + { + context.Request.EnableBuffering(); + return next(); + }); + //¿ªÆô·ÃÎʾ²̬Îļþ/wwwrootĿ¼Îļþ£¬Ҫ·ÅÔÚUseRoutingǰÃæ + app.UseStaticFiles(); + + app.UseRouting(); + app.UseCors("Policy");//Ҫ·ÅÔÚapp.UseEndpointsǰ¡£ + + //app.UseAuthentication»áÆôÓÃAuthenticationÖмä¼þ£¬¸ÃÖмä¼þ»á¸ù¾ݵ±ǰHttpÇëÇóÖеÄCookieÐÅϢÀ´ÉèÖÃHttpContext.UserÊôÐԣ¨ºóÃæ»áÓõ½£©£¬ + //ËùÒÔֻÓÐÔÚapp.UseAuthentication·½·¨֮ºóע²áµÄÖмä¼þ²ÅÄܹ»´ÓHttpContext.UserÖжÁȡµ½ֵ£¬ + //ÕâҲÊÇΪʲôÉÏÃæǿµ÷app.UseAuthentication·½·¨һ¶¨Ҫ·ÅÔÚÏÂÃæµÄapp.UseMvc·½·¨ǰÃ棬ÒòΪֻÓÐÕâÑùASP.NET CoreµÄMVCÖмä¼þÖвÅÄܶÁȡµ½HttpContext.UserµÄֵ¡£ + app.UseAuthentication(); + app.UseAuthorization(); + app.UseSession(); + app.UseResponseCaching(); + + // »ָ´/Æô¶¯ÈÎÎñ + app.UseAddTaskSchedulers(); + + app.UseMiddleware(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + }); + } + + /// + /// ע²áServices·þÎñ + /// + /// + private static void InjectRepositories(IServiceCollection services) + { + services.AddAppService(); + + //¿ªÆô¼ƻ®ÈÎÎñ + services.AddTaskSchedulers(); + } + } +} diff --git a/ZR.Admin.WebApi/Template/Controller.cs b/ZR.Admin.WebApi/Template/Controller.cs new file mode 100644 index 0000000..6b343ed --- /dev/null +++ b/ZR.Admin.WebApi/Template/Controller.cs @@ -0,0 +1,130 @@ + +//------------------------------------------------------------------------------ +// +// 此代码已从模板生成。 +// +// 手动更改此文件可能导致应用程序出现意外的行为。 +// 如果重新生成代码,将覆盖对此文件的手动更改。 +// +//------------------------------------------------------------------------------ +using Infrastructure.Attribute; +using Microsoft.AspNetCore.Mvc; +using ZR.Admin.WebApi.Filters; +using ZR.Model.System; +using ZR.Service.IService; +using Infrastructure.Model; +using SqlSugar; +using Mapster; +using ZR.Model.Dto.System; +using Infrastructure.Enums; +using Infrastructure; +using ZR.Admin.WebApi.Extensions; + +namespace ZR.Admin.WebApi.Controllers +{ + /// + /// T4代码自动生成 + /// + + [Verify] + [Route("system/SysFile")] + public class SysFile1Controller: BaseController + { + /// + /// 文件管理接口 + /// + private readonly ISysFileService _SysFileService; + + public SysFile1Controller(ISysFileService SysFileService) + { + _SysFileService = SysFileService; + } + + /// + /// 查询文件管理列表 + /// + /// + [HttpGet("list")] + //[ActionPermissionFilter(Permission = "system:SysFile:list")] + public IActionResult Query([FromQuery] SysFileQueryDto parm) + { + //开始拼装查询条件 + var predicate = Expressionable.Create(); + + //TODO 搜索条件 + //predicate = predicate.And(m => m.Name.Contains(parm.Name)); + + var response = _SysFileService.GetPages(predicate.ToExpression(), parm); + + return SUCCESS(response); + } + + /// + /// 查询文件管理详情 + /// + /// + /// + [HttpGet("{id}")] + public IActionResult Get(string id) + { + var response = _SysFileService.GetId(id); + + return SUCCESS(response); + } + + /// + /// 添加文件管理 + /// + /// + [HttpPost] + //[ActionPermissionFilter(Permission = "system:SysFile:add")] + [Log(Title = "文件管理添加", BusinessType = BusinessType.INSERT)] + public IActionResult Create([FromBody] SysFile parm) + { + if (parm == null) + { + throw new CustomException("请求参数错误"); + } + //从 Dto 映射到 实体 + var addModel = parm.Adapt().ToCreate(); + //addModel.CreateID = User.Identity.Name; + + return SUCCESS(_SysFileService.Add(addModel)); + } + + /// + /// 更新文件管理 + /// + /// + [HttpPut("edit")] + //[ActionPermissionFilter(Permission = "system:SysFile:update")] + [Log(Title = "文件管理修改", BusinessType = BusinessType.UPDATE)] + public IActionResult Update([FromBody] SysFile parm) + { + //从 Dto 映射到 实体 + var addModel = parm.Adapt().ToCreate(); + //addModel.CreateID = User.Identity.Name; + //TODO 字段映射 + var response = _SysFileService.Update(addModel); + + return SUCCESS(response); + } + + /// + /// 删除文件管理 + /// + /// + [HttpDelete("{id}")] + //[ActionPermissionFilter(Permission = "system:SysFile:delete")] + [Log(Title = "文件管理删除", BusinessType = BusinessType.DELETE)] + public IActionResult Delete(int id = 0) + { + if (id <= 0) { return OutputJson(ApiResult.Error($"删除失败Id 不能为空")); } + + // 删除文件管理 + var response = _SysFileService.Delete(id); + + return SUCCESS(response); + } + } +} \ No newline at end of file diff --git a/ZR.Admin.WebApi/Template/Controller.tt b/ZR.Admin.WebApi/Template/Controller.tt new file mode 100644 index 0000000..3646cc6 --- /dev/null +++ b/ZR.Admin.WebApi/Template/Controller.tt @@ -0,0 +1,145 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> + +<# +string ModelName="SysFile"; +string ControllerName="SysFile"; +string ServiceName="SysFileService"; +string FileName="文件管理"; +string ModelNameSpace="ZRAdmin.Controllers"; +#> +//------------------------------------------------------------------------------ +// +// 此代码已从模板生成。 +// +// 手动更改此文件可能导致应用程序出现意外的行为。 +// 如果重新生成代码,将覆盖对此文件的手动更改。 +// +//------------------------------------------------------------------------------ +using Infrastructure.Attribute; +using Microsoft.AspNetCore.Mvc; +using ZRAdmin.Filters; +using ZR.Model; +using ZR.Model.System; +using ZR.Model.Vo; +using ZR.Service.IService; +using Infrastructure.Model; +using SqlSugar; +using Mapster; +using ZR.Model.Dto.System; +using Infrastructure.Enums; +using Infrastructure; +using ZRAdmin.Extensions; + +namespace <#=ModelNameSpace#> +{ + /// + /// T4代码自动生成 + /// + + [Verify] + [Route("system/<#=ModelName#>")] + public class <#=ControllerName#>1Controller: BaseController + { + /// + /// <#=FileName#>接口 + /// + private readonly I<#=ServiceName#> _<#=ServiceName#>; + + public <#=ControllerName#>1Controller(I<#=ServiceName#> <#=ServiceName#>) + { + _<#=ServiceName#> = <#=ServiceName#>; + } + + /// + /// 查询<#=FileName#>列表 + /// + /// + [HttpGet("list")] + //[ActionPermissionFilter(Permission = "system:<#=ModelName#>:list")] + public IActionResult Query([FromQuery] <#=ModelName#>QueryDto parm) + { + //开始拼装查询条件 + var predicate = Expressionable.Create<<#=ModelName#>>(); + + //TODO 搜索条件 + //predicate = predicate.And(m => m.Name.Contains(parm.Name)); + + var response = _<#=ServiceName#>.GetPages(predicate.ToExpression(), parm); + + return SUCCESS(response); + } + + /// + /// 查询<#=FileName#>详情 + /// + /// + /// + [HttpGet("{id}")] + public IActionResult Get(string id) + { + var response = _<#=ServiceName#>.GetId(id); + + return SUCCESS(response); + } + + /// + /// 添加<#=FileName#> + /// + /// + [HttpPost] + //[ActionPermissionFilter(Permission = "system:<#=ModelName#>:add")] + [Log(Title = "<#=FileName#>添加", BusinessType = BusinessType.INSERT)] + public IActionResult Create([FromBody] <#=ModelName#> parm) + { + if (parm == null) + { + throw new CustomException("请求参数错误"); + } + //从 Dto 映射到 实体 + var addModel = parm.Adapt<<#=ModelName#>>().ToCreate(); + //addModel.CreateID = User.Identity.Name; + + return SUCCESS(_<#=ServiceName#>.Add(addModel)); + } + + /// + /// 更新<#=FileName#> + /// + /// + [HttpPut("edit")] + //[ActionPermissionFilter(Permission = "system:<#=ModelName#>:update")] + [Log(Title = "<#=FileName#>修改", BusinessType = BusinessType.UPDATE)] + public IActionResult Update([FromBody] <#=ModelName#> parm) + { + //从 Dto 映射到 实体 + var addModel = parm.Adapt<<#=ModelName#>>().ToCreate(); + //addModel.CreateID = User.Identity.Name; + //TODO 字段映射 + var response = _<#=ServiceName#>.Update(addModel); + + return SUCCESS(response); + } + + /// + /// 删除<#=FileName#> + /// + /// + [HttpDelete("{id}")] + //[ActionPermissionFilter(Permission = "system:<#=ModelName#>:delete")] + [Log(Title = "<#=FileName#>删除", BusinessType = BusinessType.DELETE)] + public IActionResult Delete(int id = 0) + { + if (id <= 0) { return OutputJson(ApiResult.Error($"删除失败Id 不能为空")); } + + // 删除<#=FileName#> + var response = _<#=ServiceName#>.Delete(id); + + return SUCCESS(response); + } + } +} \ No newline at end of file diff --git a/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj b/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj new file mode 100644 index 0000000..9f82d37 --- /dev/null +++ b/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj @@ -0,0 +1,78 @@ + + + + net5.0 + bc51af00-60b6-4e2a-935e-267c368c890a + false + zh-Hans + + + + ..\ZRAdmin.xml + 1701;1702;1591;1570 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + + + Always + + + Always + + + Always + + + TextTemplatingFileGenerator + Controller.cs + + + + + + + + + + + + + + + True + True + Controller.tt + + + + diff --git a/ZR.Admin.WebApi/appsettings.Development.json b/ZR.Admin.WebApi/appsettings.Development.json new file mode 100644 index 0000000..8d64c6d --- /dev/null +++ b/ZR.Admin.WebApi/appsettings.Development.json @@ -0,0 +1,30 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "appName": "ZR Admin System", + "ConnectionStrings": { + "Conn_Admin": "server=127.0.0.1;database=admin;user=zr;pwd=abc" + }, + "urls": "http://localhost:8888", //Æô¶¯url + "sysConfig": { + "DBCommandTimeout": 10, + "cors": "http://localhost:8887" //ÅäÖÿçÓòµØַ + }, + "DemoMode": false, //ÊǷñÑÝʾģʽ + "DbKey": "", + "DbType": 0, //MySql = 0, SqlServer = 1, Sqlite = 2, Oracle = 3, PostgreSQL = 4, + "Upload": { + "UploadDirectory": "/", + "UploadUrl": "http://localhost:8888" + }, + "ALYUN_OCS": { + "REGIONID": "cn-hangzhou", + "KEY": "XX", + "SECRET": "XX" + } +} diff --git a/ZR.Admin.WebApi/appsettings.Production.json b/ZR.Admin.WebApi/appsettings.Production.json new file mode 100644 index 0000000..68ffe8e --- /dev/null +++ b/ZR.Admin.WebApi/appsettings.Production.json @@ -0,0 +1,30 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "appName": "ZR Admin system", + "ConnectionStrings": { + "Conn_Admin": "server=127.0.0.1;database=admin;user=zr;pwd=abc" + }, + "urls": "http://localhost:8888", + "sysConfig": { + "DBCommandTimeout": 10, + "cors": "http://localhost:8887" + }, + "DemoMode": false, //ÊǷñÑÝʾģʽ + "DbKey": "", + "DbType": 0, //MySql = 0, SqlServer = 1, Sqlite = 2, Oracle = 3, PostgreSQL = 4, + "Upload": { + "UploadDirectory": "/", + "UploadUrl": "http://localhost:8888" + }, + "ALYUN_OCS": { + "REGIONID": "cn-hangzhou", + "KEY": "XX", + "SECRET": "XX" + } +} diff --git a/ZR.Admin.WebApi/appsettings.json b/ZR.Admin.WebApi/appsettings.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/ZR.Admin.WebApi/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/ZR.Admin.WebApi/build.bat b/ZR.Admin.WebApi/build.bat new file mode 100644 index 0000000..f3d0008 --- /dev/null +++ b/ZR.Admin.WebApi/build.bat @@ -0,0 +1,8 @@ +@echo off + +docker build -t ZRAdmin:latest -f ./Dockerfile . + +echo "==============²鿴¾µÏñ===========" +docker images + +pause \ No newline at end of file diff --git a/ZR.Admin.WebApi/ip2region.db b/ZR.Admin.WebApi/ip2region.db new file mode 100644 index 0000000..0fc60e6 Binary files /dev/null and b/ZR.Admin.WebApi/ip2region.db differ diff --git a/ZR.CodeGenerator/TableMappingHelper.cs b/ZR.CodeGenerator/TableMappingHelper.cs new file mode 100644 index 0000000..e0c9daa --- /dev/null +++ b/ZR.CodeGenerator/TableMappingHelper.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Infrastructure.Extensions; +using ZR.Common.Extension; + +namespace ZR.CodeGenerator.CodeGenerator +{ + public class TableMappingHelper + { + /// + /// UserService转成userService + /// + /// + /// + public static string FirstLetterLowercase(string instanceName) + { + instanceName = instanceName.ParseToString(); + if (!instanceName.IsEmpty()) + { + StringBuilder sb = new StringBuilder(); + sb.Append(instanceName[0].ToString().ToLower() + instanceName.Substring(1)); + return sb.ToString(); + } + else + { + return instanceName; + } + } + + /// + /// sys_menu_authorize变成MenuAuthorize + /// + public static string GetClassNamePrefix(string tableName) + { + string[] arr = tableName.Split('_'); + StringBuilder sb = new StringBuilder(); + for (int i = 1; i < arr.Length; i++) + { + sb.Append(arr[i][0].ToString().ToUpper() + arr[i].Substring(1)); + } + return sb.ToString(); + } + + public static string GetPropertyDatatype(string sDatatype) + { + string sTempDatatype = string.Empty; + sDatatype = sDatatype.ToLower(); + switch (sDatatype) + { + case "int": + case "number": + case "integer": + case "smallint": + sTempDatatype = "int?"; + break; + + case "bigint": + sTempDatatype = "long?"; + break; + + case "tinyint": + sTempDatatype = "byte?"; + break; + + case "numeric": + case "real": + sTempDatatype = "Single?"; + break; + + case "float": + sTempDatatype = "float?"; + break; + + case "decimal": + case "numer(8,2)": + sTempDatatype = "decimal?"; + break; + + case "bit": + sTempDatatype = "bool?"; + break; + + case "date": + case "datetime": + case "datetime2": + case "smalldatetime": + sTempDatatype = "DateTime?"; + break; + + case "money": + case "smallmoney": + sTempDatatype = "double?"; + break; + + case "char": + case "varchar": + case "nvarchar2": + case "text": + case "nchar": + case "nvarchar": + case "ntext": + default: + sTempDatatype = "string"; + break; + } + return sTempDatatype; + } + } +} diff --git a/ZR.CodeGenerator/ZR.CodeGenerator.csproj b/ZR.CodeGenerator/ZR.CodeGenerator.csproj new file mode 100644 index 0000000..948f805 --- /dev/null +++ b/ZR.CodeGenerator/ZR.CodeGenerator.csproj @@ -0,0 +1,18 @@ + + + + net5.0 + + + + + + + + + + + + + + diff --git a/ZR.Common/Cache/CacheHelper.cs b/ZR.Common/Cache/CacheHelper.cs new file mode 100644 index 0000000..9a4c8dc --- /dev/null +++ b/ZR.Common/Cache/CacheHelper.cs @@ -0,0 +1,122 @@ + +using Microsoft.Extensions.Caching.Memory; +using System; +using System.Web; + +/// +/// 数据缓存操作类 author by zhaorui 2021-3-6 +/// +namespace ZR.Common +{ + public class CacheHelper + { + public static MemoryCache Cache { get; set; } + static CacheHelper() + { + Cache = new MemoryCache(new MemoryCacheOptions + { + //SizeLimit = 1024 + }); + } + + /// + /// 全局缓存时间 + /// + public static int CacheTime = 5; //ConfigHelper.GetConfigInt("CacheTime"); + + /// + /// 获取缓存 + /// + /// + /// + /// + public static T GetCache(string key) where T : class + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + //return Cache.Get(key) as T; //或者 + return Cache.Get(key); + } + + /// + /// 获取缓存 + /// + /// + /// + public static object GetCache(string CacheKey) + { + return Cache.Get(CacheKey); + //return HttpRuntime.Cache[CacheKey]; + } + + public static object Get(string CacheKey) + { + return Cache.Get(CacheKey); + } + + /// + /// 设置缓存,永久缓存 + /// + /// key + /// 值 + public static void SetCache(string CacheKey, object objObject) + { + Cache.Set(CacheKey, objObject); + } + + /// + /// 设置缓存 + /// + /// key + /// 值 + /// 过期时间(分钟) + public static void SetCache(string CacheKey, object objObject, int Timeout) + { + Cache.Set(CacheKey, objObject, DateTime.Now.AddMinutes(Timeout)); + } + + /// + /// 设置缓存(秒) + /// + /// key + /// 值 + /// 过期时间(秒) + public static void SetCaches(string CacheKey, object objObject, int Timeout) + { + Cache.Set(CacheKey, objObject, DateTime.Now.AddSeconds(Timeout)); + } + + /// + /// 设置缓存 + /// + /// key + /// 值 + /// 过期时间 + /// 过期时间间隔 + public static void SetCache(string CacheKey, object objObject, DateTime absoluteExpiration, TimeSpan slidingExpiration) + { + Cache.Set(CacheKey, objObject, absoluteExpiration); + } + + /// + /// 设定绝对的过期时间 + /// + /// + /// + /// 超过多少秒后过期 + public static void SetCacheDateTime(string CacheKey, object objObject, long Seconds) + { + Cache.Set(CacheKey, objObject, DateTime.Now.AddSeconds(Seconds)); + } + + /// + /// 删除缓存 + /// + /// key + public static void Remove(string key) + { + Cache.Remove(key); + } + } +} + diff --git a/ZR.Common/ComputerHelper.cs b/ZR.Common/ComputerHelper.cs new file mode 100644 index 0000000..041022f --- /dev/null +++ b/ZR.Common/ComputerHelper.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using ZR.Common.Extension; + +namespace ZR.Common +{ + public class ComputerHelper + { + public static ComputerInfo GetComputerInfo() + { + ComputerInfo computerInfo = new ComputerInfo(); + try + { + MemoryMetricsClient client = new MemoryMetricsClient(); + MemoryMetrics memoryMetrics = client.GetMetrics(); + computerInfo.TotalRAM = Math.Ceiling(memoryMetrics.Total / 1024).ToString() + " GB"; + computerInfo.RAMRate = Math.Ceiling(100 * memoryMetrics.Used / memoryMetrics.Total).ToString() + " %"; + computerInfo.CPURate = Math.Ceiling(GetCPURate().ParseToDouble()) + " %"; + computerInfo.RunTime = GetRunTime(); + } + catch (Exception ex) + { + //LogHelper.Error(ex); + } + return computerInfo; + } + + public static bool IsUnix() + { + var isUnix = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + return isUnix; + } + + public static string GetCPURate() + { + string cpuRate = string.Empty; + if (IsUnix()) + { + string output = ShellHelper.Bash("top -b -n1 | grep \"Cpu(s)\" | awk '{print $2 + $4}'"); + cpuRate = output.Trim(); + } + else + { + string output = ShellHelper.Cmd("wmic", "cpu get LoadPercentage"); + cpuRate = output.Replace("LoadPercentage", string.Empty).Trim(); + } + return cpuRate; + } + + public static string GetRunTime() + { + string runTime = string.Empty; + try + { + if (IsUnix()) + { + string output = ShellHelper.Bash("uptime -s"); + output = output.Trim(); + runTime = DateTimeHelper.FormatTime((DateTime.Now - output.ParseToDateTime()).TotalMilliseconds.ToString().Split('.')[0].ParseToLong()); + } + else + { + string output = ShellHelper.Cmd("wmic", "OS get LastBootUpTime/Value"); + string[] outputArr = output.Split('=', (char)StringSplitOptions.RemoveEmptyEntries); + if (outputArr.Length == 2) + { + runTime = DateTimeHelper.FormatTime((DateTime.Now - outputArr[1].Split('.')[0].ParseToDateTime()).TotalMilliseconds.ToString().Split('.')[0].ParseToLong()); + } + } + } + catch (Exception ex) + { + //LogHelper.Error(ex); + } + return runTime; + } + } + + public class MemoryMetrics + { + public double Total { get; set; } + public double Used { get; set; } + public double Free { get; set; } + } + + public class MemoryMetricsClient + { + public MemoryMetrics GetMetrics() + { + if (ComputerHelper.IsUnix()) + { + return GetUnixMetrics(); + } + return GetWindowsMetrics(); + } + + private MemoryMetrics GetWindowsMetrics() + { + string output = ShellHelper.Cmd("wmic", "OS get FreePhysicalMemory,TotalVisibleMemorySize /Value"); + + var lines = output.Trim().Split('\n'); + var freeMemoryParts = lines[0].Split('=', (char)StringSplitOptions.RemoveEmptyEntries); + var totalMemoryParts = lines[1].Split('=', (char)StringSplitOptions.RemoveEmptyEntries); + + var metrics = new MemoryMetrics(); + metrics.Total = Math.Round(double.Parse(totalMemoryParts[1]) / 1024, 0); + metrics.Free = Math.Round(double.Parse(freeMemoryParts[1]) / 1024, 0); + metrics.Used = metrics.Total - metrics.Free; + + return metrics; + } + + private MemoryMetrics GetUnixMetrics() + { + string output = ShellHelper.Bash("free -m"); + + var lines = output.Split('\n'); + var memory = lines[1].Split(' ', (char)StringSplitOptions.RemoveEmptyEntries); + + var metrics = new MemoryMetrics(); + metrics.Total = double.Parse(memory[1]); + metrics.Used = double.Parse(memory[2]); + metrics.Free = double.Parse(memory[3]); + + return metrics; + } + } + + public class ComputerInfo + { + /// + /// CPU使用率 + /// + public string CPURate { get; set; } + /// + /// 总内存 + /// + public string TotalRAM { get; set; } + /// + /// 内存使用率 + /// + public string RAMRate { get; set; } + /// + /// 系统运行时间 + /// + public string RunTime { get; set; } + } +} \ No newline at end of file diff --git a/ZR.Common/DateTimeHelper.cs b/ZR.Common/DateTimeHelper.cs new file mode 100644 index 0000000..2fb05d7 --- /dev/null +++ b/ZR.Common/DateTimeHelper.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Common +{ + public class DateTimeHelper + { + /// + /// + /// + /// + /// + public static DateTime GetBeginTime(DateTime? dateTime, int days = 0) + { + if (dateTime == DateTime.MinValue || dateTime == null) + { + return DateTime.Now.AddDays(days); + } + return dateTime ?? DateTime.Now; + } + + #region 毫秒转天时分秒 + /// + /// 毫秒转天时分秒 + /// + /// + /// + public static string FormatTime(long ms) + { + int ss = 1000; + int mi = ss * 60; + int hh = mi * 60; + int dd = hh * 24; + + long day = ms / dd; + long hour = (ms - day * dd) / hh; + long minute = (ms - day * dd - hour * hh) / mi; + long second = (ms - day * dd - hour * hh - minute * mi) / ss; + long milliSecond = ms - day * dd - hour * hh - minute * mi - second * ss; + + string sDay = day < 10 ? "0" + day : "" + day; //天 + string sHour = hour < 10 ? "0" + hour : "" + hour;//小时 + string sMinute = minute < 10 ? "0" + minute : "" + minute;//分钟 + string sSecond = second < 10 ? "0" + second : "" + second;//秒 + string sMilliSecond = milliSecond < 10 ? "0" + milliSecond : "" + milliSecond;//毫秒 + sMilliSecond = milliSecond < 100 ? "0" + sMilliSecond : "" + sMilliSecond; + + return string.Format("{0} 天 {1} 小时 {2} 分 {3} 秒", sDay, sHour, sMinute, sSecond); + } + #endregion + + #region 获取unix时间戳 + /// + /// 获取unix时间戳 + /// + /// + /// + public static long GetUnixTimeStamp(DateTime dt) + { + long unixTime = ((DateTimeOffset)dt).ToUnixTimeMilliseconds(); + return unixTime; + } + #endregion + + #region 获取日期天的最小时间 + public static DateTime GetDayMinDate(DateTime dt) + { + DateTime min = new DateTime(dt.Year, dt.Month, dt.Day, 0, 0, 0); + return min; + } + #endregion + + #region 获取日期天的最大时间 + public static DateTime GetDayMaxDate(DateTime dt) + { + DateTime max = new DateTime(dt.Year, dt.Month, dt.Day, 23, 59, 59); + return max; + } + #endregion + + #region 获取日期天的最大时间 + public static string FormatDateTime(DateTime? dt) + { + if (dt != null) + { + if (dt.Value.Year == DateTime.Now.Year) + { + return dt.Value.ToString("MM-dd HH:mm"); + } + else + { + return dt.Value.ToString("yyyy-MM-dd HH:mm"); + } + } + return string.Empty; + } + #endregion + } +} diff --git a/ZR.Common/Extension/Extension.Convert.cs b/ZR.Common/Extension/Extension.Convert.cs new file mode 100644 index 0000000..febe6bb --- /dev/null +++ b/ZR.Common/Extension/Extension.Convert.cs @@ -0,0 +1,446 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZR.Common.Extension +{ + public static partial class Extensions + { + #region 转换为long + /// + /// 将object转换为long,若转换失败,则返回0。不抛出异常。 + /// + /// + /// + public static long ParseToLong(this object obj) + { + try + { + return long.Parse(obj.ToString()); + } + catch + { + return 0L; + } + } + + /// + /// 将object转换为long,若转换失败,则返回指定值。不抛出异常。 + /// + /// + /// + /// + public static long ParseToLong(this string str, long defaultValue) + { + try + { + return long.Parse(str); + } + catch + { + return defaultValue; + } + } + #endregion + + #region 转换为int + /// + /// 将object转换为int,若转换失败,则返回0。不抛出异常。 + /// + /// + /// + public static int ParseToInt(this object str) + { + try + { + return Convert.ToInt32(str); + } + catch + { + return 0; + } + } + + /// + /// 将object转换为int,若转换失败,则返回指定值。不抛出异常。 + /// null返回默认值 + /// + /// + /// + /// + public static int ParseToInt(this object str, int defaultValue) + { + if (str == null) + { + return defaultValue; + } + try + { + return Convert.ToInt32(str); + } + catch + { + return defaultValue; + } + } + #endregion + + #region 转换为short + /// + /// 将object转换为short,若转换失败,则返回0。不抛出异常。 + /// + /// + /// + public static short ParseToShort(this object obj) + { + try + { + return short.Parse(obj.ToString()); + } + catch + { + return 0; + } + } + + /// + /// 将object转换为short,若转换失败,则返回指定值。不抛出异常。 + /// + /// + /// + public static short ParseToShort(this object str, short defaultValue) + { + try + { + return short.Parse(str.ToString()); + } + catch + { + return defaultValue; + } + } + #endregion + + #region 转换为demical + /// + /// 将object转换为demical,若转换失败,则返回指定值。不抛出异常。 + /// + /// + /// + public static decimal ParseToDecimal(this object str, decimal defaultValue) + { + try + { + return decimal.Parse(str.ToString()); + } + catch + { + return defaultValue; + } + } + + /// + /// 将object转换为demical,若转换失败,则返回0。不抛出异常。 + /// + /// + /// + public static decimal ParseToDecimal(this object str) + { + try + { + return decimal.Parse(str.ToString()); + } + catch + { + return 0; + } + } + #endregion + + #region 转化为bool + /// + /// 将object转换为bool,若转换失败,则返回false。不抛出异常。 + /// + /// + /// + public static bool ParseToBool(this object str) + { + try + { + return bool.Parse(str.ToString()); + } + catch + { + return false; + } + } + + /// + /// 将object转换为bool,若转换失败,则返回指定值。不抛出异常。 + /// + /// + /// + public static bool ParseToBool(this object str, bool result) + { + try + { + return bool.Parse(str.ToString()); + } + catch + { + return result; + } + } + #endregion + + #region 转换为float + /// + /// 将object转换为float,若转换失败,则返回0。不抛出异常。 + /// + /// + /// + public static float ParseToFloat(this object str) + { + try + { + return float.Parse(str.ToString()); + } + catch + { + return 0; + } + } + + /// + /// 将object转换为float,若转换失败,则返回指定值。不抛出异常。 + /// + /// + /// + public static float ParseToFloat(this object str, float result) + { + try + { + return float.Parse(str.ToString()); + } + catch + { + return result; + } + } + #endregion + + #region 转换为Guid + /// + /// 将string转换为Guid,若转换失败,则返回Guid.Empty。不抛出异常。 + /// + /// + /// + public static Guid ParseToGuid(this string str) + { + try + { + return new Guid(str); + } + catch + { + return Guid.Empty; + } + } + #endregion + + #region 转换为DateTime + /// + /// 将string转换为DateTime,若转换失败,则返回日期最小值。不抛出异常。 + /// + /// + /// + public static DateTime ParseToDateTime(this string str) + { + try + { + if (string.IsNullOrWhiteSpace(str)) + { + return DateTime.MinValue; + } + if (str.Contains("-") || str.Contains("/")) + { + return DateTime.Parse(str); + } + else + { + int length = str.Length; + switch (length) + { + case 4: + return DateTime.ParseExact(str, "yyyy", System.Globalization.CultureInfo.CurrentCulture); + case 6: + return DateTime.ParseExact(str, "yyyyMM", System.Globalization.CultureInfo.CurrentCulture); + case 8: + return DateTime.ParseExact(str, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture); + case 10: + return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture); + case 12: + return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture); + case 14: + return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture); + default: + return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture); + } + } + } + catch + { + return DateTime.MinValue; + } + } + + /// + /// 将string转换为DateTime,若转换失败,则返回默认值。 + /// + /// + /// + /// + public static DateTime ParseToDateTime(this string str, DateTime? defaultValue) + { + try + { + if (string.IsNullOrWhiteSpace(str)) + { + return defaultValue.GetValueOrDefault(); + } + if (str.Contains("-") || str.Contains("/")) + { + return DateTime.Parse(str); + } + else + { + int length = str.Length; + switch (length) + { + case 4: + return DateTime.ParseExact(str, "yyyy", System.Globalization.CultureInfo.CurrentCulture); + case 6: + return DateTime.ParseExact(str, "yyyyMM", System.Globalization.CultureInfo.CurrentCulture); + case 8: + return DateTime.ParseExact(str, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture); + case 10: + return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture); + case 12: + return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture); + case 14: + return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture); + default: + return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture); + } + } + } + catch + { + return defaultValue.GetValueOrDefault(); + } + } + #endregion + + #region 转换为string + /// + /// 将object转换为string,若转换失败,则返回""。不抛出异常。 + /// + /// + /// + public static string ParseToString(this object obj) + { + try + { + if (obj == null) + { + return string.Empty; + } + else + { + return obj.ToString(); + } + } + catch + { + return string.Empty; + } + } + public static string ParseToStrings(this object obj) + { + try + { + var list = obj as IEnumerable; + if (list != null) + { + return string.Join(",", list); + } + else + { + return obj.ToString(); + } + } + catch + { + return string.Empty; + } + + } + #endregion + + #region 转换为double + /// + /// 将object转换为double,若转换失败,则返回0。不抛出异常。 + /// + /// + /// + public static double ParseToDouble(this object obj) + { + try + { + return double.Parse(obj.ToString()); + } + catch + { + return 0; + } + } + + /// + /// 将object转换为double,若转换失败,则返回指定值。不抛出异常。 + /// + /// + /// + /// + public static double ParseToDouble(this object str, double defaultValue) + { + try + { + return double.Parse(str.ToString()); + } + catch + { + return defaultValue; + } + } + #endregion + + #region 强制转换类型 + /// + /// 强制转换类型 + /// + /// + /// + /// + public static IEnumerable CastSuper(this IEnumerable source) + { + foreach (object item in source) + { + yield return (TResult)Convert.ChangeType(item, typeof(TResult)); + } + } + #endregion + } +} diff --git a/ZR.Common/Extension/Extension.Enum.cs b/ZR.Common/Extension/Extension.Enum.cs new file mode 100644 index 0000000..d5be020 --- /dev/null +++ b/ZR.Common/Extension/Extension.Enum.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +//using Newtonsoft.Json; + +namespace ZR.Common.Extension +{ + public static partial class Extensions + { + #region 枚举成员转成dictionary类型 + /// + /// 转成dictionary类型 + /// + /// + /// + public static Dictionary EnumToDictionary(this Type enumType) + { + Dictionary dictionary = new Dictionary(); + Type typeDescription = typeof(DescriptionAttribute); + FieldInfo[] fields = enumType.GetFields(); + int sValue = 0; + string sText = string.Empty; + foreach (FieldInfo field in fields) + { + if (field.FieldType.IsEnum) + { + sValue = ((int)enumType.InvokeMember(field.Name, BindingFlags.GetField, null, null, null)); + object[] arr = field.GetCustomAttributes(typeDescription, true); + if (arr.Length > 0) + { + DescriptionAttribute da = (DescriptionAttribute)arr[0]; + sText = da.Description; + } + else + { + sText = field.Name; + } + dictionary.Add(sValue, sText); + } + } + return dictionary; + } + /// + /// 枚举成员转成键值对Json字符串 + /// + /// + /// + //public static string EnumToDictionaryString(this Type enumType) + //{ + // List> dictionaryList = EnumToDictionary(enumType).ToList(); + // var sJson = JsonConvert.SerializeObject(dictionaryList); + // return sJson; + //} + #endregion + + #region 获取枚举的描述 + /// + /// 获取枚举值对应的描述 + /// + /// + /// + public static string GetDescription(this System.Enum enumType) + { + FieldInfo EnumInfo = enumType.GetType().GetField(enumType.ToString()); + if (EnumInfo != null) + { + DescriptionAttribute[] EnumAttributes = (DescriptionAttribute[])EnumInfo.GetCustomAttributes(typeof(DescriptionAttribute), false); + if (EnumAttributes.Length > 0) + { + return EnumAttributes[0].Description; + } + } + return enumType.ToString(); + } + #endregion + + #region 根据值获取枚举的描述 + public static string GetDescriptionByEnum(this object obj) + { + var tEnum = System.Enum.Parse(typeof(T), obj.ParseToString()) as System.Enum; + var description = tEnum.GetDescription(); + return description; + } + #endregion + } +} diff --git a/ZR.Common/Extension/Extension.Exception.cs b/ZR.Common/Extension/Extension.Exception.cs new file mode 100644 index 0000000..f021439 --- /dev/null +++ b/ZR.Common/Extension/Extension.Exception.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZR.Common.Extension +{ + public static partial class Extensions + { + public static Exception GetOriginalException(this Exception ex) + { + if (ex.InnerException == null) return ex; + + return ex.InnerException.GetOriginalException(); + } + } +} diff --git a/ZR.Common/Extension/Extension.Linq.cs b/ZR.Common/Extension/Extension.Linq.cs new file mode 100644 index 0000000..45425ec --- /dev/null +++ b/ZR.Common/Extension/Extension.Linq.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace ZR.Common.Extension +{ + public static class LinqExtensions + { + public static Expression Property(this Expression expression, string propertyName) + { + return Expression.Property(expression, propertyName); + } + public static Expression AndAlso(this Expression left, Expression right) + { + return Expression.AndAlso(left, right); + } + public static Expression Call(this Expression instance, string methodName, params Expression[] arguments) + { + return Expression.Call(instance, instance.Type.GetMethod(methodName), arguments); + } + public static Expression GreaterThan(this Expression left, Expression right) + { + return Expression.GreaterThan(left, right); + } + public static Expression ToLambda(this Expression body, params ParameterExpression[] parameters) + { + return Expression.Lambda(body, parameters); + } + + public static Expression> True() { return param => true; } + + public static Expression> False() { return param => false; } + + /// + /// 组合And + /// + /// + public static Expression> And(this Expression> first, Expression> second) + { + return first.Compose(second, Expression.AndAlso); + } + /// + /// 组合Or + /// + /// + public static Expression> Or(this Expression> first, Expression> second) + { + return first.Compose(second, Expression.OrElse); + } + + /// + /// Combines the first expression with the second using the specified merge function. + /// + static Expression Compose(this Expression first, Expression second, Func merge) + { + var map = first.Parameters + .Select((f, i) => new { f, s = second.Parameters[i] }) + .ToDictionary(p => p.s, p => p.f); + var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); + return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); + } + + /// + /// ParameterRebinder + /// + private class ParameterRebinder : ExpressionVisitor + { + /// + /// The ParameterExpression map + /// + readonly Dictionary map; + /// + /// Initializes a new instance of the class. + /// + /// The map. + ParameterRebinder(Dictionary map) + { + this.map = map ?? new Dictionary(); + } + /// + /// Replaces the parameters. + /// + /// The map. + /// The exp. + /// Expression + public static Expression ReplaceParameters(Dictionary map, Expression exp) + { + return new ParameterRebinder(map).Visit(exp); + } + /// + /// Visits the parameter. + /// + /// The p. + /// Expression + protected override Expression VisitParameter(ParameterExpression p) + { + ParameterExpression replacement; + + if (map.TryGetValue(p, out replacement)) + { + p = replacement; + } + return base.VisitParameter(p); + } + } + } +} diff --git a/ZR.Common/Extension/Extension.Validate.cs b/ZR.Common/Extension/Extension.Validate.cs new file mode 100644 index 0000000..379972b --- /dev/null +++ b/ZR.Common/Extension/Extension.Validate.cs @@ -0,0 +1,46 @@ +//using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZR.Common.Extension +{ + public static partial class Extensions + { + public static bool IsEmpty(this object value) + { + if (value != null && !string.IsNullOrEmpty(value.ParseToString())) + { + return false; + } + else + { + return true; + } + } + + public static bool IsNullOrZero(this object value) + { + if (value == null || value.ParseToString().Trim() == "0") + { + return true; + } + else + { + return false; + } + } + + //public static bool IsAjaxRequest(this HttpRequest request) + //{ + // if (request == null) + // throw new ArgumentNullException("request"); + + // if (request.Headers != null) + // return request.Headers["X-Requested-With"] == "XMLHttpRequest"; + // return false; + //} + } +} diff --git a/ZR.Common/FileUtil.cs b/ZR.Common/FileUtil.cs new file mode 100644 index 0000000..ae0a079 --- /dev/null +++ b/ZR.Common/FileUtil.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace ZR.Common +{ + public class FileUtil + { + /// + /// 按时间来创建文件夹 + /// + /// + /// eg: /{yourPath}/2020/11/3 + public static string GetdirPath(string path = "") + { + DateTime date = DateTime.Now; + int year = date.Year; + int month = date.Month; + int day = date.Day; + int hour = date.Hour; + + string timeDir = $"{year}/{month}/{day}/{hour}";// date.ToString("yyyyMM/dd/HH/"); + + if (!string.IsNullOrEmpty(path)) + { + timeDir = $"{path}/{timeDir}"; + } + return timeDir; + } + + /// + /// 取文件名的MD5值(16位) + /// + /// 文件名,不包括扩展名 + /// + public static string HashFileName(string str = null) + { + if (string.IsNullOrEmpty(str)) + { + str = Guid.NewGuid().ToString(); + } + MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); + return BitConverter.ToString(md5.ComputeHash(Encoding.Default.GetBytes(str)), 4, 8).Replace("-", ""); + } + } +} diff --git a/ZR.Common/HttpHelper.cs b/ZR.Common/HttpHelper.cs new file mode 100644 index 0000000..403a916 --- /dev/null +++ b/ZR.Common/HttpHelper.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace ZR.Common +{ + public class HttpHelper + { + /// + /// 发起POST同步请求 + /// + /// + /// + /// + /// application/xml、application/json、application/text、application/x-www-form-urlencoded + /// 填充消息头 + /// + public static string HttpPost(string url, string postData = null, string contentType = null, int timeOut = 30, Dictionary headers = null) + { + postData = postData ?? ""; + using (HttpClient client = new HttpClient()) + { + if (headers != null) + { + foreach (var header in headers) + client.DefaultRequestHeaders.Add(header.Key, header.Value); + } + using (HttpContent httpContent = new StringContent(postData, Encoding.UTF8)) + { + if (contentType != null) + httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType); + + HttpResponseMessage response = client.PostAsync(url, httpContent).Result; + return response.Content.ReadAsStringAsync().Result; + } + } + } + + + /// + /// 发起POST异步请求 + /// + /// + /// + /// application/xml、application/json、application/text、application/x-www-form-urlencoded + /// 填充消息头 + /// + public static async Task HttpPostAsync(string url, string postData = null, string contentType = null, int timeOut = 30, Dictionary headers = null) + { + postData = postData ?? ""; + using (HttpClient client = new HttpClient()) + { + client.Timeout = new TimeSpan(0, 0, timeOut); + if (headers != null) + { + foreach (var header in headers) + client.DefaultRequestHeaders.Add(header.Key, header.Value); + } + using (HttpContent httpContent = new StringContent(postData, Encoding.UTF8)) + { + if (contentType != null) + httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType); + + HttpResponseMessage response = await client.PostAsync(url, httpContent); + return await response.Content.ReadAsStringAsync(); + } + } + } + + /// + /// 发起GET同步请求 + /// + /// + /// + /// + /// + public static string HttpGet(string url, Dictionary headers = null) + { + using (HttpClient client = new HttpClient()) + { + if (headers != null) + { + foreach (var header in headers) + client.DefaultRequestHeaders.Add(header.Key, header.Value); + } + else + { + client.DefaultRequestHeaders.Add("ContentType", "application/x-www-form-urlencoded"); + client.DefaultRequestHeaders.Add("UserAgent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); + } + try + { + HttpResponseMessage response = client.GetAsync(url).Result; + return response.Content.ReadAsStringAsync().Result; + } + catch (Exception ex) + { + //TODO 打印日志 + Console.WriteLine($"[Http请求出错]{url}|{ex.Message}"); + } + return ""; + } + } + + /// + /// 发起GET异步请求 + /// + /// + /// + /// + /// + public static async Task HttpGetAsync(string url, Dictionary headers = null) + { + using (HttpClient client = new HttpClient()) + { + if (headers != null) + { + foreach (var header in headers) + client.DefaultRequestHeaders.Add(header.Key, header.Value); + } + HttpResponseMessage response = await client.GetAsync(url); + return await response.Content.ReadAsStringAsync(); + } + } + } +} diff --git a/ZR.Common/ShellHelper.cs b/ZR.Common/ShellHelper.cs new file mode 100644 index 0000000..c5f7ce3 --- /dev/null +++ b/ZR.Common/ShellHelper.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace ZR.Common +{ + public class ShellHelper + { + public static string Bash(string command) + { + var escapedArgs = command.Replace("\"", "\\\""); + var process = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = "/bin/bash", + Arguments = $"-c \"{escapedArgs}\"", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + } + }; + process.Start(); + string result = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + process.Dispose(); + return result; + } + + public static string Cmd(string fileName, string args) + { + string output = string.Empty; + + var info = new ProcessStartInfo(); + info.FileName = fileName; + info.Arguments = args; + info.RedirectStandardOutput = true; + + using (var process = Process.Start(info)) + { + output = process.StandardOutput.ReadToEnd(); + } + return output; + } + } +} diff --git a/ZR.Common/Tools.cs b/ZR.Common/Tools.cs new file mode 100644 index 0000000..998f390 --- /dev/null +++ b/ZR.Common/Tools.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Common +{ + public class Tools + { + /// + /// 要分割的字符串 eg: 1,3,10,00 + /// + /// + /// + public static long[] SpitLongArrary(string str) + { + string[] strIds = str.Split(',', (char)StringSplitOptions.RemoveEmptyEntries); + long[] infoIdss = Array.ConvertAll(strIds, s => long.Parse(s)); + return infoIdss; + } + + public static int[] SpitIntArrary(string str) + { + string[] strIds = str.Split(',', (char)StringSplitOptions.RemoveEmptyEntries); + int[] infoIdss = Array.ConvertAll(strIds, s => int.Parse(s)); + return infoIdss; + } + + /// + /// 根据日期获取星期几 + /// + public static string GetWeekByDate(DateTime dt) + { + var day = new[] { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" }; + return day[Convert.ToInt32(dt.DayOfWeek.ToString("d"))]; + } + + /// + /// 得到这个月的第几周 + /// + /// 年月日 + /// 传递过来的时间是第几周 + public static int GetWeekNumInMonth(DateTime daytime) + { + int dayInMonth = daytime.Day; + //本月第一天 + DateTime firstDay = daytime.AddDays(1 - daytime.Day); + //本月第一天是周几 + int weekday = (int)firstDay.DayOfWeek == 0 ? 7 : (int)firstDay.DayOfWeek; + //本月第一周有几天 + int firstWeekEndDay = 7 - (weekday - 1); + //当前日期和第一周之差 + int diffday = dayInMonth - firstWeekEndDay; + diffday = diffday > 0 ? diffday : 1; + //当前是第几周,如果整除7就减一天 + int weekNumInMonth = ((diffday % 7) == 0 + ? (diffday / 7 - 1) + : (diffday / 7)) + 1 + (dayInMonth > firstWeekEndDay ? 1 : 0); + return weekNumInMonth; + } + } +} diff --git a/ZR.Common/ZR.Common.csproj b/ZR.Common/ZR.Common.csproj new file mode 100644 index 0000000..bb64769 --- /dev/null +++ b/ZR.Common/ZR.Common.csproj @@ -0,0 +1,11 @@ + + + + net5.0 + + + + + + + diff --git a/ZR.Model/ApiResult.cs b/ZR.Model/ApiResult.cs new file mode 100644 index 0000000..462f8e0 --- /dev/null +++ b/ZR.Model/ApiResult.cs @@ -0,0 +1,103 @@ +using Newtonsoft.Json; +using System.ComponentModel; + +namespace ZRModel +{ + /// + /// 通用json格式实体返回类 + /// Author by zhaorui + /// + public class ApiResult + { + public int Code { get; set; } + public string Msg { get; set; } + /// + /// 如果data值为null,则忽略序列化将不会返回data字段 + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public object Data { get; set; } + + public ApiResult() + { + Code = 0; + Msg = "fail"; + } + + public ApiResult OnSuccess() + { + Code = 100; + Msg = "success"; + return this; + } + public ApiResult(int code, string msg, object data = null) + { + Code = code; + Msg = msg; + Data = data; + } + + public ApiResult OnSuccess(object data = null) + { + Code = (int)ResultCode.SUCCESS; + Msg = "SUCCESS"; + Data = data; + return this; + } + + /// + /// 返回成功消息 + /// + /// 数据对象 + /// 成功消息 + public static ApiResult Success(object data) { return new ApiResult(100, "success", data); } + + /// + /// 返回成功消息 + /// + /// 返回内容 + /// 成功消息 + public static ApiResult Success(string msg) { return new ApiResult(100, msg, null); } + + /// + /// 返回成功消息 + /// + /// 返回内容 + /// 数据对象 + /// 成功消息 + public static ApiResult Success(string msg, object data) { return new ApiResult(100, msg, data); } + + /// + /// 返回失败消息 + /// + /// + /// + /// + public static ApiResult Error(int code, string msg) { return new ApiResult(code, msg); } + + /// + /// 返回失败消息 + /// + /// + /// + public static ApiResult Error(string msg) { return new ApiResult(200, msg); } + + } + public enum ResultCode + { + [Description("success")] + SUCCESS = 100, + [Description("参数错误")] + PARAM_ERROR = 101, + [Description("自定义错误")] + CUSTOM_ERROR = 200, + FAIL = 0, + [Description("程序出错啦")] + GLOBAL_ERROR = 104, + [Description("请先绑定手机号")] + NOBIND_PHONENUM = 102, + [Description("授权失败")] + OAUTH_FAIL = 201, + [Description("未授权")] + DENY = 401 + } +} diff --git a/ZR.Model/Dto/System/ArticleDto.cs b/ZR.Model/Dto/System/ArticleDto.cs new file mode 100644 index 0000000..ff5a3c0 --- /dev/null +++ b/ZR.Model/Dto/System/ArticleDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.Dto.System +{ + public class ArticleQueryDto: PagerInfo + { + public string Status { get; set; } + public string Title { get; set; } + public DateTime BeginTime { get; set; } + public DateTime EndTime { get; set; } + + } +} diff --git a/ZR.Model/Dto/System/LoginBodyDto.cs b/ZR.Model/Dto/System/LoginBodyDto.cs new file mode 100644 index 0000000..cf0fa0b --- /dev/null +++ b/ZR.Model/Dto/System/LoginBodyDto.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; + +namespace ZR.Model.Dto.System +{ + public class LoginBodyDto + { + /// + /// 用户名 + /// + [Required(ErrorMessage = "用户名不能为空")] + public string Username { get; set; } + + /// + /// 用户密码 + /// + [Required(ErrorMessage = "密码不能为空")] + public string Password { get; set; } + + /** + * 验证码 + */ + public string Code { get; set; } + + /** + * 唯一标识 + */ + public string Uuid { get; set; } = ""; + public string LoginIP { get; set; } + } +} diff --git a/ZR.Model/Dto/System/MenuDto.cs b/ZR.Model/Dto/System/MenuDto.cs new file mode 100644 index 0000000..ac241f7 --- /dev/null +++ b/ZR.Model/Dto/System/MenuDto.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.Dto.System +{ + public class MenuDto + { + //{"parentId":0,"menuName":"aaa","icon":"documentation","menuType":"M","orderNum":999,"visible":0,"status":0,"path":"aaa"} + public int parentId { get; set; } + public string menuName { get; set; } + public string icon { get; set; } = ""; + public string menuType { get; set; } + public int orderNum { get; set; } + public int visible { get; set; } + public int status { get; set; } + public string path { get; set; } = "#"; + public int MenuId { get; set; } + public string component { get; set; } + public int isCache { get; set; } + public int isFrame { get; set; } + public string perms { get; set; } = ""; + } +} diff --git a/ZR.Model/Dto/System/RoleUsersCreateDto.cs b/ZR.Model/Dto/System/RoleUsersCreateDto.cs new file mode 100644 index 0000000..2ba0cfb --- /dev/null +++ b/ZR.Model/Dto/System/RoleUsersCreateDto.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace ZR.Model.Dto.System +{ + public class RoleUsersCreateDto + { + /// + /// 描述 : 角色id + /// 空值 : False + /// 默认 : + /// + [Display(Name = "角色id")] + [Required(ErrorMessage = "roleId 不能为空")] + public long RoleId { get; set; } + + /// + /// 描述 : 用户编码 [1,2,3,4] + /// 空值 : False + /// 默认 : + /// + [Display(Name = "用户编码 [1,2,3,4]")] + public List UserIds { get; set; } + } +} diff --git a/ZR.Model/Dto/System/SysFileQueryDto.cs b/ZR.Model/Dto/System/SysFileQueryDto.cs new file mode 100644 index 0000000..5c3d018 --- /dev/null +++ b/ZR.Model/Dto/System/SysFileQueryDto.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.Dto.System +{ + public class SysFileQueryDto : PagerInfo + { + } +} diff --git a/ZR.Model/Dto/System/SysLogininfoDto.cs b/ZR.Model/Dto/System/SysLogininfoDto.cs new file mode 100644 index 0000000..7d5bc43 --- /dev/null +++ b/ZR.Model/Dto/System/SysLogininfoDto.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; + +namespace ZR.Model.Dto.System +{ + public class SysLogininfoDto : SysBase + { + public int pageNum { get; set; } + /// + /// IP 地址 + /// + public string Ipaddr { get; set; } + /// + /// 登录状态 0成功 1失败 + /// + public string Status { get; set; } + /// + /// 用户名 + /// + public string UserName { get; set; } + + } +} diff --git a/ZR.Model/Dto/System/SysOperLogDto.cs b/ZR.Model/Dto/System/SysOperLogDto.cs new file mode 100644 index 0000000..e686da1 --- /dev/null +++ b/ZR.Model/Dto/System/SysOperLogDto.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; + +namespace ZR.Model.Dto.System +{ + public class SysOperLogDto : SysBase + { + /// + /// 页码 + /// + public int pageNum { get; set; } + /// + /// 操作人员 + /// + public string operName { get; set; } + /// + /// 业务类型 0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据 + /// + public int BusinessType { get; set; } = -1; + /// + /// 状态 + /// + public int Status { get; set; } = -1; + /// + /// 操作模块 + /// + public string Title { get; set; } + } +} diff --git a/ZR.Model/Dto/System/SysRoleDto.cs b/ZR.Model/Dto/System/SysRoleDto.cs new file mode 100644 index 0000000..7ba3d47 --- /dev/null +++ b/ZR.Model/Dto/System/SysRoleDto.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; + +namespace ZR.Model.Dto.System +{ + public class SysRoleDto: SysBase + { + public long RoleId { get; set; } + /// + /// 角色分配菜单 + /// + public List MenuIds { get; set; } = new List(); + public string RoleName { get; set; } + public string RoleKey { get; set; } + public int RoleSort { get; set; } + public string Status { get; set; } + } +} diff --git a/ZR.Model/Dto/System/SysUserDto.cs b/ZR.Model/Dto/System/SysUserDto.cs new file mode 100644 index 0000000..ef7df57 --- /dev/null +++ b/ZR.Model/Dto/System/SysUserDto.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; + +namespace ZR.Model.Dto.System +{ + public class SysUserDto + { + public long UserId { get; set; } + public string UserName { get; set; } + public string NickName { get; set; } + public string Email { get; set; } + public string Remark { get; set; } + public string Phonenumber { get; set; } + /// + /// '用户性别(0男 1女 2未知)', + /// + public int Sex { get; set; } + } +} diff --git a/ZR.Model/Dto/System/TasksDto.cs b/ZR.Model/Dto/System/TasksDto.cs new file mode 100644 index 0000000..bc190bc --- /dev/null +++ b/ZR.Model/Dto/System/TasksDto.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace ZR.Model.Dto.System +{ + public class TasksQueryDto + { + /// + /// 描述 : 查询字符串 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "查询字符串")] + public string QueryText { get; set; } + } + + /// + /// 添加任务 + /// + public class TasksCreateDto + { + /// + /// 描述 : 任务名称 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "任务名称")] + [Required(ErrorMessage = "任务名称不能为空")] + public string Name { get; set; } + + /// + /// 描述 : 任务分组 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "任务分组")] + [Required(ErrorMessage = "任务分组不能为空")] + public string JobGroup { get; set; } + + /// + /// 描述 : 运行时间表达式 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "运行时间表达式")] + public string Cron { get; set; } + + /// + /// 描述 : 程序集名称 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "程序集名称")] + [Required(ErrorMessage = "程序集名称不能为空")] + public string AssemblyName { get; set; } + + /// + /// 描述 : 任务所在类 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "任务所在类")] + [Required(ErrorMessage = "任务所在类不能为空")] + public string ClassName { get; set; } + + /// + /// 描述 : 任务描述 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "任务描述")] + public string Remark { get; set; } + + /// + /// 描述 : 开始时间 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "开始时间")] + public DateTime BeginTime { get; set; } + + /// + /// 描述 : 结束时间 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "结束时间")] + public DateTime EndTime { get; set; } + + /// + /// 描述 : 触发器类型(0、simple 1、cron) + /// 空值 : False + /// 默认 : + /// + [Display(Name = "触发器类型(0、simple 1、cron)")] + public int TriggerType { get; set; } + + /// + /// 描述 : 执行间隔时间(单位:秒) + /// 空值 : False + /// 默认 : 0 + /// + [Display(Name = "执行间隔时间(单位:秒)")] + public int IntervalSecond { get; set; } + + /// + /// 描述 : 传入参数 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "传入参数")] + public string JobParams { get; set; } + } + + /// + /// 更新任务 + /// + public class TasksUpdateDto + { + /// + /// 描述 : UID + /// 空值 : False + /// 默认 : + /// + [Display(Name = "UID")] + [Required(ErrorMessage = "UID不能为空")] + public string ID { get; set; } + + /// + /// 描述 : 任务名称 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "任务名称")] + [Required(ErrorMessage = "任务名称不能为空")] + public string Name { get; set; } + + /// + /// 描述 : 任务分组 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "任务分组")] + [Required(ErrorMessage = "任务分组不能为空")] + public string JobGroup { get; set; } + + /// + /// 描述 : 运行时间表达式 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "运行时间表达式")] + public string Cron { get; set; } + + /// + /// 描述 : 程序集名称 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "程序集名称")] + [Required(ErrorMessage = "程序集名称不能为空")] + public string AssemblyName { get; set; } + + /// + /// 描述 : 任务所在类 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "任务所在类")] + [Required(ErrorMessage = "任务所在类不能为空")] + public string ClassName { get; set; } + + /// + /// 描述 : 任务描述 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "任务描述")] + public string Remark { get; set; } + + /// + /// 描述 : 开始时间 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "开始时间")] + public string BeginTime { get; set; } + + /// + /// 描述 : 结束时间 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "结束时间")] + public string EndTime { get; set; } + + /// + /// 描述 : 触发器类型(0、simple 1、cron) + /// 空值 : False + /// 默认 : + /// + [Display(Name = "触发器类型(0、simple 1、cron)")] + public int TriggerType { get; set; } + + /// + /// 描述 : 执行间隔时间(单位:秒) + /// 空值 : False + /// 默认 : 0 + /// + [Display(Name = "执行间隔时间(单位:秒)")] + public int IntervalSecond { get; set; } + + /// + /// 描述 : 传入参数 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "传入参数")] + public string JobParams { get; set; } + } + +} diff --git a/ZR.Model/Dto/System/TasksLogDto.cs b/ZR.Model/Dto/System/TasksLogDto.cs new file mode 100644 index 0000000..7c573e4 --- /dev/null +++ b/ZR.Model/Dto/System/TasksLogDto.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.Dto.System +{ + public class TasksLogQueryDto + { + /// + /// 描述 : 查询字符串 + /// 空值 : False + /// 默认 : + /// + //[Display(Name = "查询字符串")] + public string Name{ get; set; } + public string JobName { get; set; } + public string JobId { get; set; } + public string JobGroup { get; set; } + public string Status { get; set; } + public DateTime? BeginTime { get; set; } + public DateTime? EndTime { get; set; } + } + + public class TasksLogDto + { + + } +} diff --git a/ZR.Model/JsonDateConverter.cs b/ZR.Model/JsonDateConverter.cs new file mode 100644 index 0000000..70ca6c8 --- /dev/null +++ b/ZR.Model/JsonDateConverter.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model +{ + public class JsonDateConverter: IsoDateTimeConverter + { + public JsonDateConverter() + { + DateTimeFormat = "yyyy-MM-dd HH:mm:ss"; + } + } +} diff --git a/ZR.Model/OptionsSetting.cs b/ZR.Model/OptionsSetting.cs new file mode 100644 index 0000000..8c723fc --- /dev/null +++ b/ZR.Model/OptionsSetting.cs @@ -0,0 +1,50 @@ + +namespace ZRModel +{ + /// + /// 获取配置文件POCO实体类 + /// + public class OptionsSetting + { + /// + /// 1、喵播 2、fireStar + /// + public int Platform { get; set; } + public string AppName { get; set; } + public string Redis { get; set; } + public string Conn_Live { get; set; } + public string Conn_Admin { get; set; } + /// + /// mongodb连接字符串 + /// + public string Mongo_Conn { get; set; } + public string Database { get; set; } + + public LoggingOptions Logging { get; set; } + public MailOptions MailOptions { get; set; } + } + /// + /// 发送邮件数据配置 + /// + public class MailOptions + { + public string From { get; set; } + public string Password { get; set; } + public string Host { get; set; } + public int Port { get; set; } + } + /// + /// 日志 + /// + public class LoggingOptions + { + public LogLevelOptions LogLevel { get; set; } + } + public class LogLevelOptions + { + public string Default { get; set; } + public string Ssytem { get; set; } + public string Microsoft { get; set; } + } + +} diff --git a/ZR.Model/PagerInfo.cs b/ZR.Model/PagerInfo.cs new file mode 100644 index 0000000..f3921ef --- /dev/null +++ b/ZR.Model/PagerInfo.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model +{ + public class PagerInfo + { + /// + /// 当前页码 + /// + public int PageNum { get; set; } + private int pageSize; + /// + /// 总记录数 + /// + public int TotalNum { get; set; } + /// + /// 总页码 + /// + public int TotalPageNum { get; set; } + public int PageSize { get => pageSize; set => pageSize = value; } + + public PagerInfo() + { + PageNum = 1; + PageSize = 20; + } + + public PagerInfo(int page = 1, int pageSize = 20) + { + PageNum = page; + PageSize = pageSize; + } + } +} diff --git a/ZR.Model/System/Article.cs b/ZR.Model/System/Article.cs new file mode 100644 index 0000000..cffa116 --- /dev/null +++ b/ZR.Model/System/Article.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + /// + /// 文章表 + /// + [SqlSugar.SugarTable("article")] + public class Article + { + [SqlSugar.SugarColumn(IsIdentity = true, IsPrimaryKey = true)] + public int Cid { get; set; } + public string Title { get; set; } + public DateTime? CreateTime { get; set; } + [SqlSugar.SugarColumn(IsOnlyIgnoreInsert = true)] + public DateTime UpdateTime { get; set; } + /// + /// 文章内容 + /// + public string Content { get; set; } + /// + /// 作者名 + /// + public string AuthorName { get; set; } + public long UserId { get; set; } + /// + /// + /// + //public string Type { get; set; } + /// + /// 文章状态 1、发布 2、草稿 + /// + public string Status { get; set; } + /// + /// 编辑器类型 markdown,html + /// + public string Fmt_type { get; set; } + /// + /// 文章标签eg:Net5,java + /// + public string Tags { get; set; } + public int Hits { get; set; } + public int Category_id { get; set; } + } +} diff --git a/ZR.Model/System/ArticleCategory.cs b/ZR.Model/System/ArticleCategory.cs new file mode 100644 index 0000000..bd395d0 --- /dev/null +++ b/ZR.Model/System/ArticleCategory.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + /// + /// 文章目录 + /// + [SqlSugar.SugarTable("articleCategory")] + public class ArticleCategory + { + public int Category_Id { get; set; } + public string Name { get; set; } + public int ParentId { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [SqlSugar.SugarColumn(IsIgnore = true)] + public List Children { get; set; } + } +} diff --git a/ZR.Model/System/LoginUser.cs b/ZR.Model/System/LoginUser.cs new file mode 100644 index 0000000..2bb2ab8 --- /dev/null +++ b/ZR.Model/System/LoginUser.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + /// + /// 登录用户信息存储 + /// + public class LoginUser + { + public long UserId { get; set; } + public string UserName { get; set; } + public string NickName { get; set; } + /// + /// 角色集合 + /// + public List RoleIds { get; set; } + /// + /// 权限集合 + /// + public List Permissions{ get; set; } + public LoginUser() + { + } + + public LoginUser(long userId, string userName) + { + UserId = userId; + UserName = userName; + } + public LoginUser(long userId, string userName, List roleIds, List permissions) + { + UserId = userId; + UserName = userName; + RoleIds = roleIds; + Permissions = permissions; + } + } +} diff --git a/ZR.Model/System/SysBase.cs b/ZR.Model/System/SysBase.cs new file mode 100644 index 0000000..07d07f7 --- /dev/null +++ b/ZR.Model/System/SysBase.cs @@ -0,0 +1,54 @@ +//using Dapper.Contrib.Extensions; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; +using SqlSugar; + +namespace ZR.Model.System +{ + public class SysBase + { + [SugarColumn(IsOnlyIgnoreUpdate = true)]//设置后修改不会有此字段 + [JsonProperty(propertyName: "CreateBy")] + public string Create_by { get; set; } + + [SugarColumn(IsOnlyIgnoreUpdate = true)]//设置后修改不会有此字段 + [JsonProperty(propertyName: "CreateTime")] + public DateTime Create_time { get; set; } = DateTime.Now; + + [JsonIgnore] + [JsonProperty(propertyName: "UpdateBy")] + [SugarColumn(IsOnlyIgnoreInsert = true)] + public string Update_by { get; set; } + + [SugarColumn(IsOnlyIgnoreInsert = true)]//设置后插入数据不会有此字段 + [JsonIgnore] + [JsonProperty(propertyName: "UpdateTime")] + public DateTime Update_time { get; set; } = DateTime.Now; + + public string Remark { get; set; } + + /// + /// 搜素时间起始时间 + /// + /// + /// Write:需穿一个bool值,false时insert,update等操作会忽略此列(和Computed的作用差不多,看了源码也没发现与Computed有什么不一样的地方,有了解的朋友可以赐教下哈) + /// ExplicitKey:指定此列为主键(不自动增长类型例如guid,ExplicitKey与Key地区别下面会详细讲) + /// Key:指定此列为主键(自动增长主键),可忽略,忽略后默认查找 + /// [Computed]计算属性,打上此标签,对象地insert,update等操作会忽略此列 + /// + [SugarColumn(IsIgnore = true)] + //[Computed] + [JsonIgnore] + public DateTime? BeginTime { get; set; } + + /// + /// 用于搜索使用 + /// + [SugarColumn(IsIgnore = true)] + //[Computed] + [JsonIgnore] + public DateTime? EndTime { get; set; } + } +} diff --git a/ZR.Model/System/SysDept.cs b/ZR.Model/System/SysDept.cs new file mode 100644 index 0000000..95ee177 --- /dev/null +++ b/ZR.Model/System/SysDept.cs @@ -0,0 +1,57 @@ +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + /// + /// 部门表 + /// + [SqlSugar.SugarTable("sys_dept")] + public class SysDept: SysBase + { + /** 部门ID */ + [SqlSugar.SugarColumn(IsIdentity = true, IsPrimaryKey = true)] + public long DeptId { get; set; } + + /** 父部门ID */ + public long ParentId { get; set; } + + /** 祖级列表 */ + public string Ancestors { get; set; } + + /** 部门名称 */ + public string DeptName { get; set; } + + /** 显示顺序 */ + public int OrderNum { get; set; } + + /** 负责人 */ + public string Leader { get; set; } + + /** 联系电话 */ + public string Phone { get; set; } + + /** 邮箱 */ + public string Email { get; set; } + + /** 部门状态:0正常,1停用 */ + public string Status { get; set; } + + /// + /// 删除标志(0代表存在 2代表删除) + /// + [SugarColumn(IsOnlyIgnoreInsert = true)] + public string DelFlag { get; set; } + + /** 父部门名称 */ + //[SugarColumn(IsIgnore = true)] + //public string ParentName { get; set; } + + /// + /// 子菜单 + /// + public List children = new List(); + } +} diff --git a/ZR.Model/System/SysDictData.cs b/ZR.Model/System/SysDictData.cs new file mode 100644 index 0000000..fe8d30d --- /dev/null +++ b/ZR.Model/System/SysDictData.cs @@ -0,0 +1,31 @@ +//using Dapper.Contrib.Extensions; +using System; +using System.Collections.Generic; +using System.Text; +using SqlSugar; + +namespace ZR.Model.System +{ + /// + /// 字典数据表 + /// + //[Table("sys_dict_data")] + [SugarTable("sys_dict_data")] + public class SysDictData: SysBase + { + //[Key] + /// + /// 字典主键 + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]//主键并且自增 (string不能设置自增) + public long DictCode{ get; set; } + public int DictSort { get; set; } + public string DictLabel { get; set; } + public string DictValue { get; set; } + public string DictType { get; set; } + public string CssClass { get; set; } + public string ListClass { get; set; } + public string IsDefault { get; set; } + public string Status { get; set; } + } +} diff --git a/ZR.Model/System/SysDictType.cs b/ZR.Model/System/SysDictType.cs new file mode 100644 index 0000000..f46aad8 --- /dev/null +++ b/ZR.Model/System/SysDictType.cs @@ -0,0 +1,35 @@ +//using Dapper.Contrib.Extensions; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + /// + /// 字典类型表 + /// + [SugarTable("sys_dict_type")]//当和数据库名称不一样可以设置别名 + //[Table("sys_dict_type")] + public class SysDictType : SysBase + { + /// + /// 字典主键 + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]//主键并且自增 (string不能设置自增) + //[Key] + public long DictId { get; set; } + /// + /// 字典名称 + /// + public string DictName { get; set; } + /// + /// 字典类型 + /// + public string DictType { get; set; } + /// + /// 状态 0、正常 1、停用 + /// + public string Status { get; set; } + } +} diff --git a/ZR.Model/System/SysFile.cs b/ZR.Model/System/SysFile.cs new file mode 100644 index 0000000..5d17f40 --- /dev/null +++ b/ZR.Model/System/SysFile.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + [SqlSugar.SugarTable("sys_file")] + public class SysFile + { + public int Id { get; set; } + public string FilePath { get; set; } + public string FileName { get; set; } + public string StorePath { get; set; } + public string AccessPat { get; set; } + public int FileSize { get; set; } + public string FileExt { get; set; } + public DateTime? AddTime { get; set; } + + } +} diff --git a/ZR.Model/System/SysLogininfor.cs b/ZR.Model/System/SysLogininfor.cs new file mode 100644 index 0000000..e1ea472 --- /dev/null +++ b/ZR.Model/System/SysLogininfor.cs @@ -0,0 +1,56 @@ +//using Dapper.Contrib.Extensions; +using SqlSugar; +using System; + +namespace ZR.Model.System +{ + /// + /// sys_logininfor 表 + /// + [SugarTable("sys_logininfor")] + public class SysLogininfor: SysBase + { + //[Key] + [SugarColumn(IsPrimaryKey = true)] + public long infoId { get; set; } + + /// + /// 用户账号 + /// + public string userName { get; set; } + + /// + /// 登录状态 0成功 1失败 + /// + public string status { get; set; } + + /// + /// 登录IP地址 + /// + public string ipaddr { get; set; } + + /// + /// 登录地点 + /// + public string loginLocation { get; set; } + + /// + /// 浏览器类型 + /// + public string browser { get; set; } + + /** 操作系统 */ + //@Excel(name = "操作系统") + public string os { get; set; } + + /// + /// 提示消息 + /// + public string msg { get; set; } + + /// + /// 访问时间 + /// + public DateTime loginTime { get; set; } = DateTime.Now; + } +} diff --git a/ZR.Model/System/SysMenu.cs b/ZR.Model/System/SysMenu.cs new file mode 100644 index 0000000..1af655c --- /dev/null +++ b/ZR.Model/System/SysMenu.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; + +namespace ZR.Model.System +{ + /// + /// Sys_menu表 + /// + //[Table("sys_menu")] + [SqlSugar.SugarTable("sys_menu")] + public class SysMenu: SysBase + { + /// + /// 菜单ID + /// + //[Key]//非自动增长主键时使用ExplicitKey + [SqlSugar.SugarColumn(IsPrimaryKey = true)] + public long menuId { get; set; } + /// + /// 菜单名称 + /// + public string menuName { get; set; } + + /// + /// 父菜单名称 + /// + [SqlSugar.SugarColumn(IsIgnore = true)] + public string parentName { get; set; } + + /// + /// 父菜单ID + /// + public long parentId { get; set; } + + /// + /// 显示顺序 + /// + public int orderNum { get; set; } + + /// + /// 路由地址 + /// + public string path { get; set; } = "#"; + + /// + /// 组件路径 + /// + public string component { get; set; } + + /// + /// 是否缓存(1缓存 0不缓存) + /// + public string isCache { get; set; } + /// + /// 是否外链 1、是 0、否 + /// + public string isFrame { get; set; } + + /// + /// 类型(M目录 C菜单 F按钮 L链接) + /// + public string menuType { get; set; } + + /// + /// 显示状态(0显示 1隐藏) + /// + public string visible { get; set; } + + /// + /// 菜单状态(0正常 1停用) + /// + public string status { get; set; } + + /// + /// 权限字符串 + /// + public string perms { get; set; } + + /// + /// 菜单图标 + /// + public string icon { get; set; } = string.Empty; + + /// + /// 子菜单 + /// + public List children = new List(); + } +} diff --git a/ZR.Model/System/SysOperLog.cs b/ZR.Model/System/SysOperLog.cs new file mode 100644 index 0000000..3a04de5 --- /dev/null +++ b/ZR.Model/System/SysOperLog.cs @@ -0,0 +1,84 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + [SqlSugar.SugarTable("sys_oper_log")] + public class SysOperLog + { + [SqlSugar.SugarColumn(IsPrimaryKey = true)] + public long OperId { get; set; } + /** 操作模块 */ + //@Excel(name = "操作模块") + public String title { get; set; } + + /** 业务类型(0其它 1新增 2修改 3删除) */ + //@Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") + public int businessType { get; set; } + + /** 业务类型数组 */ + [SqlSugar.SugarColumn(IsIgnore = true)] + public int[] businessTypes { get; set; } + + /** 请求方法 */ + //@Excel(name = "请求方法") + public String method { get; set; } + + /** 请求方式 */ + //@Excel(name = "请求方式") + public String requestMethod { get; set; } + + /** 操作类别(0其它 1后台用户 2手机端用户) */ + //@Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") + public int operatorType { get; set; } + + /** 操作人员 */ + //@Excel(name = "操作人员") + public String operName { get; set; } + + /** 部门名称 */ + //@Excel(name = "部门名称") + public String deptName { get; set; } + + /** 请求url */ + //@Excel(name = "请求地址") + public String operUrl { get; set; } + + /** 操作地址 */ + //@Excel(name = "操作地址") + public String operIp { get; set; } + + /** 操作地点 */ + //@Excel(name = "操作地点") + public String operLocation { get; set; } + + /** 请求参数 */ + //@Excel(name = "请求参数") + public String operParam { get; set; } + + /** 返回参数 */ + //@Excel(name = "返回参数") + public String jsonResult { get; set; } + + /** 操作状态(0正常 1异常) */ + //@Excel(name = "状态", readConverterExp = "0=正常,1=异常") + public int status { get; set; } + + /** 错误消息 */ + //@Excel(name = "错误消息") + public String errorMsg { get; set; } + + /** 操作时间 */ + //@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + //@Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + //[DataType] + [JsonConverter(typeof(JsonDateConverter))] + public DateTime? operTime { get; set; } + /// + /// 操作用时 + /// + public long Elapsed { get; set; } + } +} diff --git a/ZR.Model/System/SysPost.cs b/ZR.Model/System/SysPost.cs new file mode 100644 index 0000000..600cc07 --- /dev/null +++ b/ZR.Model/System/SysPost.cs @@ -0,0 +1,16 @@ +namespace ZR.Model.System +{ + [SqlSugar.SugarTable("sys_post")] + public class SysPost: SysBase + { + /// + /// 岗位Id + /// + [SqlSugar.SugarColumn(IsPrimaryKey = true, IsIdentity = true)] + public long PostId { get; set; } + public string PostCode { get; set; } + public string PostName { get; set; } + public int PostSort { get; set; } + public string Status { get; set; } + } +} diff --git a/ZR.Model/System/SysRole.cs b/ZR.Model/System/SysRole.cs new file mode 100644 index 0000000..5026247 --- /dev/null +++ b/ZR.Model/System/SysRole.cs @@ -0,0 +1,74 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + /// + /// 角色表 sys_role + /// + [SqlSugar.SugarTable("sys_role")] + public class SysRole : SysBase + { + /// + /// 角色ID + /// + [SqlSugar.SugarColumn(IsPrimaryKey = true, IsIdentity = true)] + public long RoleId { get; set; } + + /// + /// 角色名称 + /// + public string RoleName { get; set; } + + /// + /// 角色权限 + /// + public string RoleKey { get; set; } + + /// + /// 角色排序 + /// + public int RoleSort { get; set; } + + /// + /// 帐号状态(0正常 1停用) + /// + public string Status { get; set; } + + /// + /// 删除标志(0代表存在 2代表删除) + /// + [SqlSugar.SugarColumn(IsOnlyIgnoreInsert = true, IsOnlyIgnoreUpdate = true)] + public string DelFlag { get; set; } + + /// + /// 菜单组 + /// + [SqlSugar.SugarColumn(IsIgnore = true)] + public long[] MenuIds { get; set; } + /// + /// 部门组(数据权限) + /// + [SqlSugar.SugarColumn(IsIgnore = true)] + public long[] DeptIds { get; set; } + + public SysRole() { } + + public SysRole(long roleId) + { + RoleId = roleId; + } + + public bool IsAdmin() + { + return IsAdmin(RoleId); + } + + public static bool IsAdmin(long roleId) + { + return 1 == roleId; + } + } +} diff --git a/ZR.Model/System/SysRoleMenu.cs b/ZR.Model/System/SysRoleMenu.cs new file mode 100644 index 0000000..601c2ee --- /dev/null +++ b/ZR.Model/System/SysRoleMenu.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + /// + /// 角色菜单 + /// + [SqlSugar.SugarTable("sys_role_menu")] + public class SysRoleMenu + { + [JsonProperty("roleId")] + [SqlSugar.SugarColumn(IsPrimaryKey = true)] + public long Role_id { get; set; } + [JsonProperty("menuId")] + [SqlSugar.SugarColumn(IsPrimaryKey = true)] + public long Menu_id { get; set; } + public DateTime Create_time { get; set; } + public string Create_by { get; set; } + } +} diff --git a/ZR.Model/System/SysRolePost.cs b/ZR.Model/System/SysRolePost.cs new file mode 100644 index 0000000..705b139 --- /dev/null +++ b/ZR.Model/System/SysRolePost.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using SqlSugar; + +namespace ZR.Model.System +{ + /// + /// 角色部门 + /// + [SugarTable("sys_role_post")] + public class SysRolePost + { + public long RoleId { get; set; } + public long DeptId { get; set; } + } +} diff --git a/ZR.Model/System/SysTasksLog.cs b/ZR.Model/System/SysTasksLog.cs new file mode 100644 index 0000000..e339226 --- /dev/null +++ b/ZR.Model/System/SysTasksLog.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + /// + /// 任务日志 + /// + [SqlSugar.SugarTable("sys_Tasks_log")] + public class SysTasksLog + { + /// + /// 日志Id + /// + [SqlSugar.SugarColumn(IsIdentity = true, IsPrimaryKey = true)] + public long JobLogId { get; set; } + /// + /// 任务Id + /// + public string JobId { get; set; } + public string JobName { get; set; } + public string JobGroup { get; set; } + /// + /// 执行状态(0正常 1失败) + /// + public string Status { get; set; } + /// + /// 异常 + /// + public string Exception { get; set; } + /// + /// + /// + public string JobMessage { get; set; } + /// + /// 调用目标字符串 + /// + public string InvokeTarget { get; set; } + public DateTime CreateTime { get; set; } + /// + /// 执行用时,毫秒 + /// + //[SqlSugar.SugarColumn(IsIgnore = true)] + public double Elapsed { get; set; } + } +} diff --git a/ZR.Model/System/SysTasksQz.cs b/ZR.Model/System/SysTasksQz.cs new file mode 100644 index 0000000..ac02bf8 --- /dev/null +++ b/ZR.Model/System/SysTasksQz.cs @@ -0,0 +1,156 @@ +using Newtonsoft.Json; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace ZR.Model.System +{ + /// + ///计划任务 + /// + [SugarTable("Sys_TasksQz")] + public class SysTasksQz + { + public SysTasksQz() + { + } + + /// + /// 描述 : UID + /// 空值 : False + /// 默认 : + /// + [Display(Name = "UID")] + [SugarColumn(IsPrimaryKey = true)] + public string ID { get; set; } + + /// + /// 描述 : 任务名称 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "任务名称")] + public string Name { get; set; } + + /// + /// 描述 : 任务分组 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "任务分组")] + public string JobGroup { get; set; } + + /// + /// 描述 : 运行时间表达式 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "运行时间表达式")] + public string Cron { get; set; } + + /// + /// 描述 : 程序集名称 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "程序集名称")] + public string AssemblyName { get; set; } + + /// + /// 描述 : 任务所在类 + /// 空值 : False + /// 默认 : + /// + [Display(Name = "任务所在类")] + public string ClassName { get; set; } + + /// + /// 描述 : 任务描述 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "任务描述")] + public string Remark { get; set; } + + /// + /// 描述 : 执行次数 + /// 空值 : False + /// 默认 : 0 + /// + [Display(Name = "执行次数")] + public int RunTimes { get; set; } + + /// + /// 描述 : 开始时间 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "开始时间")] + public DateTime? BeginTime { get; set; } + + /// + /// 描述 : 结束时间 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "结束时间")] + public DateTime? EndTime { get; set; } + + /// + /// 描述 : 触发器类型(0、simple 1、cron) + /// 空值 : False + /// 默认 : 1 + /// + [Display(Name = "触发器类型(0、simple 1、cron)")] + public int TriggerType { get; set; } + + /// + /// 描述 : 执行间隔时间(单位:秒) + /// 空值 : False + /// 默认 : 0 + /// + [Display(Name = "执行间隔时间(单位:秒)")] + public int IntervalSecond { get; set; } + + /// + /// 描述 : 是否启动 + /// 空值 : False + /// 默认 : 0 + /// + [Display(Name = "是否启动")] + public bool IsStart { get; set; } + + /// + /// 描述 : 传入参数 + /// 空值 : True + /// 默认 : + /// + [Display(Name = "传入参数")] + public string JobParams { get; set; } + + [SugarColumn(IsOnlyIgnoreUpdate = true)]//设置后修改不会有此字段 + [JsonProperty(propertyName: "CreateBy")] + public string Create_by { get; set; } + + /// + /// 描述 : 创建时间 + /// 空值 : False + /// 默认 : + /// + //[Display(Name = "创建时间")] + [SugarColumn(IsOnlyIgnoreUpdate = true)]//设置后修改不会有此字段 + [JsonProperty(propertyName: "CreateTime")] + public DateTime Create_time { get; set; } = DateTime.Now; + + [JsonIgnore] + [JsonProperty(propertyName: "UpdateBy")] + [SugarColumn(IsOnlyIgnoreInsert = true)] + public string Update_by { get; set; } + + [SugarColumn(IsOnlyIgnoreInsert = true)]//设置后插入数据不会有此字段 + [JsonProperty(propertyName: "UpdateTime")] + public DateTime Update_time { get; set; } = DateTime.Now; + } +} diff --git a/ZR.Model/System/SysUser.cs b/ZR.Model/System/SysUser.cs new file mode 100644 index 0000000..af36710 --- /dev/null +++ b/ZR.Model/System/SysUser.cs @@ -0,0 +1,113 @@ +using Newtonsoft.Json; +using Npoi.Mapper.Attributes; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + /// + /// 用户表 + /// + [SugarTable("sys_user")] + public class SysUser : SysBase + { + /// + /// 用户id + /// + [SugarColumn(IsIdentity = true, IsPrimaryKey = true)] + public long UserId { get; set; } + [Column("用户名")]//对应Excel列名 + //[Required]//校验必填 + //[Duplication]//校验模板类该列数据是否重复 + public string UserName { get; set; } + + [Column("用户昵称")] + //[Required] + public string NickName { get; set; } + + /// + /// '用户类型(00系统用户)', + /// + //[JsonProperty(propertyName: "userType")] + //public string User_type { get; set; } = ""; + [SugarColumn(IsOnlyIgnoreInsert = true)] + public string Avatar { get; set; } + + public string Email { get; set; } + + [JsonIgnore] + //[ColName("用户密码")] + public string Password { get; set; } + + //[ColName("手机号")] + public string Phonenumber { get; set; } + + /// + /// 用户性别(0男 1女 2未知) + /// + public string Sex { get; set; } + + /// + /// 帐号状态(0正常 1停用) + /// + public string Status { get; set; } + + /// + /// 删除标志(0代表存在 2代表删除) + /// + [SugarColumn(IsOnlyIgnoreInsert = true)] + public string DelFlag { get; set; } + + /// + /// 最后登录IP + /// + [SugarColumn(IsOnlyIgnoreInsert = true)] + public string LoginIP { get; set; } + + /// + /// 最后登录时间 + /// + [SugarColumn(IsOnlyIgnoreInsert = true)] + public DateTime LoginDate { get; set; } + + /// + /// 部门Id + /// + public long DeptId { get; set; } + + #region 表额外字段 + public bool IsAdmin() + { + return IsAdmin(UserId); + } + public static bool IsAdmin(long userId) + { + return 1 == userId; + } + + /// + /// 拥有角色个数 + /// + //[SugarColumn(IsIgnore = true)] + //public int RoleNum { get; set; } + [SugarColumn(IsIgnore = true)] + public string DeptName { get; set; } + /// + /// 角色id集合 + /// + [SugarColumn(IsIgnore = true)] + public int[] RoleIds { get; set; } + /// + /// 岗位集合 + /// + [SugarColumn(IsIgnore = true)] + public int[] PostIds { get; set; } + + [SugarColumn(IsIgnore = true)] + public List Roles { get; set; } + + #endregion + } +} diff --git a/ZR.Model/System/SysUserPost.cs b/ZR.Model/System/SysUserPost.cs new file mode 100644 index 0000000..7804cca --- /dev/null +++ b/ZR.Model/System/SysUserPost.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; +using SqlSugar; + +namespace ZR.Model.System +{ + + /// + /// 用户岗位 + /// + [SugarTable("sys_user_post")] + public class SysUserPost + { + public long UserId { get; set; } + public long PostId { get; set; } + } +} diff --git a/ZR.Model/System/SysUserRole.cs b/ZR.Model/System/SysUserRole.cs new file mode 100644 index 0000000..1495195 --- /dev/null +++ b/ZR.Model/System/SysUserRole.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + /// + /// 用户角色关联表 用户N-1 角色 + /// + [SqlSugar.SugarTable("sys_user_role")] + public class SysUserRole + { + [SqlSugar.SugarColumn(ColumnName = "user_id", IsPrimaryKey = true)] + public long UserId { get; set; } + + [SqlSugar.SugarColumn(ColumnName = "role_id", IsPrimaryKey = true)] + public long RoleId { get; set; } + } +} diff --git a/ZR.Model/System/UserConstants.cs b/ZR.Model/System/UserConstants.cs new file mode 100644 index 0000000..bc68af6 --- /dev/null +++ b/ZR.Model/System/UserConstants.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System +{ + public class UserConstants + { + /** + * 平台内系统用户的唯一标志 + */ + public static string SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static string NORMAL = "0"; + + /** 异常状态 */ + public static string EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static string USER_DISABLE = "1"; + + /** 角色封禁状态 */ + public static string ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static string DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static string DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static string DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static string YES = "Y"; + + /** 是否菜单外链(是) */ + public static string YES_FRAME = "1"; + + /** 是否菜单外链(否) */ + public static string NO_FRAME = "0"; + + /** 菜单类型(目录) */ + public static string TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static string TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static string TYPE_BUTTON = "F"; + /** 菜单类型(链接) */ + //public static string TYPE_LINK = "L"; + + /** Layout组件标识 */ + public static string LAYOUT = "Layout"; + /** ParentView组件标识 */ + public static string PARENT_VIEW = "ParentView"; + + /** 校验返回结果码 */ + public static string UNIQUE = "0"; + public static string NOT_UNIQUE = "1"; + } +} diff --git a/ZR.Model/Vo/System/RouterVo.cs b/ZR.Model/Vo/System/RouterVo.cs new file mode 100644 index 0000000..73b7399 --- /dev/null +++ b/ZR.Model/Vo/System/RouterVo.cs @@ -0,0 +1,50 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Model.System.Vo +{ + /// + /// 路由展示 + /// + public class RouterVo + { + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public bool AlwaysShow { get; set; } + private string component; + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public bool Hidden { get; set; } + public string Name { get; set; } + public string Path { get; set; } + //[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Redirect { get; set; } + public Meta Meta { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public List Children { get; set; } + public string Component { get => component; set => component = value; } + } + + public class Meta + { + /// + /// 设置该路由在侧边栏和面包屑中展示的名字 + /// + public string Title { get; set; } + /// + /// 设置该路由的图标,对应路径src/assets/icons/svg + /// + public string Icon { get; set; } + /// + /// 设置为true,则不会被 缓存 + /// + public bool NoCache { get; set; } + + public Meta(string title, string icon, bool noCache) + { + Title = title; + Icon = icon; + NoCache = noCache; + } + } +} diff --git a/ZR.Model/Vo/System/TreeSelectVo.cs b/ZR.Model/Vo/System/TreeSelectVo.cs new file mode 100644 index 0000000..46ed195 --- /dev/null +++ b/ZR.Model/Vo/System/TreeSelectVo.cs @@ -0,0 +1,58 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; + +namespace ZR.Model.Vo.System +{ + /// + /// Treeselect树结构实体类 + /// + public class TreeSelectVo + { + /// + /// 节点Id + /// + public long Id { get; set; } + /// + /// 节点名称 + /// + public string Label { get; set; } + + public TreeSelectVo() { } + + public TreeSelectVo(SysMenu menu) + { + Id = menu.menuId; + Label = menu.menuName; + + //menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); java写法 + List child = new List(); + foreach (var item in menu.children) + { + child.Add(new TreeSelectVo(item)); + } + + Children = child; + } + + public TreeSelectVo(SysDept dept) + { + Id = dept.DeptId; + Label = dept.DeptName; + + //menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); java写法 + List child = new List(); + foreach (var item in dept.children) + { + child.Add(new TreeSelectVo(item)); + } + + Children = child; + } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public List Children { get; set; } + } +} diff --git a/ZR.Model/Vo/VMPageResult.cs b/ZR.Model/Vo/VMPageResult.cs new file mode 100644 index 0000000..a6af85b --- /dev/null +++ b/ZR.Model/Vo/VMPageResult.cs @@ -0,0 +1,53 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Data; + +namespace ZR.Model.Vo +{ + public class VMPageResult where T : new() + { + public int Page { get; set; } + public int PageSize { get; set; } + public long TotalNum { get; set; } + //public int TotalPages { get; set; } + public IList Result { get; set; } + public long EndPage { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public DataTable Data { get; set; } + + public VMPageResult() + { + } + + /// + /// 集合使用 + /// + /// + /// + public VMPageResult(IList result, PagerInfo pagerInfo) + { + this.Result = result; + this.TotalNum = pagerInfo.TotalNum; + this.PageSize = pagerInfo.PageSize; + this.Page = pagerInfo.PageNum; + //计算 + this.EndPage = pagerInfo.TotalNum % PageSize == 0 ? pagerInfo.TotalNum / PageSize : pagerInfo.TotalNum / PageSize + 1; + } + + /// + /// datable使用 + /// + /// + /// + public VMPageResult(DataTable result, PagerInfo pagerInfo) + { + this.Data = result; + this.TotalNum = pagerInfo.TotalNum; + this.PageSize = pagerInfo.PageSize; + this.Page = pagerInfo.PageNum; + //计算 + this.EndPage = pagerInfo.TotalNum % PageSize == 0 ? pagerInfo.TotalNum / PageSize : pagerInfo.TotalNum / PageSize + 1; + } + } + +} diff --git a/ZR.Model/ZR.Model.csproj b/ZR.Model/ZR.Model.csproj new file mode 100644 index 0000000..975d730 --- /dev/null +++ b/ZR.Model/ZR.Model.csproj @@ -0,0 +1,23 @@ + + + + netstandard2.1 + + + + + + + + + + + + + + + + + + + diff --git a/ZR.Repository/DbProvider/SugarDbContext.cs b/ZR.Repository/DbProvider/SugarDbContext.cs new file mode 100644 index 0000000..67ec657 --- /dev/null +++ b/ZR.Repository/DbProvider/SugarDbContext.cs @@ -0,0 +1,54 @@ +using Infrastructure; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ZR.Repository.DbProvider +{ + /// + /// SqlSugar ORM + /// + public class SugarDbContext + { + public SqlSugarClient Db; //用来处理事务多表查询和复杂的操作 + + /// + /// 使用SugarSql获取连接对象 + /// 构造方法有问题 + /// + /// + public SugarDbContext() + { + string connStr = ConfigUtils.Instance.GetConnectionStrings(OptionsSetting.ConnAdmin); + string dbKey = ConfigUtils.Instance.GetAppConfig(OptionsSetting.DbKey); + int dbType = ConfigUtils.Instance.GetAppConfig(OptionsSetting.DbType, 0); + if (!string.IsNullOrEmpty(dbKey)) + { + connStr = NETCore.Encrypt.EncryptProvider.DESDecrypt(connStr, dbKey); + } + Db = new SqlSugarClient(new List() + { + new ConnectionConfig(){ + ConnectionString = connStr, + DbType = (DbType)dbType, + IsAutoCloseConnection = true,//开启自动释放模式和EF原理一样 + InitKeyType = InitKeyType.Attribute,//从特性读取主键和自增列信息 + ConfigId = 0 + }, + }); + + //调式代码 用来打印SQL + Db.Aop.OnLogExecuting = (sql, pars) => + { + Console.WriteLine("【SQL语句】" + sql.ToLower() + "\r\n" + Db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value))); + }; + //出错打印日志 + Db.Aop.OnError = (e) => + { + Console.WriteLine($"[执行Sql出错]{e.Message},SQL={e.Sql}"); + Console.WriteLine(); + }; + } + } +} diff --git a/ZR.Repository/Interceptor/SqlLogInterceptorAttribute.cs b/ZR.Repository/Interceptor/SqlLogInterceptorAttribute.cs new file mode 100644 index 0000000..fe569de --- /dev/null +++ b/ZR.Repository/Interceptor/SqlLogInterceptorAttribute.cs @@ -0,0 +1,16 @@ +using AspectCore.DynamicProxy; +using System.Threading.Tasks; +using ZR.Repository.DbProvider; + +namespace ZR.Repository.Interceptor +{ + + public class SqlLogInterceptorAttribute : AbstractInterceptorAttribute + { + public override Task Invoke(AspectContext context, AspectDelegate next) + { + global::System.Console.WriteLine(""); + return next(context); + } + } +} diff --git a/ZR.Repository/System/ArticleRepository.cs b/ZR.Repository/System/ArticleRepository.cs new file mode 100644 index 0000000..5b0cd75 --- /dev/null +++ b/ZR.Repository/System/ArticleRepository.cs @@ -0,0 +1,20 @@ +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ZR.Model.Dto.System; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + /// + /// 文章管理 + /// + [AppService(ServiceLifetime = LifeTime.Transient)] + public class ArticleRepository + { + + } +} diff --git a/ZR.Repository/System/BaseRepository.cs b/ZR.Repository/System/BaseRepository.cs new file mode 100644 index 0000000..cd7a790 --- /dev/null +++ b/ZR.Repository/System/BaseRepository.cs @@ -0,0 +1,21 @@ +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ZR.Repository.DbProvider; + +namespace ZR.Repository.System +{ + public class BaseRepository : SugarDbContext + { + protected void PrintLog() + { + //调式代码 用来打印SQL + Db.Aop.OnLogExecuting = (sql, pars) => + { + Console.WriteLine("【SQL语句】" + sql.ToLower() + "\r\n" + Db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value))); + }; + } + } +} diff --git a/ZR.Repository/System/SysDeptRepository.cs b/ZR.Repository/System/SysDeptRepository.cs new file mode 100644 index 0000000..4935ec2 --- /dev/null +++ b/ZR.Repository/System/SysDeptRepository.cs @@ -0,0 +1,30 @@ +using Infrastructure.Attribute; +using System.Collections.Generic; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + /// + /// 部门管理 + /// + [AppService(ServiceLifetime= LifeTime.Transient)] + public class SysDeptRepository : BaseRepository + { + /// + /// + /// + /// + /// + public List SelectChildrenDeptById(long deptId) + { + string sql = "select * from sys_dept where find_in_set(@deptId, ancestors)"; + + return Db.SqlQueryable(sql).AddParameters(new { @deptId = deptId }).ToList(); + } + + public int UdateDeptChildren(SysDept dept) + { + return Db.Updateable(dept).UpdateColumns(it => new { it.Ancestors }).ExecuteCommand(); + } + } +} diff --git a/ZR.Repository/System/SysDictDataRepository.cs b/ZR.Repository/System/SysDictDataRepository.cs new file mode 100644 index 0000000..43b24b6 --- /dev/null +++ b/ZR.Repository/System/SysDictDataRepository.cs @@ -0,0 +1,117 @@ +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + /// + /// 字典数据 + /// + [AppService(ServiceLifetime = LifeTime.Transient)] + public class SysDictDataRepository : BaseRepository + { + /// + /// 字典类型数据搜索 + /// + /// + /// + public List SelectDictDataList(SysDictData dictData) + { + return Db.Queryable() + .WhereIF(!string.IsNullOrEmpty(dictData.DictLabel), it => it.DictLabel.Contains(dictData.DictLabel)) + .WhereIF(!string.IsNullOrEmpty(dictData.Status), it => it.Status == dictData.Status) + .WhereIF(!string.IsNullOrEmpty(dictData.DictType), it => it.DictType == dictData.DictType) + .ToList(); + } + + /// + /// 根据字典类型查询 + /// + /// + /// + public List SelectDictDataByType(string dictType) + { + return Db.Queryable().Where(f => f.Status == "0" && f.DictType == dictType) + .OrderBy(it => it.DictSort) + .ToList(); + } + + /// + /// 根据DictCode查询 + /// + /// + /// + public SysDictData SelectDictDataById(long dictCode) + { + return Db.Queryable().Where(f => f.DictCode == dictCode).First(); + } + + /// + /// 新增保存字典数据信息 + /// + /// + /// + public long InsertDictData(SysDictData dict) + { + var result = Db.Insertable(dict).IgnoreColumns(it => new { dict.Update_by }) + .ExecuteReturnIdentity(); + return result; + } + + /// + /// 修改数据 + /// + /// + /// + public long UpdateDictData(SysDictData dict) + { + return Db.Updateable() + .SetColumns(t => new SysDictData() + { + Remark = dict.Remark, + Update_time = DateTime.Now, + DictSort = dict.DictSort, + DictLabel = dict.DictLabel, + DictValue = dict.DictValue, + Status = dict.Status + }) + .Where(f => f.DictCode == dict.DictCode).ExecuteCommand(); + } + + /// + /// 批量删除字典数据信息 + /// + /// + /// + public int DeleteDictDataByIds(long[] dictCodes) + { + return Db.Deleteable().In(dictCodes).ExecuteCommand(); + } + + /// + /// 查询字典数据 + /// + /// + /// + public int CountDictDataByType(string dictType) + { + return Db.Queryable().Count(f => f.DictType == dictType); + } + + /// + /// 同步修改字典类型 + /// + /// 旧字典类型 + /// 新字典类型 + /// + public int UpdateDictDataType(string old_dictType, string new_dictType) + { + //只更新DictType字段根据where条件 + return Db.Updateable() + .SetColumns(t => new SysDictData() { DictType = new_dictType }) + .Where(f => f.DictType == old_dictType) + .ExecuteCommand(); + } + } +} diff --git a/ZR.Repository/System/SysDictRepository.cs b/ZR.Repository/System/SysDictRepository.cs new file mode 100644 index 0000000..f30a142 --- /dev/null +++ b/ZR.Repository/System/SysDictRepository.cs @@ -0,0 +1,91 @@ +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + /// + /// 字典 + /// + [AppService(ServiceLifetime = LifeTime.Transient)] + public class SysDictRepository : BaseRepository + { + /// + /// 查询字段类型列表 + /// + /// 实体模型 + /// + public List SelectDictTypeList(SysDictType dictType) + { + return Db + .Queryable() + .WhereIF(!string.IsNullOrEmpty(dictType.DictName), it => it.DictName.Contains(dictType.DictName)) + .WhereIF(!string.IsNullOrEmpty(dictType.Status), it => it.Status == dictType.Status) + .WhereIF(!string.IsNullOrEmpty(dictType.DictType), it => it.DictType == dictType.DictType).ToList(); + } + + /// + /// 根据Id查询 + /// + /// + /// + public SysDictType SelectDictTypeById(long id) + { + return Db.Queryable().First(it => it.DictId == id); + } + + /// + /// 检查字典类型唯一值 + /// + /// + /// + public SysDictType CheckDictTypeUnique(string dictType) + { + return Db.Queryable().First(it => it.DictType == dictType); + } + + /// + /// 删除一个 + /// + /// + /// + public int DeleteDictTypeById(long id) + { + return Db.Deleteable().In(id).ExecuteCommand(); + } + + /// + /// 批量删除 + /// + /// + /// + public int DeleteDictTypeByIds(long[] id) + { + return Db.Deleteable().In(id).ExecuteCommand(); + } + + /// + /// 插入 + /// + /// + /// + public long InsertDictType(SysDictType sysDictType) + { + var result = Db.Insertable(sysDictType).IgnoreColumns(it => new { sysDictType.Update_by }) + .ExecuteReturnIdentity(); + return result; + } + + /// + /// 修改 + /// + /// + /// + public int UpdateDictType(SysDictType dictType) + { + return Db.Updateable(dictType).IgnoreColumns(it => new { dictType.Create_by }).ExecuteCommand(); + } + } +} diff --git a/ZR.Repository/System/SysLogininfoRepository.cs b/ZR.Repository/System/SysLogininfoRepository.cs new file mode 100644 index 0000000..1bdb962 --- /dev/null +++ b/ZR.Repository/System/SysLogininfoRepository.cs @@ -0,0 +1,89 @@ +using Infrastructure.Attribute; +using Infrastructure.Extensions; +using System.Collections.Generic; +using ZR.Model; +using ZR.Model.Dto.System; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + [AppService(ServiceLifetime = LifeTime.Transient)] + public class SysLogininfoRepository : BaseRepository + { + /// + /// 查询登录日志 + /// + /// + /// + /// + public List GetLoginLog(SysLogininfor logininfoDto, PagerInfo pager) + { + int totalCount = 0; + var list = Db.Queryable() + .Where(it => it.loginTime >= logininfoDto.BeginTime && it.loginTime <= logininfoDto.EndTime) + .WhereIF(logininfoDto.ipaddr.IfNotEmpty(), f => f.ipaddr == logininfoDto.ipaddr) + .WhereIF(logininfoDto.userName.IfNotEmpty(), f => f.userName.Contains(logininfoDto.userName)) + .WhereIF(logininfoDto.status.IfNotEmpty(), f => f.status == logininfoDto.status) + .OrderBy(it => it.infoId, SqlSugar.OrderByType.Desc) + .IgnoreColumns(it => new { it.Create_by, it.Create_time, it.Update_by, it.Update_time, it.Remark }) + .ToPageList(pager.PageNum, pager.PageSize, ref totalCount); + pager.TotalNum = totalCount; + return list; + } + + /// + /// 登录日志记录 + /// + /// + /// + public void AddLoginInfo(SysLogininfor sysLogininfor) + { + int result = Db.Insertable(sysLogininfor) + .IgnoreColumns(it => new { it.Create_by, it.Create_time, it.Remark }) + .ExecuteReturnIdentity(); + } + + /// + /// 清空登录日志 + /// + public void TruncateLogininfo() + { + string sql = "truncate table sys_logininfor"; + + Db.Ado.ExecuteCommand(sql); + } + + /// + /// 删除登录日志 + /// + /// + /// + public int DeleteLogininforByIds(long[] ids) + { + return Db.Deleteable().In(ids).ExecuteCommand(); + } + + /// + /// 登录 + /// + /// 登录实体 + /// + public SysUser Login(LoginBodyDto user) + { + return Db.Queryable().First(it => it.UserName == user.Username && it.Password == user.Password); + } + + /// + /// 修改登录信息 + /// + /// + /// + /// + public void UpdateLoginInfo(LoginBodyDto user, long userId) + { + Db.Updateable(new SysUser() { LoginIP = user.LoginIP, LoginDate = Db.GetDate(), UserId = userId }) + .UpdateColumns(it => new { it.LoginIP, it.LoginDate }) + .ExecuteCommand(); + } + } +} diff --git a/ZR.Repository/System/SysMenuRepository.cs b/ZR.Repository/System/SysMenuRepository.cs new file mode 100644 index 0000000..0332b87 --- /dev/null +++ b/ZR.Repository/System/SysMenuRepository.cs @@ -0,0 +1,192 @@ +using Infrastructure.Attribute; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using ZR.Model.Dto.System; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + /// + /// 系统菜单 + /// + [AppService(ServiceLifetime = LifeTime.Transient)] + public class SysMenuRepository : BaseRepository + { + /// + /// 获取所有菜单(菜单管理) + /// + /// + public List SelectMenuList(SysMenu menu) + { + return Db.Queryable() + .WhereIF(!string.IsNullOrEmpty(menu.menuName), it => it.menuName.Contains(menu.menuName)) + .WhereIF(!string.IsNullOrEmpty(menu.visible), it => it.visible == menu.visible) + .WhereIF(!string.IsNullOrEmpty(menu.status), it => it.status == menu.status) + .OrderBy(it => new { it.parentId, it.orderNum }) + .ToList(); + } + + /// + /// 根据用户查询系统菜单列表 + /// + /// + /// 用户id + /// + public List SelectMenuListByUserId(SysMenu sysMenu, long userId) + { + return Db.Queryable((menu, roleMenu, userRole, role) => new JoinQueryInfos( + JoinType.Left, menu.menuId == roleMenu.Menu_id, + JoinType.Left, roleMenu.Role_id == userRole.RoleId, + JoinType.Left, userRole.RoleId == role.RoleId + )) + .Where((menu, roleMenu, userRole, role) => userRole.UserId == userId) + .WhereIF(!string.IsNullOrEmpty(sysMenu.menuName), (menu, roleMenu, userRole, role) => menu.menuName.Contains(sysMenu.menuName)) + .WhereIF(!string.IsNullOrEmpty(sysMenu.visible), (menu, roleMenu, userRole, role) => menu.visible == sysMenu.visible) + .WhereIF(!string.IsNullOrEmpty(sysMenu.status), (menu, roleMenu, userRole, role) => menu.status == sysMenu.status) + .OrderBy((menu, roleMenu, userRole, role) => new { menu.parentId, menu.orderNum }) + //.Distinct() + .Select((menu, roleMenu, userRole, role) => menu).ToList(); + } + + #region 左侧菜单树 + + /// + /// 管理员获取左侧菜单树 + /// + /// + public List SelectMenuTreeAll() + { + var menuTypes = new string[] { "M", "C" }; + + return Db.Queryable() + .Where(f => f.status == "0" && menuTypes.Contains(f.menuType)) + .OrderBy(it => new { it.parentId, it.orderNum }).ToList(); + } + + /// + /// 根据用户角色获取左侧菜单树 + /// + /// + /// + public List SelectMenuTreeByRoleIds(List roleIds) + { + var menuTypes = new string[] { "M", "C"}; + return Db.Queryable((menu, roleMenu) => new JoinQueryInfos( + JoinType.Left, menu.menuId == roleMenu.Menu_id + )) + .Where((menu, roleMenu) => roleIds.Contains(roleMenu.Role_id) && menuTypes.Contains(menu.menuType) && menu.status == "0") + .OrderBy((menu, roleMenu) => new { menu.parentId, menu.orderNum }) + .Select((menu, roleMenu) => menu).ToList(); + } + + #endregion + + /// + /// 获取菜单详情 + /// + /// + /// + public SysMenu SelectMenuById(int menuId) + { + return Db.Queryable().Where(it => it.menuId == menuId).Single(); + } + + /// + /// 添加菜单 + /// + /// + /// + public int AddMenu(SysMenu menu) + { + menu.Create_time = Db.GetDate(); + menu.menuId = Db.Insertable(menu).ExecuteReturnIdentity(); + return 1; + } + + /// + /// 编辑菜单 + /// + /// + /// + public int EditMenu(SysMenu menu) + { + return Db.Updateable(menu).ExecuteCommand(); + } + + /// + /// 删除菜单 + /// + /// + /// + public int DeleteMenuById(int menuId) + { + return Db.Deleteable().Where(it => it.menuId == menuId).ExecuteCommand(); + } + + /// + /// 菜单排序 + /// + /// 菜单Dto + /// + public int ChangeSortMenu(MenuDto menuDto) + { + var result = Db.Updateable(new SysMenu() { menuId = menuDto.MenuId, orderNum = menuDto.orderNum }) + .UpdateColumns(it => new { it.orderNum }).ExecuteCommand(); + return result; + } + + /// + /// 查询菜单权限 + /// + /// + /// + public List SelectMenuPermsByUserId(long userId) + { + return Db.Queryable((m, rm, ur, r) => new JoinQueryInfos( + JoinType.Left, m.menuId == rm.Menu_id, + JoinType.Left, rm.Role_id == ur.RoleId, + JoinType.Left, ur.RoleId == r.RoleId + )) + //.Distinct() + .Where((m, rm, ur, r) => m.status == "0" && r.Status == "0" && ur.UserId == userId) + .Select((m, rm, ur, r) => m).ToList(); + } + + /// + /// 校验菜单名称是否唯一 + /// + /// + /// + public SysMenu CheckMenuNameUnique(SysMenu menu) + { + return Db.Queryable() + .Where(it => it.menuName == menu.menuName && it.parentId == menu.parentId).Single(); + } + + /// + /// 是否存在菜单子节点 + /// + /// + /// + public int HasChildByMenuId(long menuId) + { + return Db.Queryable().Where(it => it.parentId == menuId).Count(); + } + + #region RoleMenu + + /// + /// 查询菜单使用数量 + /// + /// + /// + public int CheckMenuExistRole(long menuId) + { + return Db.Queryable().Where(it => it.Menu_id == menuId).Count(); + } + + #endregion + } +} diff --git a/ZR.Repository/System/SysOperLogRepository.cs b/ZR.Repository/System/SysOperLogRepository.cs new file mode 100644 index 0000000..840de43 --- /dev/null +++ b/ZR.Repository/System/SysOperLogRepository.cs @@ -0,0 +1,73 @@ +using Infrastructure.Attribute; +using Infrastructure.Extensions; +using System.Collections.Generic; +using ZR.Model; +using ZR.Model.Dto.System; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + [AppService(ServiceLifetime = LifeTime.Transient)] + public class SysOperLogRepository : BaseRepository + { + /// + /// 查询操作日志 + /// + /// + /// 分页数据 + /// + public List GetSysOperLog(SysOperLogDto sysOper, PagerInfo pagerInfo) + { + int totalCount = 0; + var list = Db.Queryable() + .Where(it => it.operTime >= sysOper.BeginTime && it.operTime <= sysOper.EndTime) + .WhereIF(sysOper.Title.IfNotEmpty(), it => it.title.Contains(sysOper.Title)) + .WhereIF(sysOper.operName.IfNotEmpty(), it => it.operName.Contains(sysOper.operName)) + .WhereIF(sysOper.BusinessType != -1, it =>it.businessType == sysOper.BusinessType) + .WhereIF(sysOper.Status != -1, it => it.status == sysOper.Status) + .OrderBy(it => it.OperId, SqlSugar.OrderByType.Desc) + .ToPageList(pagerInfo.PageNum, pagerInfo.PageSize, ref totalCount); + pagerInfo.TotalNum = totalCount; + return list; + } + + /// + /// 添加操作日志 + /// + /// + /// + public void AddSysOperLog(SysOperLog sysOperLog) + { + Db.Insertable(sysOperLog).ExecuteCommandAsync(); + } + + /// + /// 清空日志 + /// + public void ClearOperLog() + { + string sql = "truncate table sys_oper_log"; + Db.Ado.ExecuteCommand(sql); + } + + /// + /// 删除操作日志 + /// + /// + /// + public int DeleteOperLogByIds(long[] operIds) + { + return Db.Deleteable().In(operIds).ExecuteCommand(); + } + + /// + /// 查询操作日志 + /// + /// + /// + public SysOperLog SelectOperLogById(long operId) + { + return Db.Queryable().InSingle(operId); + } + } +} diff --git a/ZR.Repository/System/SysRoleRepository.cs b/ZR.Repository/System/SysRoleRepository.cs new file mode 100644 index 0000000..fc2480d --- /dev/null +++ b/ZR.Repository/System/SysRoleRepository.cs @@ -0,0 +1,182 @@ +using Infrastructure.Attribute; +using SqlSugar; +using System.Collections.Generic; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + /// + /// 角色操作类 + /// + [AppService(ServiceLifetime = LifeTime.Transient)] + public class SysRoleRepository : BaseRepository + { + /// + /// 根据条件分页查询角色数据 + /// + /// + public List SelectRoleList(SysRole sysRole) + { + return Db.Queryable() + .Where(role => role.DelFlag == "0") + .WhereIF(!string.IsNullOrEmpty(sysRole.RoleName), role => role.RoleName.Contains(sysRole.RoleName)) + .WhereIF(!string.IsNullOrEmpty(sysRole.Status), role => role.Status == sysRole.Status) + .WhereIF(!string.IsNullOrEmpty(sysRole.RoleKey), role => role.RoleKey == sysRole.RoleKey) + .OrderBy(role => role.RoleSort) + .ToList(); + } + + /// + /// 根据用户查询 + /// + /// + /// + public List SelectRolePermissionByUserId(long userId) + { + return Db.Queryable() + .Where(role => role.DelFlag == "0") + .Where(it => SqlFunc.Subqueryable().Where(s => s.UserId == userId).Any()) + .OrderBy(role => role.RoleSort) + .ToList(); + } + + /// + /// 查询所有角色 + /// + /// + public List SelectRoleAll() + { + return Db.Queryable().OrderBy(it => it.RoleSort).ToList(); + } + + /// + /// 通过角色ID查询角色 + /// + /// 角色编号 + /// + public SysRole SelectRoleById(long roleId) + { + return Db.Queryable().InSingle(roleId); + } + + /// + /// 通过角色ID删除角色 + /// + /// + /// + public int DeleteRoleByRoleIds(long[] roleId) + { + return Db.Deleteable().In(roleId).ExecuteCommand(); + } + + /// + /// 获取用户所有角色信息 + /// + /// + /// + public List SelectUserRoleListByUserId(long userId) + { + return Db.Queryable((ur, r) => new SqlSugar.JoinQueryInfos( + SqlSugar.JoinType.Left, ur.RoleId == r.RoleId + )).Where((ur, r) => ur.UserId == userId) + .Select((ur, r) => r).ToList(); + } + + #region 用户角色对应菜单 用户N-1角色 + + /// + /// 根据角色获取菜单id + /// + /// + /// + public List SelectRoleMenuByRoleId(long roleId) + { + return Db.Queryable().Where(it => it.Role_id == roleId).ToList(); + } + + /// + /// 批量插入用户菜单 + /// + /// + /// + public int AddRoleMenu(List sysRoleMenus) + { + return Db.Insertable(sysRoleMenus).ExecuteCommand(); + } + + /// + /// 删除角色与菜单关联 + /// + /// + /// + public int DeleteRoleMenuByRoleId(long roleId) + { + return Db.Deleteable().Where(it => it.Role_id == roleId).ExecuteCommand(); + } + + #endregion + + /// + /// 添加角色 + /// + /// + /// + public long InsertRole(SysRole sysRole) + { + sysRole.Create_time = Db.GetDate(); + return Db.Insertable(sysRole).ExecuteReturnBigIdentity(); + } + + /// + /// 修改用户角色 + /// + /// + /// + public int UpdateSysRole(SysRole sysRole) + { + var db = Db; + sysRole.Update_time = db.GetDate(); + + return db.Updateable() + .SetColumns(it => it.Update_time == sysRole.Update_time) + .SetColumns(it => it.Remark == sysRole.Remark) + .SetColumns(it => it.Update_by == sysRole.Update_by) + .SetColumnsIF(!string.IsNullOrEmpty(sysRole.RoleName), it => it.RoleName == sysRole.RoleName) + .SetColumnsIF(!string.IsNullOrEmpty(sysRole.RoleKey), it => it.RoleKey == sysRole.RoleKey) + .SetColumnsIF(sysRole.RoleSort >= 0, it => it.RoleSort == sysRole.RoleSort) + .Where(it => it.RoleId == sysRole.RoleId) + .ExecuteCommand(); + } + + /// + /// 更改角色权限状态 + /// + /// + /// + /// + public int UpdateRoleStatus(SysRole role) + { + return Db.Updateable(role).UpdateColumns(t => new { t.Status }).ExecuteCommand(); + } + + /// + /// 检查角色权限是否存在 + /// + /// 角色权限 + /// + public SysRole CheckRoleKeyUnique(string roleKey) + { + return Db.Queryable().Where(it => it.RoleKey == roleKey).Single(); + } + + /// + /// 校验角色名称是否唯一 + /// + /// 角色名称 + /// + public SysRole CheckRoleNameUnique(string roleName) + { + return Db.Queryable().Where(it => it.RoleName == roleName).Single(); + } + } +} diff --git a/ZR.Repository/System/SysUserPostRepository.cs b/ZR.Repository/System/SysUserPostRepository.cs new file mode 100644 index 0000000..73ffb91 --- /dev/null +++ b/ZR.Repository/System/SysUserPostRepository.cs @@ -0,0 +1,27 @@ +using Infrastructure.Attribute; +using SqlSugar; +using System.Collections.Generic; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + /// + /// 用户岗位 + /// + [AppService(ServiceLifetime = LifeTime.Transient)] + public class SysUserPostRepository : BaseRepository + { + /// + /// 获取用户岗位 + /// + /// + /// + public List SelectPostsByUserId(long userId) + { + return Db.Queryable((p, up) => new JoinQueryInfos( + JoinType.Left, up.PostId == p.PostId + )).Where((p, up) => up.UserId == userId) + .Select().ToList(); + } + } +} diff --git a/ZR.Repository/System/SysUserRepository.cs b/ZR.Repository/System/SysUserRepository.cs new file mode 100644 index 0000000..03ab81c --- /dev/null +++ b/ZR.Repository/System/SysUserRepository.cs @@ -0,0 +1,147 @@ +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using ZR.Model; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + /// + /// 用户管理 + /// + [AppService(ServiceLifetime = LifeTime.Transient)] + public class SysUserRepository : BaseRepository + { + /// + /// 根据条件分页查询用户列表 + /// + /// + /// + /// + public List SelectUserList(SysUser user, PagerInfo pager) + { + string sql = @"SELECT u.*,d.deptName, d.leader + FROM sys_user u + left join sys_dept d on u.deptId = d.deptId + WHERE u.delFlag = '0' "; + int totalCount = 0; + var list = Db.SqlQueryable(sql) + .WhereIF(!string.IsNullOrEmpty(user.UserName), it => it.UserName.Contains(user.UserName)) + .WhereIF(!string.IsNullOrEmpty(user.Status), it => it.Status == user.Status) + .WhereIF(user.BeginTime != DateTime.MinValue && user.BeginTime != null, it => it.Create_time >= user.BeginTime) + .WhereIF(user.EndTime != DateTime.MinValue && user.BeginTime != null, it => it.EndTime <= user.EndTime) + .WhereIF(user.DeptId != 0, it => it.DeptId == user.DeptId) + .OrderBy(it => it.UserId) + .ToPageList(pager.PageNum, pager.PageSize, ref totalCount); + pager.TotalNum = totalCount; + return list; + } + + /// + /// 通过用户ID查询用户 + /// + /// + /// + public SysUser SelectUserById(long userId) + { + return Db.Queryable().Where(f => f.UserId == userId).First(); + } + + /// + /// 校验用户名是否存在 + /// + /// + /// + public int CheckUserNameUnique(string userName) + { + return Db.Queryable().Where(it => it.UserName == userName).Count(); + } + + /// + /// 添加用户 + /// + /// + /// + public int AddUser(SysUser sysUser) + { + sysUser.Create_time = DateTime.Now; + return Db.Insertable(sysUser).ExecuteReturnIdentity(); + } + + /// + /// 重置密码 + /// + /// + /// + /// + public int ResetPwd(long userid, string password) + { + return Db.Updateable(new SysUser() { UserId = userid, Password = password }) + .UpdateColumns(it => new { it.Password }).ExecuteCommand(); + } + + /// + /// 改变用户状态 + /// + /// + /// + public int ChangeUserStatus(SysUser user) + { + return Db.Updateable(user).UpdateColumns(t => new { t.Status }) + .ExecuteCommand(); + } + + /// + /// 删除用户 + /// + /// 用户id + /// + public int DeleteUser(long userid) + { + return Db.Updateable(new SysUser() { UserId = userid, DelFlag = "2" }) + .UpdateColumns(t => t.DelFlag) + .ExecuteCommand(); + } + + /// + /// 修改用户信息 + /// + /// + /// + public int UpdateUser(SysUser user) + { + return Db.Updateable(user) + //.SetColumns(t => new SysUser() + //{ + // UserName = user.UserName, + // Status = user.Status, + // NickName = user.NickName, + // Remark = user.Remark, + // Email = user.Email, + // Update_by = user.Update_by, + // Phonenumber = user.Phonenumber, + // Update_time = DateTime.Now, + // Sex = user.Sex, + // DeptId = user.DeptId + //}) + .IgnoreColumns(ignoreAllNullColumns: true)//忽略所有为null + .IgnoreColumns(it => new { it.Password, it.Avatar }) + .Where(f => f.UserId == user.UserId).ExecuteCommand(); + } + + /// + /// 修改用户头像 + /// + /// + /// + public int UpdatePhoto(SysUser user) + { + return Db.Updateable() + .SetColumns(t => new SysUser() + { + Avatar = user.Avatar + }) + .Where(f => f.UserId == user.UserId).ExecuteCommand(); + } + } +} diff --git a/ZR.Repository/System/SysUserRoleRepository.cs b/ZR.Repository/System/SysUserRoleRepository.cs new file mode 100644 index 0000000..d64bb93 --- /dev/null +++ b/ZR.Repository/System/SysUserRoleRepository.cs @@ -0,0 +1,77 @@ +using Infrastructure.Attribute; +using SqlSugar; +using System.Collections.Generic; +using ZR.Model.System; + +namespace ZR.Repository.System +{ + [AppService(ServiceLifetime = LifeTime.Transient)] + public class SysUserRoleRepository : BaseRepository + { + /// + /// 删除用户角色 + /// + /// + /// + public int DeleteUserRoleByUserId(int userId) + { + return Db.Deleteable().Where(it => it.UserId == userId).ExecuteCommand(); + } + + /// + /// 批量删除角色对应用户 + /// + /// 角色id + /// 用户id集合 + /// + public int DeleteRoleUserByUserIds(long roleId, List userIds) + { + return Db.Deleteable().Where(it => it.RoleId == roleId && userIds.Contains(it.UserId)) + .ExecuteCommand(); + } + + /// + /// 添加用户角色 + /// + /// + /// + public int AddUserRole(List sysUsers) + { + return Db.Insertable(sysUsers).ExecuteCommand(); + } + + /// + /// 删除角色关联的用户 + /// + /// + /// + public int DeleteUserRoleByRoleId(int roleId) + { + return Db.Deleteable().In(roleId).ExecuteCommand(); + } + + /// + /// 获取角色分配个数 + /// + /// + /// + public int CountUserRoleByRoleId(long roleId) + { + return Db.Queryable().Where(it => it.RoleId == roleId).Count(); + } + + /// + /// 获取用户数据根据角色id + /// + /// + /// + public List GetSysUsersByRoleId(long roleId) + { + return Db.Queryable((t1, user) => new JoinQueryInfos( + JoinType.Left, t1.UserId == user.UserId)) + .Where((t1, user) => t1.RoleId == roleId && user.DelFlag == "0") + .Select((t1, user) => user) + .ToList(); + } + } +} diff --git a/ZR.Repository/ZR.Repository.csproj b/ZR.Repository/ZR.Repository.csproj new file mode 100644 index 0000000..c4175f5 --- /dev/null +++ b/ZR.Repository/ZR.Repository.csproj @@ -0,0 +1,18 @@ + + + + net5.0 + + + + + + + + + + + + + + diff --git a/ZR.Service/BaseService.cs b/ZR.Service/BaseService.cs new file mode 100644 index 0000000..f2d3830 --- /dev/null +++ b/ZR.Service/BaseService.cs @@ -0,0 +1,313 @@ +using Infrastructure.Attribute; +using Infrastructure.Model; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using ZR.Model; +using ZR.Repository.DbProvider; + +namespace ZR.Service +{ + /// + /// 基础服务定义 + /// + /// + public class BaseService : SugarDbContext, IBaseService where T : class, new() + { + #region 事务 + + /// + /// 启用事务 + /// + public void BeginTran() + { + Db.Ado.BeginTran(); + } + + /// + /// 提交事务 + /// + public void CommitTran() + { + Db.Ado.CommitTran(); + } + + /// + /// 回滚事务 + /// + public void RollbackTran() + { + Db.Ado.RollbackTran(); + } + + #endregion + + #region 添加操作 + /// + /// 添加一条数据 + /// + /// T + /// + public int Add(T parm) + { + return Db.Insertable(parm).RemoveDataCache().ExecuteCommand(); + } + + /// + /// 批量添加数据 + /// + /// List + /// + public int Add(List parm) + { + return Db.Insertable(parm).RemoveDataCache().ExecuteCommand(); + } + + /// + /// 添加或更新数据 + /// + /// List + /// + public T Saveable(T parm, Expression> uClumns = null, Expression> iColumns = null) + { + var command = Db.Saveable(parm); + + if (uClumns != null) + { + command = command.UpdateIgnoreColumns(uClumns); + } + + if (iColumns != null) + { + command = command.InsertIgnoreColumns(iColumns); + } + + return command.ExecuteReturnEntity(); + } + + /// + /// 批量添加或更新数据 + /// + /// List + /// + public List Saveable(List parm, Expression> uClumns = null, Expression> iColumns = null) + { + var command = Db.Saveable(parm); + + if (uClumns != null) + { + command = command.UpdateIgnoreColumns(uClumns); + } + + if (iColumns != null) + { + command = command.InsertIgnoreColumns(iColumns); + } + + return command.ExecuteReturnList(); + } + #endregion + + #region 查询操作 + + /// + /// 根据条件查询数据是否存在 + /// + /// 条件表达式树 + /// + public bool Any(Expression> where) + { + return Db.Queryable().Any(where); + } + + /// + /// 根据条件合计字段 + /// + /// 条件表达式树 + /// + public TResult Sum(Expression> where, Expression> field) + { + return Db.Queryable().Where(where).Sum(field); + } + + /// + /// 根据主值查询单条数据 + /// + /// 主键值 + /// 泛型实体 + public T GetId(object pkValue) + { + return Db.Queryable().InSingle(pkValue); + } + + /// + /// 根据主键查询多条数据 + /// + /// + /// + public List GetIn(object[] ids) + { + return Db.Queryable().In(ids).ToList(); + } + + /// + /// 根据条件取条数 + /// + /// 条件表达式树 + /// + public int GetCount(Expression> where) + { + return Db.Queryable().Count(where); + + } + + /// + /// 查询所有数据(无分页,请慎用) + /// + /// + public List GetAll(bool useCache = false, int cacheSecond = 3600) + { + return Db.Queryable().WithCacheIF(useCache, cacheSecond).ToList(); + } + + /// + /// 获得一条数据 + /// + /// Expression> + /// + public T GetFirst(Expression> where) + { + return Db.Queryable().Where(where).First(); + } + + /// + /// 获得一条数据 + /// + /// string + /// + public T GetFirst(string parm) + { + return Db.Queryable().Where(parm).First(); + } + + + /// + /// 根据条件查询分页数据 + /// + /// + /// + /// + public PagedInfo GetPages(Expression> where, PagerInfo parm) + { + var source = Db.Queryable().Where(where); + + return source.ToPage(parm); + } + + public PagedInfo GetPages(Expression> where, PagerInfo parm, Expression> order, string orderEnum = "Asc") + { + var source = Db.Queryable().Where(where).OrderByIF(orderEnum == "Asc", order, OrderByType.Asc).OrderByIF(orderEnum == "Desc", order, OrderByType.Desc); + + return source.ToPage(parm); + } + + /// + /// 根据条件查询数据 + /// + /// 条件表达式树 + /// + public List GetWhere(Expression> where, bool useCache = false, int cacheSecond = 3600) + { + var query = Db.Queryable().Where(where).WithCacheIF(useCache, cacheSecond); + return query.ToList(); + } + + /// + /// 根据条件查询数据 + /// + /// 条件表达式树 + /// + public List GetWhere(Expression> where, Expression> order, string orderEnum = "Asc", bool useCache = false, int cacheSecond = 3600) + { + var query = Db.Queryable().Where(where).OrderByIF(orderEnum == "Asc", order, OrderByType.Asc).OrderByIF(orderEnum == "Desc", order, OrderByType.Desc).WithCacheIF(useCache, cacheSecond); + return query.ToList(); + } + + #endregion + + #region 修改操作 + + /// + /// 修改一条数据 + /// + /// T + /// + public int Update(T parm) + { + return Db.Updateable(parm).RemoveDataCache().ExecuteCommand(); + } + + /// + /// 批量修改 + /// + /// T + /// + public int Update(List parm) + { + return Db.Updateable(parm).RemoveDataCache().ExecuteCommand(); + } + + /// + /// 按查询条件更新 + /// + /// + /// + /// + public int Update(Expression> where, Expression> columns) + { + return Db.Updateable().SetColumns(columns).Where(where).RemoveDataCache().ExecuteCommand(); + } + + #endregion + + #region 删除操作 + + /// + /// 删除一条或多条数据 + /// + /// string + /// + public int Delete(object id) + { + return Db.Deleteable(id).RemoveDataCache().ExecuteCommand(); + } + + /// + /// 删除一条或多条数据 + /// + /// string + /// + public int Delete(object[] ids) + { + return Db.Deleteable().In(ids).RemoveDataCache().ExecuteCommand(); + } + + /// + /// 根据条件删除一条或多条数据 + /// + /// 过滤条件 + /// + public int Delete(Expression> where) + { + return Db.Deleteable().Where(where).RemoveDataCache().ExecuteCommand(); + } + + public int DeleteTable() + { + return Db.Deleteable().RemoveDataCache().ExecuteCommand(); + } + #endregion + + } +} diff --git a/ZR.Service/Extensions/QueryableExtension.cs b/ZR.Service/Extensions/QueryableExtension.cs new file mode 100644 index 0000000..1bf1a23 --- /dev/null +++ b/ZR.Service/Extensions/QueryableExtension.cs @@ -0,0 +1,66 @@ + +using Infrastructure.Model; +using SqlSugar; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ZR.Model; +using ZR.Model.Vo; + +namespace ZR.Service +{ + public static class QueryableExtension + { + /// + /// 读取列表 + /// + /// + /// + /// + /// + /// + //public static async Task> ToPageAsync(this ISugarQueryable source, PageParm parm) + //{ + // var page = new PagedInfo(); + // var total = await source.CountAsync(); + // page.TotalCount = total; + // page.TotalPages = total / parm.PageSize; + + // if (total % parm.PageSize > 0) + // page.TotalPages++; + + // page.PageSize = parm.PageSize; + // page.PageIndex = parm.PageIndex; + + // page.DataSource = await source.OrderByIF(!string.IsNullOrEmpty(parm.Sort), $"{parm.OrderBy} {(parm.Sort == "descending" ? "desc" : "asc")}").ToPageListAsync(parm.PageIndex, parm.PageSize); + // return page; + //} + + /// + /// 读取列表 + /// + /// + /// + /// + /// + /// + public static PagedInfo ToPage(this ISugarQueryable source, PagerInfo parm) + { + var page = new PagedInfo(); + var total = source.Count(); + page.TotalCount = total; + //page.TotalPage = total / parm.PageSize; + + //if (total % parm.PageSize > 0) + // page.TotalPages++; + + page.PageSize = parm.PageSize; + page.PageIndex = parm.PageNum; + + //page.DataSource = source.OrderByIF(!string.IsNullOrEmpty(parm.Sort), $"{parm.OrderBy} {(parm.Sort == "descending" ? "desc" : "asc")}").ToPageList(parm.PageNum, parm.PageSize); + page.Result = source.ToPageList(parm.PageNum, parm.PageSize); + return page; + } + + } +} diff --git a/ZR.Service/IBaseService.cs b/ZR.Service/IBaseService.cs new file mode 100644 index 0000000..72959f0 --- /dev/null +++ b/ZR.Service/IBaseService.cs @@ -0,0 +1,231 @@ +using Infrastructure.Model; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; +using ZR.Model; + +namespace ZR.Service +{ + /// + /// 基础服务定义 + /// + /// + public interface IBaseService where T : class + { + + #region 事务 + + /// + /// 启用事务 + /// + void BeginTran(); + + + /// + /// 提交事务 + /// + void CommitTran(); + + + /// + /// 回滚事务 + /// + void RollbackTran(); + + #endregion + + #region 添加操作 + /// + /// 添加一条数据 + /// + /// + /// + int Add(T parm); + + /// + /// 批量添加数据 + /// + /// List + /// + int Add(List parm); + + /// + /// 添加或更新数据 + /// + /// + /// + T Saveable(T parm, Expression> uClumns = null, Expression> iColumns = null); + + /// + /// 批量添加或更新数据 + /// + /// List + /// + List Saveable(List parm, Expression> uClumns = null, Expression> iColumns = null); + + + + #endregion + + #region 查询操作 + + /// + /// 根据条件查询数据是否存在 + /// + /// 条件表达式树 + /// + bool Any(Expression> where); + + /// + /// 根据条件合计字段 + /// + /// 条件表达式树 + /// + TResult Sum(Expression> where, Expression> field); + + + /// + /// 根据主值查询单条数据 + /// + /// 主键值 + /// 泛型实体 + T GetId(object pkValue); + + + /// + /// 根据主键查询多条数据 + /// + /// + /// + List GetIn(object[] ids); + + + /// + /// 根据条件取条数 + /// + /// 条件表达式树 + /// + int GetCount(Expression> where); + + + /// + /// 查询所有数据(无分页,请慎用) + /// + /// + List GetAll(bool useCache = false, int cacheSecond = 3600); + + + /// + /// 获得一条数据 + /// + /// Expression> + /// + T GetFirst(Expression> where); + + + /// + /// 获得一条数据 + /// + /// string + /// + T GetFirst(string parm); + + /// + /// 根据条件查询分页数据 + /// + /// + /// + /// + PagedInfo GetPages(Expression> where, PagerInfo parm); + + /// + /// 根据条件查询分页 + /// + /// + /// + /// + /// + /// + PagedInfo GetPages(Expression> where, PagerInfo parm, Expression> order, string orderEnum = "Asc"); + + /// + /// 根据条件查询数据 + /// + /// 条件表达式树 + /// + List GetWhere(Expression> where, bool useCache = false, int cacheSecond = 3600); + + /// + /// 根据条件查询数据 + /// + /// 条件表达式树 + /// + List GetWhere(Expression> where, Expression> order, string orderEnum = "Asc", bool useCache = false, int cacheSecond = 3600); + + + #endregion + + #region 修改操作 + + /// + /// 修改一条数据 + /// + /// T + /// + int Update(T parm); + + + /// + /// 批量修改 + /// + /// T + /// + int Update(List parm); + + + /// + /// 按查询条件更新 + /// + /// + /// + /// + int Update(Expression> where, Expression> columns); + + + #endregion + + #region 删除操作 + + /// + /// 删除一条或多条数据 + /// + /// string + /// + int Delete(object id); + + + /// + /// 删除一条或多条数据 + /// + /// string + /// + int Delete(object[] ids); + + /// + /// 根据条件删除一条或多条数据 + /// + /// 过滤条件 + /// + int Delete(Expression> where); + + /// + /// 清空表 + /// + /// + int DeleteTable(); + #endregion + + } +} \ No newline at end of file diff --git a/ZR.Service/IService/IArticleCategoryService.cs b/ZR.Service/IService/IArticleCategoryService.cs new file mode 100644 index 0000000..2db0a40 --- /dev/null +++ b/ZR.Service/IService/IArticleCategoryService.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface IArticleCategoryService : IBaseService + { + List BuildCategoryTree(List categories); + } +} diff --git a/ZR.Service/IService/IArticleService.cs b/ZR.Service/IService/IArticleService.cs new file mode 100644 index 0000000..9e9dcd7 --- /dev/null +++ b/ZR.Service/IService/IArticleService.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ZR.Model.Dto.System; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface IArticleService : IBaseService
+ { + + } +} diff --git a/ZR.Service/IService/ISysDeptService.cs b/ZR.Service/IService/ISysDeptService.cs new file mode 100644 index 0000000..78bc4a0 --- /dev/null +++ b/ZR.Service/IService/ISysDeptService.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; +using ZR.Model.Vo.System; + +namespace ZR.Service.IService +{ + public interface ISysDeptService : IBaseService + { + public List GetSysDepts(SysDept dept); + public string CheckDeptNameUnique(SysDept dept); + public int InsertDept(SysDept dept); + public int UpdateDept(SysDept dept); + public void UpdateDeptChildren(long deptId, string newAncestors, string oldAncestors); + public List GetChildrenDepts(List depts, long deptId); + public List BuildDeptTree(List depts); + public List BuildDeptTreeSelect(List depts); + } +} diff --git a/ZR.Service/IService/ISysDictDataService.cs b/ZR.Service/IService/ISysDictDataService.cs new file mode 100644 index 0000000..63b7d29 --- /dev/null +++ b/ZR.Service/IService/ISysDictDataService.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysDictDataService : IBaseService + { + public List SelectDictDataList(SysDictData dictData); + public List SelectDictDataByType(string dictType); + public SysDictData SelectDictDataById(long dictCode); + public long InsertDictData(SysDictData dict); + public long UpdateDictData(SysDictData dict); + public int DeleteDictDataByIds(long[] dictCodes); + } +} diff --git a/ZR.Service/IService/ISysDictService.cs b/ZR.Service/IService/ISysDictService.cs new file mode 100644 index 0000000..f2d0697 --- /dev/null +++ b/ZR.Service/IService/ISysDictService.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + /// + /// + /// + public interface ISysDictService: IBaseService + { + public List SelectDictTypeList(SysDictType dictType); + + /// + /// 根据Id查询 + /// + /// + /// + public SysDictType SelectDictTypeById(long id); + + /// + /// 校验字典类型称是否唯一 + /// + /// 字典类型 + /// + public string CheckDictTypeUnique(SysDictType dictType); + + /// + /// 删除一个 + /// + /// + /// + public int DeleteDictTypeById(long id); + + /// + /// 批量删除字典数据信息 + /// + /// + /// + public int DeleteDictTypeByIds(long[] dictIds); + + /// + /// 插入字典类型 + /// + /// + /// + public long InsertDictType(SysDictType sysDictType); + + /// + /// 修改字典类型 + /// + /// + /// + public int UpdateDictType(SysDictType sysDictType); + } +} diff --git a/ZR.Service/IService/ISysFileService.cs b/ZR.Service/IService/ISysFileService.cs new file mode 100644 index 0000000..d1deeb4 --- /dev/null +++ b/ZR.Service/IService/ISysFileService.cs @@ -0,0 +1,9 @@ +using Infrastructure.Attribute; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysFileService : IBaseService + { + } +} diff --git a/ZR.Service/IService/ISysLoginService.cs b/ZR.Service/IService/ISysLoginService.cs new file mode 100644 index 0000000..22272e2 --- /dev/null +++ b/ZR.Service/IService/ISysLoginService.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model; +using ZR.Model.Dto.System; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysLoginService + { + public SysUser Login(LoginBodyDto loginBody, SysLogininfor logininfor); + + /// + /// 查询操作日志 + /// + /// + /// 分页 + /// + public List GetLoginLog(SysLogininfor logininfoDto, PagerInfo pager); + + /// + /// 记录登录日志 + /// + /// + /// + public void AddLoginInfo(SysLogininfor sysLogininfor); + + /// + /// 清空登录日志 + /// + public void TruncateLogininfo(); + + /// + /// 删除登录日志 + /// + /// + /// + public int DeleteLogininforByIds(long[] ids); + } +} diff --git a/ZR.Service/IService/ISysMenuService.cs b/ZR.Service/IService/ISysMenuService.cs new file mode 100644 index 0000000..04c90c4 --- /dev/null +++ b/ZR.Service/IService/ISysMenuService.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Model.System.Vo; +using ZR.Model.Vo.System; + +namespace ZR.Service.IService +{ + public interface ISysMenuService: IBaseService + { + public List SelectMenuList(long userId); + + public List SelectMenuList(SysMenu menu, long userId); + + public SysMenu GetMenuByMenuId(int menuId); + + public int AddMenu(SysMenu menu); + + public int EditMenu(SysMenu menu); + + public int DeleteMenuById(int menuId); + + public string CheckMenuNameUnique(SysMenu menu); + + public int ChangeSortMenu(MenuDto menuDto); + + public bool HasChildByMenuId(long menuId); + + public List SelectMenuTreeByUserId(long userId); + + public List SelectMenuPermsListByUserId(long userId); + + public List SelectMenuPermsByUserId(long userId); + + public bool CheckMenuExistRole(long menuId); + + public List BuildMenus(List menus); + + public List BuildMenuTreeSelect(List menus); + } +} diff --git a/ZR.Service/IService/ISysOperLogService.cs b/ZR.Service/IService/ISysOperLogService.cs new file mode 100644 index 0000000..eef404e --- /dev/null +++ b/ZR.Service/IService/ISysOperLogService.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using ZR.Model; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Service.System; + +namespace ZR.Service.IService +{ + public interface ISysOperLogService : IBaseService + { + public void InsertOperlog(SysOperLog operLog); + + /// + /// 查询系统操作日志集合 + /// + /// 操作日志对象 + /// + /// 操作日志集合 + public List SelectOperLogList(SysOperLogDto operLog, PagerInfo pager); + + /// + /// 清空操作日志 + /// + public void CleanOperLog(); + + /// + /// 批量删除系统操作日志 + /// + /// 需要删除的操作日志ID + /// 结果 + public int DeleteOperLogByIds(long[] operIds); + + /// + /// 查询操作日志详细 + /// + /// 操作ID + /// 操作日志对象 + public SysOperLog SelectOperLogById(long operId); + } +} diff --git a/ZR.Service/IService/ISysPermissionService.cs b/ZR.Service/IService/ISysPermissionService.cs new file mode 100644 index 0000000..6897396 --- /dev/null +++ b/ZR.Service/IService/ISysPermissionService.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysPermissionService + { + public List GetRolePermission(SysUser user); + public List GetMenuPermission(SysUser user); + } +} diff --git a/ZR.Service/IService/ISysPostService.cs b/ZR.Service/IService/ISysPostService.cs new file mode 100644 index 0000000..1df4365 --- /dev/null +++ b/ZR.Service/IService/ISysPostService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysPostService: IBaseService + { + string CheckPostNameUnique(SysPost sysPost); + string CheckPostCodeUnique(SysPost sysPost); + } +} diff --git a/ZR.Service/IService/ISysRoleService.cs b/ZR.Service/IService/ISysRoleService.cs new file mode 100644 index 0000000..3a34389 --- /dev/null +++ b/ZR.Service/IService/ISysRoleService.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysRoleService: IBaseService + { + /// + /// 根据条件分页查询角色数据 + /// + /// 角色信息 + /// 角色数据集合信息 + public List SelectRoleList(SysRole role); + + /// + /// 查询所有角色 + /// + /// + /// + public List SelectRoleAll(); + + /// + /// 根据用户查询 + /// + /// + /// + public List SelectRolePermissionByUserId(long userId); + + /// + /// 通过角色ID查询角色 + /// + /// 角色ID + /// 角色对象信息 + public SysRole SelectRoleById(long roleId); + + /// + /// 批量删除角色信息 + /// + /// 需要删除的角色ID + /// + public int DeleteRoleByRoleId(long[] roleIds); + + /// + /// 更改角色权限状态 + /// + /// + /// + public int UpdateRoleStatus(SysRole roleDto); + + /// + /// 校验角色权限是否唯一 + /// + /// 角色信息 + /// + public string CheckRoleKeyUnique(SysRole sysRole); + + /// + /// 校验角色是否允许操作 + /// + /// + public void CheckRoleAllowed(SysRole role); + + /// + /// 新增保存角色信息 + /// + /// 角色信息 + /// + public long InsertRole(SysRole sysRole); + + /// + /// 通过角色ID删除角色和菜单关联 + /// + /// 角色ID + /// + public int DeleteRoleMenuByRoleId(long roleId); + + #region Service + + + /// + /// 新增角色菜单信息 + /// + /// + /// + public int InsertRoleMenu(SysRole sysRoleDto); + + /// + /// 判断是否是管理员 + /// + /// + /// + public bool IsAdmin(long userid); + + /// + /// 获取角色菜单id集合 + /// + /// + /// + public List SelectUserRoleMenus(long roleId); + + /// + /// 获取用户角色列表 + /// + /// + /// + public List SelectUserRoleListByUserId(long userId); + + /// + /// 获取用户权限集合 + /// + /// + /// + public List SelectUserRoles(long userId); + + /// + /// 获取用户权限字符串集合 + /// + /// + /// + public List SelectUserRoleKeys(long userId); + + public List SelectUserRoleNames(long userId); + + #endregion + + /// + /// 修改保存角色信息 + /// + /// 角色信息 + /// + public int UpdateRole(SysRole sysRole); + } +} diff --git a/ZR.Service/IService/ISysTasksLogService.cs b/ZR.Service/IService/ISysTasksLogService.cs new file mode 100644 index 0000000..ef0bb09 --- /dev/null +++ b/ZR.Service/IService/ISysTasksLogService.cs @@ -0,0 +1,14 @@ +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysTasksLogService : IBaseService + { + /// + /// 记录任务执行日志 + /// + /// + //public int AddTaskLog(string jobId); + public SysTasksLog AddTaskLog(string jobId, SysTasksLog tasksLog); + } +} diff --git a/ZR.Service/IService/ISysTasksQzService.cs b/ZR.Service/IService/ISysTasksQzService.cs new file mode 100644 index 0000000..a7c60c7 --- /dev/null +++ b/ZR.Service/IService/ISysTasksQzService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysTasksQzService: IBaseService + { + } +} diff --git a/ZR.Service/IService/ISysUserPostService.cs b/ZR.Service/IService/ISysUserPostService.cs new file mode 100644 index 0000000..2335b17 --- /dev/null +++ b/ZR.Service/IService/ISysUserPostService.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysUserPostService: IBaseService + { + public void InsertUserPost(SysUser user); + + public List GetUserPostsByUserId(long userId); + + public string GetPostsStrByUserId(long userId); + } +} diff --git a/ZR.Service/IService/ISysUserRoleService.cs b/ZR.Service/IService/ISysUserRoleService.cs new file mode 100644 index 0000000..55d4b01 --- /dev/null +++ b/ZR.Service/IService/ISysUserRoleService.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ZR.Model.Dto.System; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysUserRoleService + { + public int CountUserRoleByRoleId(long roleId); + + /// + /// 删除用户角色 + /// + /// + /// + public int DeleteUserRoleByUserId(int userId); + + /// + /// 批量删除角色对应用户 + /// + /// + /// + /// + public int DeleteRoleUserByUserIds(long roleId, List userIds); + + /// + /// 添加用户角色 + /// + /// + /// + public int AddUserRole(List sysUsers); + + public List GetSysUsersByRoleId(long roleId); + + /// + /// 新增用户角色信息 + /// + /// + /// + public int InsertUserRole(SysUser user); + + /// + /// 新增加角色用户 + /// + /// 角色id + /// 用户ids + /// + public int InsertRoleUser(RoleUsersCreateDto roleUsersCreateDto); + } +} diff --git a/ZR.Service/IService/ISysUserService.cs b/ZR.Service/IService/ISysUserService.cs new file mode 100644 index 0000000..ad65cb1 --- /dev/null +++ b/ZR.Service/IService/ISysUserService.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ZR.Model; +using ZR.Model.System; + +namespace ZR.Service.IService +{ + public interface ISysUserService : IBaseService + { + public List SelectUserList(SysUser user, PagerInfo pager); + + /// + /// 通过用户ID查询用户 + /// + /// + /// + public SysUser SelectUserById(long userId); + + /// + /// 校验用户名称是否唯一 + /// + /// + /// + public string CheckUserNameUnique(string userName); + + /// + /// 新增保存用户信息 + /// + /// + /// + public long InsertUser(SysUser sysUser); + + /// + /// 修改用户信息 + /// + /// + /// + public int UpdateUser(SysUser user); + + public int ChangeUser(SysUser user); + + /// + /// 重置密码 + /// + /// + /// + /// + public int ResetPwd(long userid, string password); + + public int ChangeUserStatus(SysUser user); + + /// + /// 删除用户 + /// + /// + /// + public int DeleteUser(long userid); + + /// + /// 修改用户头像 + /// + /// + /// + public int UpdatePhoto(SysUser user); + } +} diff --git a/ZR.Service/System/ArticleCategoryService.cs b/ZR.Service/System/ArticleCategoryService.cs new file mode 100644 index 0000000..9dd5a0a --- /dev/null +++ b/ZR.Service/System/ArticleCategoryService.cs @@ -0,0 +1,71 @@ +using Infrastructure.Attribute; +using System.Collections.Generic; +using System.Linq; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + /// + /// 文章目录 + /// + [AppService(ServiceType = typeof(IArticleCategoryService), ServiceLifetime = LifeTime.Transient)] + public class ArticleCategoryService : BaseService, IArticleCategoryService + { + /// + /// 构建前端所需要树结构 + /// + /// 目录列表 + /// + public List BuildCategoryTree(List categories) + { + List returnList = new List(); + List tempList = categories.Select(f => f.Category_Id).ToList(); + foreach (var dept in categories) + { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.Contains(dept.ParentId)) + { + RecursionFn(categories, dept); + returnList.Add(dept); + } + } + + if (!returnList.Any()) + { + returnList = categories; + } + return returnList; + } + + /// + /// 递归列表 + /// + /// + /// + private void RecursionFn(List list, ArticleCategory t) + { + //得到子节点列表 + List childList = GetChildList(list, t); + t.Children = childList; + foreach (var item in childList) + { + if (GetChildList(list, item).Count() > 0) + { + RecursionFn(list, item); + } + } + } + + /// + /// 递归获取子菜单 + /// + /// 所有菜单 + /// + /// + private List GetChildList(List list, ArticleCategory t) + { + return list.Where(p => p.ParentId == t.Category_Id).ToList(); + } + } +} diff --git a/ZR.Service/System/ArticleService.cs b/ZR.Service/System/ArticleService.cs new file mode 100644 index 0000000..fd7c06d --- /dev/null +++ b/ZR.Service/System/ArticleService.cs @@ -0,0 +1,15 @@ +using Infrastructure.Attribute; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + /// + /// + /// + [AppService(ServiceType = typeof(IArticleService), ServiceLifetime = LifeTime.Transient)] + public class ArticleService : BaseService
, IArticleService + { + + } +} diff --git a/ZR.Service/System/SysDeptService.cs b/ZR.Service/System/SysDeptService.cs new file mode 100644 index 0000000..f83b21c --- /dev/null +++ b/ZR.Service/System/SysDeptService.cs @@ -0,0 +1,220 @@ +using Infrastructure; +using Infrastructure.Attribute; +using Infrastructure.Extensions; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ZR.Common; +using ZR.Model.System; +using ZR.Model.Vo.System; +using ZR.Repository.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + /// + /// 部门管理 + /// + [AppService(ServiceType = typeof(ISysDeptService), ServiceLifetime = LifeTime.Transient)] + public class SysDeptService : BaseService, ISysDeptService + { + public SysDeptRepository DeptRepository; + + public SysDeptService(SysDeptRepository deptRepository) + { + DeptRepository = deptRepository; + } + + /// + /// 查询部门管理数据 + /// + /// + /// + public List GetSysDepts(SysDept dept) + { + //开始拼装查询条件 + var predicate = Expressionable.Create(); + predicate = predicate.And(it => it.DelFlag == "0"); + predicate = predicate.AndIF(dept.DeptName.IfNotEmpty(), it => it.DeptName.Contains(dept.DeptName)); + predicate = predicate.AndIF(dept.Status.IfNotEmpty(), it => it.Status == dept.Status); + + var response = GetWhere(predicate.ToExpression()); + + return response; + } + + /// + /// 校验部门名称是否唯一 + /// + /// + /// + public string CheckDeptNameUnique(SysDept dept) + { + long deptId = dept.DeptId == 0 ? -1L : dept.DeptId; + SysDept info = GetFirst(it => it.DeptName == dept.DeptName && it.ParentId == dept.ParentId); + if (info != null && info.DeptId != deptId) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /// + /// 新增保存部门信息 + /// + /// + /// + public int InsertDept(SysDept dept) + { + SysDept info = GetFirst(it => it.DeptId == dept.ParentId); + //如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.Equals(info.Status)) + { + throw new CustomException("部门停用,不允许新增"); + } + + dept.Ancestors = info.Ancestors + "," + dept.ParentId; + return Add(dept); + } + + /// + /// 修改保存部门信息 + /// + /// + /// + public int UpdateDept(SysDept dept) + { + SysDept newParentDept = GetFirst(it => it.ParentId == dept.ParentId); + SysDept oldDept = GetFirst(m => m.DeptId == dept.DeptId); + if (newParentDept != null && oldDept != null) + { + string newAncestors = newParentDept.Ancestors + "," + newParentDept.DeptId; + string oldAncestors = oldDept.Ancestors; + dept.Ancestors = newAncestors; + UpdateDeptChildren(dept.DeptId, newAncestors, oldAncestors); + } + int result = Update(dept); + if (UserConstants.DEPT_NORMAL.Equals(dept.Status)) + { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + //UpdateParentDeptStatus(dept); + } + return result; + } + + /// + /// 修改该部门的父级部门状态 + /// + /// 当前部门 + private void UpdateParentDeptStatus(SysDept dept) + { + string updateBy = dept.Update_by; + dept = GetFirst(it => it.DeptId == dept.DeptId); + dept.Update_by = updateBy; + //DeptRepository.UpdateParentDeptStatus(dept); + } + + /// + /// 修改子元素关系 + /// + /// 被修改的部门ID + /// 新的父ID集合 + /// 旧的父ID集合 + public void UpdateDeptChildren(long deptId, string newAncestors, string oldAncestors) + { + List children = GetChildrenDepts(GetSysDepts(new SysDept()), deptId); + + foreach (var child in children) + { + child.Ancestors = child.Ancestors.Replace(oldAncestors, newAncestors); + + if (child.DeptId.Equals(deptId)) + { + DeptRepository.UdateDeptChildren(child); + } + } + } + + public List GetChildrenDepts(List depts, long deptId) + { + return depts.FindAll(delegate (SysDept item) + { + long[] pid = Tools.SpitLongArrary(item.Ancestors); + return pid.Contains(deptId); + }); + } + + /// + /// 构建前端所需要树结构 + /// + /// 部门列表 + /// + public List BuildDeptTree(List depts) + { + List returnList = new List(); + List tempList = depts.Select(f => f.DeptId).ToList(); + foreach (var dept in depts) + { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.Contains(dept.ParentId)) + { + RecursionFn(depts, dept); + returnList.Add(dept); + } + } + + if (!returnList.Any()) + { + returnList = depts; + } + return returnList; + } + + /// + /// 构建前端所需下拉树结构 + /// + /// + /// + public List BuildDeptTreeSelect(List depts) + { + List menuTrees = BuildDeptTree(depts); + List treeMenuVos = new List(); + foreach (var item in menuTrees) + { + treeMenuVos.Add(new TreeSelectVo(item)); + } + return treeMenuVos; + } + + /// + /// 递归列表 + /// + /// + /// + private void RecursionFn(List list, SysDept t) + { + //得到子节点列表 + List childList = GetChildList(list, t); + t.children = childList; + foreach (var item in childList) + { + if (GetChildList(list, item).Count() > 0) + { + RecursionFn(list, item); + } + } + } + /// + /// 递归获取子菜单 + /// + /// 所有菜单 + /// + /// + private List GetChildList(List list, SysDept dept) + { + return list.Where(p => p.ParentId == dept.DeptId).ToList(); + } + } +} diff --git a/ZR.Service/System/SysDictDataService.cs b/ZR.Service/System/SysDictDataService.cs new file mode 100644 index 0000000..9e65056 --- /dev/null +++ b/ZR.Service/System/SysDictDataService.cs @@ -0,0 +1,94 @@ +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Common; +using ZR.Model.System; +using ZR.Repository.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + [AppService(ServiceType = typeof(ISysDictDataService), ServiceLifetime = LifeTime.Transient)] + public class SysDictDataService: BaseService, ISysDictDataService + { + + private readonly SysDictDataRepository SysDictDataRepository; + public SysDictDataService(SysDictDataRepository sysDictDataRepository) + { + SysDictDataRepository = sysDictDataRepository; + } + + /// + /// 查询字典数据 + /// + /// + /// + public List SelectDictDataList(SysDictData dictData) + { + return SysDictDataRepository.SelectDictDataList(dictData); + } + + /// + /// 根据字典类型查询 + /// + /// + /// + public List SelectDictDataByType(string dictType) + { + string CK = $"SelectDictDataByType_{dictType}"; + if (!(CacheHelper.GetCache(CK) is List list)) + { + list = SysDictDataRepository.SelectDictDataByType(dictType); + CacheHelper.SetCache(CK, list, 30); + } + return list; + } + + /// + /// 根据字典数据ID查询信息 + /// + /// + /// + public SysDictData SelectDictDataById(long dictCode) + { + string CK = $"SelectDictDataByCode_{dictCode}"; + if (!(CacheHelper.GetCache(CK) is SysDictData list)) + { + list = SysDictDataRepository.SelectDictDataById(dictCode); + CacheHelper.SetCache(CK, list, 5); + } + return list; + } + + /// + /// 插入数据 + /// + /// + /// + public long InsertDictData(SysDictData dict) + { + return SysDictDataRepository.InsertDictData(dict); + } + + /// + /// 修改数据 + /// + /// + /// + public long UpdateDictData(SysDictData dict) + { + return SysDictDataRepository.UpdateDictData(dict); + } + + /// + /// 批量删除字典数据信息 + /// + /// + /// + public int DeleteDictDataByIds(long[] dictCodes) + { + return SysDictDataRepository.DeleteDictDataByIds(dictCodes); + } + } +} diff --git a/ZR.Service/System/SysDictService.cs b/ZR.Service/System/SysDictService.cs new file mode 100644 index 0000000..3eef28a --- /dev/null +++ b/ZR.Service/System/SysDictService.cs @@ -0,0 +1,121 @@ +using Infrastructure; +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; +using ZR.Repository.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + /// + /// 字典类型 + /// + [AppService(ServiceType = typeof(ISysDictService), ServiceLifetime = LifeTime.Transient)] + public class SysDictService : BaseService, ISysDictService + { + private SysDictRepository sysDictRepository; + private SysDictDataRepository DictDataRepository; + + public SysDictService(SysDictRepository sysDictRepository, SysDictDataRepository dictDataRepository) + { + this.sysDictRepository = sysDictRepository; + this.DictDataRepository = dictDataRepository; + } + + /// + /// 查询字段类型列表 + /// + /// 实体模型 + /// + public List SelectDictTypeList(SysDictType dictType) + { + return sysDictRepository.SelectDictTypeList(dictType); + } + + /// + /// 根据Id查询 + /// + /// + /// + public SysDictType SelectDictTypeById(long id) + { + return sysDictRepository.SelectDictTypeById(id); + } + + /// + /// 校验字典类型称是否唯一 + /// + /// 字典类型 + /// + public string CheckDictTypeUnique(SysDictType dictType) + { + SysDictType sysDictType = sysDictRepository.CheckDictTypeUnique(dictType.DictType); + if (sysDictType != null && sysDictType.DictId != dictType.DictId) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /// + /// 删除一个 + /// + /// + /// + public int DeleteDictTypeById(long id) + { + return sysDictRepository.DeleteDictTypeById(id); + } + + /// + /// 批量删除字典数据信息 + /// + /// + /// + public int DeleteDictTypeByIds(long[] dictIds) + { + foreach (var dictId in dictIds) + { + SysDictType dictType = SelectDictTypeById(dictId); + if (DictDataRepository.CountDictDataByType(dictType.DictType) > 0) + { + throw new CustomException($"{dictType.DictName}已分配,不能删除"); + } + } + int count = sysDictRepository.DeleteDictTypeByIds(dictIds); + //if (count > 0) + //{ + // DictUtils.clearDictCache(); + //} + return count; + } + + /// + /// 插入字典类型 + /// + /// + /// + public long InsertDictType(SysDictType sysDictType) + { + return sysDictRepository.InsertDictType(sysDictType); + } + + /// + /// 修改字典类型 + /// + /// + /// + public int UpdateDictType(SysDictType sysDictType) + { + SysDictType oldDict = sysDictRepository.SelectDictTypeById(sysDictType.DictId); + if (sysDictType.DictType != oldDict.DictType) + { + //同步修改 dict_data表里面的DictType值 + DictDataRepository.UpdateDictDataType(oldDict.DictType, sysDictType.DictType); + } + return sysDictRepository.UpdateDictType(sysDictType); + } + } +} diff --git a/ZR.Service/System/SysFileService.cs b/ZR.Service/System/SysFileService.cs new file mode 100644 index 0000000..df1182d --- /dev/null +++ b/ZR.Service/System/SysFileService.cs @@ -0,0 +1,15 @@ +using Infrastructure.Attribute; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + /// + /// 文件管理 + /// + [AppService(ServiceType = typeof(ISysFileService), ServiceLifetime = LifeTime.Transient)] + public class SysFileService: BaseService, ISysFileService + { + + } +} diff --git a/ZR.Service/System/SysLoginService.cs b/ZR.Service/System/SysLoginService.cs new file mode 100644 index 0000000..8758e1b --- /dev/null +++ b/ZR.Service/System/SysLoginService.cs @@ -0,0 +1,106 @@ +using Infrastructure; +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using ZR.Common; +using ZR.Model; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Repository.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + /// + /// 登录 + /// + [AppService(ServiceType = typeof(ISysLoginService), ServiceLifetime = LifeTime.Transient)] + public class SysLoginService: ISysLoginService + { + private SysLogininfoRepository SysLogininfoRepository; + + public SysLoginService(SysLogininfoRepository sysLogininfo) + { + SysLogininfoRepository = sysLogininfo; + } + + /// + /// 登录验证 + /// + /// + /// + public SysUser Login(LoginBodyDto loginBody, SysLogininfor logininfor) + { + //密码md5 + loginBody.Password = NETCore.Encrypt.EncryptProvider.Md5(loginBody.Password); + + SysUser user = SysLogininfoRepository.Login(loginBody); + logininfor.userName = loginBody.Username; + logininfor.status = "1"; + logininfor.loginTime = DateTime.Now; + + if (user == null || user.UserId <= 0) + { + logininfor.msg = "用户名或密码错误"; + AddLoginInfo(logininfor); + throw new CustomException(ResultCode.LOGIN_ERROR ,logininfor.msg); + } + if (user.Status == "1") + { + logininfor.msg = "该用户已禁用"; + AddLoginInfo(logininfor); + throw new CustomException(ResultCode.LOGIN_ERROR, logininfor.msg); + } + + logininfor.status = "0"; + logininfor.msg = "登录成功"; + AddLoginInfo(logininfor); + SysLogininfoRepository.UpdateLoginInfo(loginBody, user.UserId); + return user; + } + + + /// + /// 查询操作日志 + /// + /// + /// 分页 + /// + public List GetLoginLog(SysLogininfor logininfoDto, PagerInfo pager) + { + logininfoDto.BeginTime = DateTimeHelper.GetBeginTime(logininfoDto.BeginTime, -1); + logininfoDto.EndTime = DateTimeHelper.GetBeginTime(logininfoDto.EndTime, 1); + + var list = SysLogininfoRepository.GetLoginLog(logininfoDto, pager); + return list; + } + + /// + /// 记录登录日志 + /// + /// + /// + public void AddLoginInfo(SysLogininfor sysLogininfor) + { + SysLogininfoRepository.AddLoginInfo(sysLogininfor); + } + + /// + /// 清空登录日志 + /// + public void TruncateLogininfo() + { + SysLogininfoRepository.TruncateLogininfo(); + } + + /// + /// 删除登录日志 + /// + /// + /// + public int DeleteLogininforByIds(long[] ids) + { + return SysLogininfoRepository.DeleteLogininforByIds(ids); + } + } +} diff --git a/ZR.Service/System/SysMenuService.cs b/ZR.Service/System/SysMenuService.cs new file mode 100644 index 0000000..c4d702e --- /dev/null +++ b/ZR.Service/System/SysMenuService.cs @@ -0,0 +1,418 @@ +using Infrastructure.Attribute; +using System.Collections.Generic; +using System.Linq; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Model.System.Vo; +using ZR.Model.Vo; +using ZR.Model.Vo.System; +using ZR.Repository.System; +using ZR.Service.IService; + +namespace ZR.Service +{ + /// + /// 菜单 + /// + [AppService(ServiceType = typeof(ISysMenuService), ServiceLifetime = LifeTime.Transient)] + public class SysMenuService: BaseService, ISysMenuService + { + public SysMenuRepository MenuRepository; + public ISysRoleService SysRoleService; + + public SysMenuService( + SysMenuRepository menuRepository, + ISysRoleService sysRoleService) + { + MenuRepository = menuRepository; + SysRoleService = sysRoleService; + } + + /// + /// 根据用户查询系统菜单列表 + /// + /// + /// + /// + public List SelectMenuList(long userId) + { + return SelectMenuList(new SysMenu(), userId); + } + + /// + /// 获取所有菜单(菜单管理) + /// + /// + public List SelectMenuList(SysMenu menu, long userId) + { + List menuList; + if (SysUser.IsAdmin(userId)) + { + menuList = MenuRepository.SelectMenuList(menu); + } + else + { + menuList = MenuRepository.SelectMenuListByUserId(menu, userId); + } + return menuList; + } + + /// + /// 获取菜单详情 + /// + /// + /// + public SysMenu GetMenuByMenuId(int menuId) + { + return MenuRepository.SelectMenuById(menuId); + } + + /// + /// 添加菜单 + /// + /// + /// + public int AddMenu(SysMenu menu) + { + return MenuRepository.AddMenu(menu); + } + + /// + /// 编辑菜单 + /// + /// + /// + public int EditMenu(SysMenu menu) + { + menu.icon = string.IsNullOrEmpty(menu.icon) ? "" : menu.icon; + return MenuRepository.EditMenu(menu); + } + + /// + /// 删除菜单管理信息 + /// + /// + /// + public int DeleteMenuById(int menuId) + { + return MenuRepository.DeleteMenuById(menuId); + } + + /// + /// 校验菜单名称是否唯一 + /// + /// + /// + public string CheckMenuNameUnique(SysMenu menu) + { + long menuId = menu.menuId == 0 ? -1 : menu.menuId; + SysMenu info = MenuRepository.CheckMenuNameUnique(menu); + + //if (info != null && menuId != info.menuId && menu.menuName.Equals(info.menuName)) + //{ + // return UserConstants.NOT_UNIQUE; + //} + if (info != null && info.menuId != menu.menuId) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /// + /// 菜单排序 + /// + /// + /// + public int ChangeSortMenu(MenuDto menuDto) + { + return MenuRepository.ChangeSortMenu(menuDto); + } + + /// + /// 是否存在菜单子节点 + /// + /// + /// + public bool HasChildByMenuId(long menuId) + { + return MenuRepository.HasChildByMenuId(menuId) > 0; + } + + /// + /// 获取左边导航栏菜单树 + /// + /// + /// + public List SelectMenuTreeByUserId(long userId) + { + List menus; + if (SysRoleService.IsAdmin(userId)) + { + menus = MenuRepository.SelectMenuTreeAll(); + } + else + { + List roleIds = SysRoleService.SelectUserRoles(userId); + menus = MenuRepository.SelectMenuTreeByRoleIds(roleIds); + } + return GetChildPerms(menus, 0); + } + + /// + /// 查询菜单权限 + /// + /// + /// + public List SelectMenuPermsListByUserId(long userId) + { + return MenuRepository.SelectMenuPermsByUserId(userId); + } + + /// + /// 查询精确到按钮的操作权限 + /// + /// + /// + public List SelectMenuPermsByUserId(long userId) + { + var menuList = SelectMenuPermsListByUserId(userId).Where(f => !string.IsNullOrEmpty(f.perms)); + + return menuList.Select(x => x.perms).Distinct().ToList(); + } + + #region RoleMenu + + /// + /// 查询菜单使用数量 + /// + /// + /// + public bool CheckMenuExistRole(long menuId) + { + return MenuRepository.CheckMenuExistRole(menuId) > 0; + } + + #endregion + + #region 方法 + + /// + /// 根据父节点的ID获取所有子节点 + /// + /// 分类表 + /// 传入的父节点ID + /// + public List GetChildPerms(List list, int parentId) + { + List returnList = new List(); + var data = list.FindAll(f => f.parentId == parentId); + + foreach (var item in data) + { + RecursionFn(list, item); + + returnList.Add(item); + } + return returnList; + } + + /// + /// 递归列表 + /// + /// + /// + private void RecursionFn(List list, SysMenu t) + { + //得到子节点列表 + List childList = GetChildList(list, t); + t.children = childList; + foreach (var item in childList) + { + if (GetChildList(list, item).Count() > 0) + { + RecursionFn(list, item); + } + } + } + + /// + /// 递归获取子菜单 + /// + /// 所有菜单 + /// + /// + private List GetChildList(List list, SysMenu sysMenu) + { + return list.Where(p => p.parentId == sysMenu.menuId).ToList(); + } + + /// + /// 获取路由侧边栏,动态生成 + /// + /// + /// + public List BuildMenus(List menus) + { + List routers = new List(); + + foreach (var menu in menus) + { + RouterVo router = new RouterVo + { + Hidden = "1".Equals(menu.visible), + Name = GetRouteName(menu), + Path = GetRoutePath(menu), + Component = GetComponent(menu), + Meta = new Meta(menu.menuName, menu.icon, "1".Equals(menu.isCache)) + }; + + List cMenus = menu.children; + //是目录并且有子菜单 + if (cMenus != null && cMenus.Count > 0 && (UserConstants.TYPE_DIR.Equals(menu.menuType))) + { + router.AlwaysShow = true; + router.Redirect = "noRedirect"; + router.Children = BuildMenus(cMenus); + } + else if (IsMeunFrame(menu)) + { + List childrenList = new List(); + RouterVo children = new RouterVo + { + Path = menu.path, + Component = menu.component, + Name = string.IsNullOrEmpty(menu.path) ? "" : menu.path.ToLower(), + Meta = new Meta(menu.menuName, menu.icon, "1".Equals(menu.isCache)) + }; + childrenList.Add(children); + router.Children = childrenList; + } + routers.Add(router); + } + + return routers; + } + + /// + /// 构建前端所需要下拉树结构 + /// + /// 菜单列表 + /// 下拉树结构列表 + public List BuildMenuTree(List menus) + { + List returnList = new List(); + List tempList = menus.Select(f => f.menuId).ToList(); + + foreach (var menu in menus) + { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.Contains(menu.parentId)) + { + RecursionFn(menus, menu); + returnList.Add(menu); + } + } + if (!returnList.Any()) + { + returnList = menus; + } + return returnList; + } + + /// + /// 构建前端所需要下拉树结构 + /// + /// + /// + public List BuildMenuTreeSelect(List menus) + { + List menuTrees = BuildMenuTree(menus); + List treeMenuVos = new List(); + foreach (var item in menuTrees) + { + treeMenuVos.Add(new TreeSelectVo(item)); + } + return treeMenuVos; + } + + /// + /// 获取路由名称 + /// + /// 菜单信息 + /// 路由名称 + public string GetRouteName(SysMenu menu) + { + string routerName = menu.path.ToLower(); + // 非外链并且是一级目录(类型为目录) + if (IsMeunFrame(menu)) + { + routerName = string.Empty; + } + return routerName; + } + + /// + /// 获取路由路径 + /// + /// 菜单信息 + /// 路由地址 + public string GetRoutePath(SysMenu menu) + { + string routePath = menu.path; + // 非外链并且是一级目录(类型为目录) + if (0 == menu.parentId && UserConstants.TYPE_DIR.Equals(menu.menuType) && UserConstants.NO_FRAME.Equals(menu.isFrame)) + { + routePath = "/" + menu.path; + } + else if (IsMeunFrame(menu))// 非外链并且是一级目录(类型为菜单) + { + routePath = "/"; + } + return routePath; + } + + /// + /// 获取组件名称 + /// + /// + /// + public string GetComponent(SysMenu menu) + { + string component = UserConstants.LAYOUT; + if (!string.IsNullOrEmpty(menu.component) && !IsMeunFrame(menu)) + { + component = menu.component; + } + else if (string.IsNullOrEmpty(menu.component) && IsParentView(menu)) + { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /// + /// 是否为菜单内部跳转 + /// + /// 菜单信息 + /// + public bool IsMeunFrame(SysMenu menu) + { + return menu.parentId == 0 && UserConstants.TYPE_MENU.Equals(menu.menuType) && menu.isFrame.Equals(UserConstants.NO_FRAME); + } + + /// + /// + /// 是否为parent_view组件 + /// + /// 菜单信息 + /// + public bool IsParentView(SysMenu menu) + { + return menu.parentId != 0 && UserConstants.TYPE_DIR.Equals(menu.menuType); + } + + #endregion + + } +} diff --git a/ZR.Service/System/SysOperLogService.cs b/ZR.Service/System/SysOperLogService.cs new file mode 100644 index 0000000..668accd --- /dev/null +++ b/ZR.Service/System/SysOperLogService.cs @@ -0,0 +1,78 @@ +using Infrastructure.Attribute; +using System.Collections.Generic; +using ZR.Common; +using ZR.Model; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Repository.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + /// + /// 操作日志 + /// + [AppService(ServiceType = typeof(ISysOperLogService), ServiceLifetime = LifeTime.Transient)] + public class SysOperLogService: BaseService, ISysOperLogService + { + public SysOperLogRepository sysOperLogRepository; + + public SysOperLogService(SysOperLogRepository sysOperLog) + { + sysOperLogRepository = sysOperLog; + } + + /// + /// 新增操作日志操作 + /// + /// 日志对象 + public void InsertOperlog(SysOperLog operLog) + { + sysOperLogRepository.AddSysOperLog(operLog); + } + + /// + /// 查询系统操作日志集合 + /// + /// 操作日志对象 + /// + /// 操作日志集合 + public List SelectOperLogList(SysOperLogDto operLog, PagerInfo pager) + { + operLog.BeginTime = DateTimeHelper.GetBeginTime(operLog.BeginTime, -1); + operLog.EndTime = DateTimeHelper.GetBeginTime(operLog.EndTime, 1); + + var list = sysOperLogRepository.GetSysOperLog(operLog, pager); + + return list; + } + + /// + /// 清空操作日志 + /// + public void CleanOperLog() + { + sysOperLogRepository.ClearOperLog(); + } + + /// + /// 批量删除系统操作日志 + /// + /// 需要删除的操作日志ID + /// 结果 + public int DeleteOperLogByIds(long[] operIds) + { + return sysOperLogRepository.DeleteOperLogByIds(operIds); + } + + /// + /// 查询操作日志详细 + /// + /// 操作ID + /// 操作日志对象 + public SysOperLog SelectOperLogById(long operId) + { + return sysOperLogRepository.SelectOperLogById(operId); + } + } +} diff --git a/ZR.Service/System/SysPermissionService.cs b/ZR.Service/System/SysPermissionService.cs new file mode 100644 index 0000000..e946098 --- /dev/null +++ b/ZR.Service/System/SysPermissionService.cs @@ -0,0 +1,65 @@ +using Infrastructure; +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + [AppService(ServiceType = typeof(ISysPermissionService), ServiceLifetime = LifeTime.Transient)] + public class SysPermissionService : ISysPermissionService + { + private readonly ISysRoleService SysRoleService; + private readonly ISysMenuService SysMenuService; + + public SysPermissionService( + ISysRoleService sysRoleService, + ISysMenuService sysMenuService) + { + SysRoleService = sysRoleService; + SysMenuService = sysMenuService; + } + + /// + /// 获取角色数据权限 + /// + /// 用户信息 + /// 角色权限信息 + public List GetRolePermission(SysUser user) + { + List roles = new List(); + // 管理员拥有所有权限 + if (user.IsAdmin()) + { + roles.Add("admin"); + } + else + { + roles.AddRange(SysRoleService.SelectUserRoleKeys(user.UserId)); + } + return roles; + } + + /// + /// 获取菜单数据权限 + /// + /// 用户信息 + /// 菜单权限信息 + public List GetMenuPermission(SysUser user) + { + List perms = new List(); + // 管理员拥有所有权限 + if (user.IsAdmin()) + { + perms.Add(GlobalConstant.AdminPerm); + } + else + { + perms.AddRange(SysMenuService.SelectMenuPermsByUserId(user.UserId)); + } + return perms; + } + } +} diff --git a/ZR.Service/System/SysPostService.cs b/ZR.Service/System/SysPostService.cs new file mode 100644 index 0000000..56534c0 --- /dev/null +++ b/ZR.Service/System/SysPostService.cs @@ -0,0 +1,46 @@ +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + /// + /// 岗位管理 + /// + [AppService(ServiceType = typeof(ISysPostService), ServiceLifetime = LifeTime.Transient)] + public class SysPostService : BaseService, ISysPostService + { + /// + /// 校验岗位编码是否唯一 + /// + /// + /// + public string CheckPostCodeUnique(SysPost post) + { + SysPost info = GetFirst(it => it.PostCode.Equals(post.PostCode)); + if (info != null && info.PostId != post.PostId) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /// + /// 校验岗位名称是否唯一 + /// + /// + /// + public string CheckPostNameUnique(SysPost post) + { + SysPost info = GetFirst(it => it.PostName.Equals(post.PostName)); + if (info != null && info.PostId != post.PostId) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + } +} diff --git a/ZR.Service/System/SysRoleService.cs b/ZR.Service/System/SysRoleService.cs new file mode 100644 index 0000000..8591f92 --- /dev/null +++ b/ZR.Service/System/SysRoleService.cs @@ -0,0 +1,255 @@ +using Infrastructure; +using Infrastructure.Attribute; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ZR.Model.System; +using ZR.Repository.System; +using ZR.Service.IService; +using ZR.Service.System; + +namespace ZR.Service +{ + /// + /// 角色 + /// + [AppService(ServiceType = typeof(ISysRoleService), ServiceLifetime = LifeTime.Transient)] + public class SysRoleService: BaseService, ISysRoleService + { + private SysRoleRepository SysRoleRepository; + private ISysUserRoleService SysUserRoleService; + + public SysRoleService( + SysRoleRepository sysRoleRepository, + ISysUserRoleService sysUserRoleService) + { + SysRoleRepository = sysRoleRepository; + SysUserRoleService = sysUserRoleService; + } + + /// + /// 根据条件分页查询角色数据 + /// + /// 角色信息 + /// 角色数据集合信息 + public List SelectRoleList(SysRole role) + { + return SysRoleRepository.SelectRoleList(role); + } + + /// + /// 查询所有角色 + /// + /// + /// + public List SelectRoleAll() + { + return SysRoleRepository.SelectRoleList(new SysRole()); + } + + /// + /// 根据用户查询 + /// + /// + /// + public List SelectRolePermissionByUserId(long userId) + { + return SysRoleRepository.SelectRolePermissionByUserId(userId); + } + + /// + /// 通过角色ID查询角色 + /// + /// 角色ID + /// 角色对象信息 + public SysRole SelectRoleById(long roleId) + { + return SysRoleRepository.SelectRoleById(roleId); + } + + /// + /// 批量删除角色信息 + /// + /// 需要删除的角色ID + /// + public int DeleteRoleByRoleId(long[] roleIds) + { + foreach (var item in roleIds) + { + CheckRoleAllowed(new SysRole(item)); + SysRole role = SelectRoleById(item); + if (SysUserRoleService.CountUserRoleByRoleId(item) > 0) + { + throw new CustomException($"{role.RoleName}已分配,不能删除"); + } + } + return SysRoleRepository.DeleteRoleByRoleIds(roleIds); + } + + /// + /// 更改角色权限状态 + /// + /// + /// + public int UpdateRoleStatus(SysRole roleDto) + { + return SysRoleRepository.UpdateRoleStatus(roleDto); + } + + /// + /// 校验角色权限是否唯一 + /// + /// 角色信息 + /// + public string CheckRoleKeyUnique(SysRole sysRole) + { + long roleId = 0 == sysRole.RoleId ? -1L : sysRole.RoleId; + SysRole info = SysRoleRepository.CheckRoleKeyUnique(sysRole.RoleKey); + if (info != null && info.RoleId != roleId) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /// + /// 校验角色是否允许操作 + /// + /// + public void CheckRoleAllowed(SysRole role) + { + if (IsAdmin(role.RoleId)) + { + throw new CustomException("不允许操作超级管理员角色"); + } + } + + /// + /// 新增保存角色信息 + /// + /// 角色信息 + /// + public long InsertRole(SysRole sysRole) + { + return SysRoleRepository.InsertRole(sysRole); + //return InsertRoleMenu(sysRole); + } + + /// + /// 通过角色ID删除角色和菜单关联 + /// + /// 角色ID + /// + public int DeleteRoleMenuByRoleId(long roleId) + { + return SysRoleRepository.DeleteRoleMenuByRoleId(roleId); + } + + #region Service + + + /// + /// 新增角色菜单信息 + /// + /// + /// + public int InsertRoleMenu(SysRole sysRoleDto) + { + int rows = 1; + // 新增用户与角色管理 + List sysRoleMenus = new List(); + foreach (var item in sysRoleDto.MenuIds) + { + SysRoleMenu rm = new SysRoleMenu(); + rm.Menu_id = item; + rm.Role_id = sysRoleDto.RoleId; + rm.Create_by = sysRoleDto.Create_by; + rm.Create_time = DateTime.Now; + sysRoleMenus.Add(rm); + } + //添加角色菜单 + if (sysRoleMenus.Count > 0) + { + rows = SysRoleRepository.AddRoleMenu(sysRoleMenus); + } + + return rows; + } + + /// + /// 判断是否是管理员 + /// + /// + /// + public bool IsAdmin(long userid) + { + List roles = SelectUserRoleKeys(userid); + + return ((IList)roles).Contains("admin"); + } + + /// + /// 获取角色菜单id集合 + /// + /// + /// + public List SelectUserRoleMenus(long roleId) + { + var list = SysRoleRepository.SelectRoleMenuByRoleId(roleId); + + return list.Select(x => x.Menu_id).ToList(); + } + + /// + /// 获取用户角色列表 + /// + /// + /// + public List SelectUserRoleListByUserId(long userId) + { + return SysRoleRepository.SelectUserRoleListByUserId(userId); + } + + /// + /// 获取用户权限集合 + /// + /// + /// + public List SelectUserRoles(long userId) + { + var list = SelectUserRoleListByUserId(userId).Where(f => f.Status == "0"); + + return list.Select(x => x.RoleId).ToList(); + } + + /// + /// 获取用户权限字符串集合 + /// + /// + /// + public List SelectUserRoleKeys(long userId) + { + var list = SelectUserRoleListByUserId(userId); + return list.Select(x => x.RoleKey).ToList(); + } + + public List SelectUserRoleNames(long userId) + { + var list = SelectUserRoleListByUserId(userId); + return list.Select(x => x.RoleName).ToList(); + } + #endregion + + /// + /// 修改保存角色信息 + /// + /// 角色信息 + /// + public int UpdateRole(SysRole sysRole) + { + return SysRoleRepository.UpdateSysRole(sysRole); + } + } +} diff --git a/ZR.Service/System/SysTasksLogService.cs b/ZR.Service/System/SysTasksLogService.cs new file mode 100644 index 0000000..4d83262 --- /dev/null +++ b/ZR.Service/System/SysTasksLogService.cs @@ -0,0 +1,34 @@ +using Infrastructure.Attribute; +using System; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + [AppService(ServiceLifetime = LifeTime.Transient, ServiceType = typeof(ISysTasksLogService))] + public class SysTasksLogService : BaseService, ISysTasksLogService + { + private ISysTasksQzService _tasksQzService; + public SysTasksLogService(ISysTasksQzService tasksQzService) + { + _tasksQzService = tasksQzService; + } + + public SysTasksLog AddTaskLog(string jobId, SysTasksLog logModel) + { + //获取任务信息 + var model = _tasksQzService.GetId(jobId); + + if (model != null) + { + logModel.JobId = jobId; + logModel.JobName = model.Name; + logModel.JobGroup = model.JobGroup; + logModel.CreateTime = DateTime.Now; + } + + Add(logModel); + return logModel; + } + } +} diff --git a/ZR.Service/System/SysTasksQzService.cs b/ZR.Service/System/SysTasksQzService.cs new file mode 100644 index 0000000..1ecbc13 --- /dev/null +++ b/ZR.Service/System/SysTasksQzService.cs @@ -0,0 +1,12 @@ +using Infrastructure.Attribute; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + [AppService(ServiceType = typeof(ISysTasksQzService), ServiceLifetime = LifeTime.Transient)] + public class SysTasksQzService : BaseService, ISysTasksQzService + { + + } +} diff --git a/ZR.Service/System/SysUserPostService.cs b/ZR.Service/System/SysUserPostService.cs new file mode 100644 index 0000000..cf24642 --- /dev/null +++ b/ZR.Service/System/SysUserPostService.cs @@ -0,0 +1,63 @@ +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ZR.Model.System; +using ZR.Repository.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + /// + /// 用户岗位 + /// + [AppService(ServiceType = typeof(ISysUserPostService), ServiceLifetime = LifeTime.Transient)] + public class SysUserPostService : BaseService, ISysUserPostService + { + public SysUserPostRepository UserPostRepository; + + public SysUserPostService(SysUserPostRepository userPostRepository) + { + UserPostRepository = userPostRepository; + } + + /// + /// 新增用户岗位信息 + /// + /// + public void InsertUserPost(SysUser user) + { + // 新增用户与岗位管理 + List list = new List(); + foreach (var item in user.PostIds) + { + list.Add(new SysUserPost() { PostId = item, UserId = user.UserId }); + } + Add(list); + } + + + /// + /// 查询用户岗位集合 + /// + /// + /// + public List GetUserPostsByUserId(long userId) + { + var list = GetWhere(f => f.UserId == userId); + return list.Select(x => x.PostId).ToList(); + } + + /// + /// 获取用户岗位 + /// + /// + /// + public string GetPostsStrByUserId(long userId) + { + var list = UserPostRepository.SelectPostsByUserId(userId); + return string.Join(',', list.Select(x => x.PostName)); + } + } +} diff --git a/ZR.Service/System/SysUserRoleService.cs b/ZR.Service/System/SysUserRoleService.cs new file mode 100644 index 0000000..a42cbf4 --- /dev/null +++ b/ZR.Service/System/SysUserRoleService.cs @@ -0,0 +1,107 @@ +using Infrastructure.Attribute; +using System; +using System.Collections.Generic; +using System.Text; +using ZR.Model.Dto.System; +using ZR.Model.System; +using ZR.Repository.System; +using ZR.Service.IService; + +namespace ZR.Service.System +{ + [AppService(ServiceType = typeof(ISysUserRoleService),ServiceLifetime = LifeTime.Transient)] + public class SysUserRoleService: ISysUserRoleService + { + public SysUserRoleRepository SysUserRoleRepository; + + public SysUserRoleService(SysUserRoleRepository sysUserRoleRepository) + { + SysUserRoleRepository = sysUserRoleRepository; + } + + + /// + /// 通过角色ID查询角色使用数量 + /// + /// + /// + public int CountUserRoleByRoleId(long roleId) + { + return SysUserRoleRepository.CountUserRoleByRoleId(roleId); + } + + /// + /// 删除用户角色 + /// + /// + /// + public int DeleteUserRoleByUserId(int userId) + { + return SysUserRoleRepository.DeleteUserRoleByUserId(userId); + } + + /// + /// 批量删除角色对应用户 + /// + /// + /// + /// + public int DeleteRoleUserByUserIds(long roleId, List userIds) + { + return SysUserRoleRepository.DeleteRoleUserByUserIds(roleId, userIds); + } + + /// + /// 添加用户角色 + /// + /// + /// + public int AddUserRole(List sysUsers) + { + return SysUserRoleRepository.AddUserRole(sysUsers); + } + + /// + /// 获取用户数据根据角色id + /// + /// + /// + public List GetSysUsersByRoleId(long roleId) + { + return SysUserRoleRepository.GetSysUsersByRoleId(roleId); + } + + /// + /// 新增用户角色信息 + /// + /// + /// + public int InsertUserRole(SysUser user) + { + List userRoles = new List(); + foreach (var item in user.RoleIds) + { + userRoles.Add(new SysUserRole() { RoleId = item, UserId = user.UserId }); + } + + return userRoles.Count > 0 ? AddUserRole(userRoles) : 0; + } + + /// + /// 新增加角色用户 + /// + /// 角色id + /// 用户ids + /// + public int InsertRoleUser(RoleUsersCreateDto roleUsersCreateDto) + { + List userRoles = new List(); + foreach (var item in roleUsersCreateDto.UserIds) + { + userRoles.Add(new SysUserRole() { RoleId = roleUsersCreateDto.RoleId, UserId = item }); + } + + return userRoles.Count > 0 ? AddUserRole(userRoles) : 0; + } + } +} diff --git a/ZR.Service/System/SysUserService.cs b/ZR.Service/System/SysUserService.cs new file mode 100644 index 0000000..c3a0af0 --- /dev/null +++ b/ZR.Service/System/SysUserService.cs @@ -0,0 +1,151 @@ +using Infrastructure.Attribute; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ZR.Model; +using ZR.Model.System; +using ZR.Repository.System; +using ZR.Service.IService; + +namespace ZR.Service +{ + [AppService(ServiceType = typeof(ISysUserService), ServiceLifetime = LifeTime.Transient)] + public class SysUserService : BaseService, ISysUserService + { + private readonly SysUserRepository UserRepository; + private readonly ISysRoleService RoleService; + private readonly ISysUserRoleService UserRoleService; + private readonly ISysUserPostService UserPostService; + + public SysUserService( + SysUserRepository userRepository, + ISysRoleService sysRoleService, + ISysUserRoleService userRoleService, + ISysUserPostService userPostService) + { + UserRepository = userRepository; + RoleService = sysRoleService; + UserRoleService = userRoleService; + UserPostService = userPostService; + } + + /// + /// 根据条件分页查询用户列表 + /// + /// + public List SelectUserList(SysUser user, PagerInfo pager) + { + var list = UserRepository.SelectUserList(user, pager); + + return list; + } + + /// + /// 通过用户ID查询用户 + /// + /// + /// + public SysUser SelectUserById(long userId) + { + return UserRepository.SelectUserById(userId); + } + + /// + /// 校验用户名称是否唯一 + /// + /// + /// + public string CheckUserNameUnique(string userName) + { + int count = UserRepository.CheckUserNameUnique(userName); + if (count > 0) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /// + /// 新增保存用户信息 + /// + /// + /// + public long InsertUser(SysUser sysUser) + { + long userId = UserRepository.AddUser(sysUser); + sysUser.UserId = userId; + //新增用户角色信息 + UserRoleService.InsertUserRole(sysUser); + //新增用户岗位信息 + UserPostService.InsertUserPost(sysUser); + return userId; + } + + /// + /// 修改用户信息 + /// + /// + /// + public int UpdateUser(SysUser user) + { + var roleIds = RoleService.SelectUserRoles(user.UserId); + var diffArr = roleIds.Where(c => !((IList)user.RoleIds).Contains(c)).ToArray(); + var diffArr2 = user.RoleIds.Where(c => !((IList)roleIds).Contains(c)).ToArray(); + + if (diffArr.Length > 0 || diffArr2.Length > 0) + { + //删除用户与角色关联 + UserRoleService.DeleteUserRoleByUserId((int)user.UserId); + //新增用户与角色关联 + UserRoleService.InsertUserRole(user); + } + // 删除用户与岗位关联 + UserPostService.Delete(it => it.UserId == user.UserId); + // 新增用户与岗位管理 + UserPostService.InsertUserPost(user); + return UserRepository.UpdateUser(user); + } + + public int ChangeUser(SysUser user) + { + return UserRepository.UpdateUser(user); + } + + /// + /// 重置密码 + /// + /// + /// + /// + public int ResetPwd(long userid, string password) + { + return UserRepository.ResetPwd(userid, password); + } + + public int ChangeUserStatus(SysUser user) + { + return UserRepository.ChangeUserStatus(user); + } + + /// + /// 删除用户 + /// + /// + /// + public int DeleteUser(long userid) + { + return UserRepository.DeleteUser(userid); + } + + /// + /// 修改用户头像 + /// + /// + /// + public int UpdatePhoto(SysUser user) + { + return UserRepository.UpdatePhoto(user); + } + } +} diff --git a/ZR.Service/ZR.Service.csproj b/ZR.Service/ZR.Service.csproj new file mode 100644 index 0000000..6f3c4d0 --- /dev/null +++ b/ZR.Service/ZR.Service.csproj @@ -0,0 +1,12 @@ + + + + net5.0 + + + + + + + + diff --git a/ZR.Tasks/ITaskSchedulerServer.cs b/ZR.Tasks/ITaskSchedulerServer.cs new file mode 100644 index 0000000..f9e6b73 --- /dev/null +++ b/ZR.Tasks/ITaskSchedulerServer.cs @@ -0,0 +1,28 @@ +using Infrastructure.Model; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using ZR.Model.System; + +namespace ZR.Tasks +{ + public interface ITaskSchedulerServer + { + Task StartTaskScheduleAsync(); + + Task StopTaskScheduleAsync(); + + Task AddTaskScheduleAsync(SysTasksQz tasksQz); + + Task PauseTaskScheduleAsync(SysTasksQz tasksQz); + + Task ResumeTaskScheduleAsync(SysTasksQz tasksQz); + + Task DeleteTaskScheduleAsync(SysTasksQz tasksQz); + + Task RunTaskScheduleAsync(SysTasksQz tasksQz); + + Task UpdateTaskScheduleAsync(SysTasksQz tasksQz, string groupName); + } +} diff --git a/ZR.Tasks/JobFactory.cs b/ZR.Tasks/JobFactory.cs new file mode 100644 index 0000000..c7d70fa --- /dev/null +++ b/ZR.Tasks/JobFactory.cs @@ -0,0 +1,51 @@ +using Microsoft.Extensions.DependencyInjection; +using Quartz; +using Quartz.Spi; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZR.Tasks +{ + public class JobFactory : IJobFactory + { + /// + /// 注入反射获取依赖对象 + /// + private readonly IServiceProvider _serviceProvider; + public JobFactory(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + /// + /// 实现接口Job + /// + /// + /// + /// + public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) + { + try + { + var serviceScope = _serviceProvider.CreateScope(); + var job = serviceScope.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob; + return job; + + } + catch (Exception) + { + throw; + } + } + + public void ReturnJob(IJob job) + { + var disposable = job as IDisposable; + if (disposable != null) + { + disposable.Dispose(); + } + + } + } +} diff --git a/ZR.Tasks/TaskScheduler/JobBase.cs b/ZR.Tasks/TaskScheduler/JobBase.cs new file mode 100644 index 0000000..3d5b9cd --- /dev/null +++ b/ZR.Tasks/TaskScheduler/JobBase.cs @@ -0,0 +1,87 @@ +using Infrastructure; +using NLog; +using Quartz; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; +using ZR.Model.System; +using ZR.Service.IService; + +namespace ZR.Tasks +{ + public class JobBase + { + /// + /// 日志接口 + /// + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + /// + /// 执行指定任务 + /// + /// 作业上下文 + /// 业务逻辑方法 + public async Task> ExecuteJob(IJobExecutionContext context, Func job) + { + string logMsg = ""; + double elapsed = 0; + int status = 0; + try + { + //var s = context.Trigger.Key.Name; + //记录Job时间 + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + //执行任务 + await job(); + stopwatch.Stop(); + elapsed = stopwatch.Elapsed.TotalMilliseconds; + logMsg = "Succeed"; + } + catch (Exception ex) + { + JobExecutionException e2 = new JobExecutionException(ex); + //true 是立即重新执行任务 + e2.RefireImmediately = true; + status = 1; + logMsg = $"Fail,Exception:{ex.Message}"; + } + Dictionary dic = new Dictionary(); + dic.Add("elapsed", elapsed); + dic.Add("status", status); + dic.Add("content", logMsg); + + RecordTaskLog(context, dic); + return dic; + } + + /// + /// 记录到日志 + /// + /// + /// + protected void RecordTaskLog(IJobExecutionContext context, Dictionary executeLog) + { + var tasksLogService = (ISysTasksLogService)App.GetRequiredService(typeof(ISysTasksLogService)); + + // 可以直接获取 JobDetail 的值 + IJobDetail job = context.JobDetail; + //var param = context.MergedJobDataMap; + + // 也可以通过数据库配置,获取传递过来的参数 + //JobDataMap data = context.JobDetail.JobDataMap; + //int jobId = data.GetInt("JobParam"); + + var logModel = new SysTasksLog(); + + logModel.InvokeTarget = job.JobType.FullName; + logModel.Elapsed = (double)executeLog.GetValueOrDefault("elapsed", "0"); + logModel.JobMessage = executeLog.GetValueOrDefault("content").ToString(); + logModel.Status = executeLog.GetValueOrDefault("status", "0").ToString(); + logModel = tasksLogService.AddTaskLog(job.Key.Name, logModel); + + logger.Info($"执行任务【{job.Key.Name}|{logModel.JobName}】结果={logModel.JobMessage}"); + } + } +} diff --git a/ZR.Tasks/TaskScheduler/Job_SyncTest.cs b/ZR.Tasks/TaskScheduler/Job_SyncTest.cs new file mode 100644 index 0000000..e8f1b1c --- /dev/null +++ b/ZR.Tasks/TaskScheduler/Job_SyncTest.cs @@ -0,0 +1,27 @@ +using Quartz; +using System.Threading.Tasks; + +namespace ZR.Tasks +{ + /// + /// 定时任务测试 + /// + //[AppService(ServiceLifetime = LifeTime.Scoped)] + public class Job_SyncTest : JobBase, IJob + { + //private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + public async Task Execute(IJobExecutionContext context) + { + await ExecuteJob(context, async () => await Run()); + } + + public async Task Run() + { + await Task.Delay(1); + //TODO 业务逻辑 + + //throw new System.Exception("error"); + } + } +} diff --git a/ZR.Tasks/TaskSchedulerServer.cs b/ZR.Tasks/TaskSchedulerServer.cs new file mode 100644 index 0000000..d261ca9 --- /dev/null +++ b/ZR.Tasks/TaskSchedulerServer.cs @@ -0,0 +1,345 @@ +using Infrastructure.Model; +using NLog; +using Quartz; +using Quartz.Impl; +using Quartz.Impl.Triggers; +using Quartz.Spi; +using System; +using System.Collections.Specialized; +using System.Reflection; +using System.Threading.Tasks; +using ZR.Model.System; + +namespace ZR.Tasks +{ + /// + /// 计划任务中心 + /// + //[AppService] + public class TaskSchedulerServer : ITaskSchedulerServer + { + private Task _scheduler; + private readonly IJobFactory _jobFactory; + /// + /// 日志接口 + /// + private static Logger logger = LogManager.GetCurrentClassLogger(); + + public TaskSchedulerServer(IJobFactory jobFactory) + { + _scheduler = GetTaskSchedulerAsync(); + _jobFactory = jobFactory; + } + + /// + /// 开启计划任务 + /// 参考文章:https://www.quartz-scheduler.net/documentation/quartz-3.x/configuration/reference.html#datasources-ado-net-jobstores + /// + /// + private Task GetTaskSchedulerAsync() + { + if (_scheduler != null) + { + return _scheduler; + } + + NameValueCollection collection = new NameValueCollection + { + { "quartz.serializer.type","binary" }, + //quartz参数 + //{ "quartz.scheduler.instanceId", "AUTO" }, + //{ "quartz.serializer.type","json" }, + ////下面为指定quartz持久化数据库的配置 + //{ "quartz.jobStore.type","Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" }, + //{ "quartz.jobStore.tablePrefix","QRTZ_"}, + //{ "quartz.jobStore.driverDelegateType", "Quartz.Impl.AdoJobStore.MySQLDelegate, Quartz"}, + //{ "quartz.jobStore.useProperties", "true"}, + //{ "quartz.jobStore.dataSource", "myDS" }, + //{ "quartz.dataSource.myDS.connectionString", @"server=118.24.27.111;port=3306;database=zrryAdmin;uid=zrry;pwd=@zrry1993^Hz;Charset=utf8;"}, + //{ "quartz.dataSource.myDS.provider", "MySql" }, + }; + + StdSchedulerFactory factory = new StdSchedulerFactory(collection); + + return _scheduler = factory.GetScheduler(); + } + + public async Task StartTaskScheduleAsync() + { + try + { + _scheduler.Result.JobFactory = _jobFactory; + if (_scheduler.Result.IsStarted) + { + return ApiResult.Error(500, $"计划任务已经开启"); + } + + //等待任务运行完成 + await _scheduler.Result.Start(); + return ApiResult.Success("计划任务开启成功"); + } + catch (Exception) + { + throw; + } + } + + /// + /// 停止计划任务 + /// + /// + public async Task StopTaskScheduleAsync() + { + try + { + if (_scheduler.Result.IsShutdown) + { + return ApiResult.Error(500, $"计划任务已经停止"); + } + + await _scheduler.Result.Shutdown(); + return ApiResult.Success("计划任务已经停止"); + } + catch (Exception) + { + throw; + } + } + + /// + /// 添加一个计划任务 + /// + /// + /// + public async Task AddTaskScheduleAsync(SysTasksQz tasksQz) + { + try + { + JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup); + if (await _scheduler.Result.CheckExists(jobKey)) + { + return ApiResult.Error(500, $"该计划任务已经在执行:【{tasksQz.Name}】,请勿重复添加!"); + } + #region 设置开始时间和结束时间 + + tasksQz.BeginTime = tasksQz.BeginTime == null ? DateTime.Now : tasksQz.BeginTime; + tasksQz.EndTime = tasksQz.EndTime == null ? DateTime.MaxValue.AddDays(-1) : tasksQz.EndTime; + + DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(tasksQz.BeginTime, 1);//设置开始时间 + DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(tasksQz.EndTime, 1);//设置暂停时间 + + #endregion + + #region 通过反射获取程序集类型和类 + + Assembly assembly = Assembly.Load(new AssemblyName(tasksQz.AssemblyName)); + Type jobType = assembly.GetType(tasksQz.AssemblyName + "." + tasksQz.ClassName); + + #endregion + //2、开启调度器。判断任务调度是否开启 + if (!_scheduler.Result.IsStarted) + { + await StartTaskScheduleAsync(); + } + + //3、创建任务。传入反射出来的执行程序集 + IJobDetail job = new JobDetailImpl(tasksQz.ID, tasksQz.JobGroup, jobType); + job.JobDataMap.Add("JobParam", tasksQz.JobParams); + ITrigger trigger; + + //4、创建一个触发器 + if (tasksQz.Cron != null && CronExpression.IsValidExpression(tasksQz.Cron)) + { + trigger = CreateCronTrigger(tasksQz); + //解决Quartz启动后第一次会立即执行问题解决办法 + ((CronTriggerImpl)trigger).MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing; + + // 5、将触发器和任务器绑定到调度器中 + await _scheduler.Result.ScheduleJob(job, trigger); + } + + //任务没有启动、暂停任务 + if (!tasksQz.IsStart) + { + _scheduler.Result.PauseJob(jobKey).Wait(); + } + return ApiResult.Success($"启动计划任务:【{tasksQz.Name}】成功!"); + } + catch (Exception ex) + { + logger.Error(ex, $"启动计划任务失败,分组:{tasksQz.JobGroup},作业:【{tasksQz.Name}】,error:{ex.Message}"); + return ApiResult.Error(500, $"启动计划任务:【{tasksQz.Name}】失败!"); + } + } + + /// + /// 暂停指定的计划任务 + /// + /// + /// + public async Task PauseTaskScheduleAsync(SysTasksQz tasksQz) + { + try + { + JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup); + if (await _scheduler.Result.CheckExists(jobKey)) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + await _scheduler.Result.PauseJob(jobKey); + } + return ApiResult.Success($"暂停计划任务:【{tasksQz.Name}】成功"); + } + catch (Exception ex) + { + logger.Error(ex); + return new ApiResult(500, $"暂停计划任务:【{tasksQz.Name}】失败,{ex.Message}"); + } + } + + /// + /// 恢复指定计划任务 + /// + /// + /// + public async Task ResumeTaskScheduleAsync(SysTasksQz tasksQz) + { + try + { + JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup); + if (!await _scheduler.Result.CheckExists(jobKey)) + { + return ApiResult.Error(500, $"未找到计划任务:【{tasksQz.Name}】"); + } + await _scheduler.Result.ResumeJob(jobKey); + return ApiResult.Success($"恢复计划任务:【{tasksQz.Name}】成功"); + } + catch (Exception) + { + throw; + } + } + + /// + /// 删除指定计划任务 + /// + /// + /// + public async Task DeleteTaskScheduleAsync(SysTasksQz tasksQz) + { + try + { + JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup); + + //await _scheduler.Result.ScheduleJob() + + await _scheduler.Result.DeleteJob(jobKey); + return ApiResult.Success($"删除计划任务:【{tasksQz.Name}】成功"); + } + catch (Exception ex) + { + logger.Error(ex); + return ApiResult.Error($"删除计划任务:【{tasksQz.Name}】失败, error={ex.Message}"); + } + } + + /// + /// 立即运行 + /// + /// + /// + public async Task RunTaskScheduleAsync(SysTasksQz tasksQz) + { + try + { + JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup); + + await _scheduler.Result.TriggerJob(jobKey); + return ApiResult.Success($"运行计划任务:【{tasksQz.Name}】成功"); + } + catch (Exception ex) + { + return new ApiResult(500, $"执行计划任务:【{tasksQz.Name}】失败,{ex.Message}"); + } + } + + /// + /// 更新计划任务 + /// + /// + /// + public async Task UpdateTaskScheduleAsync(SysTasksQz tasksQz, string groupName) + { + try + { + JobKey jobKey = new JobKey(tasksQz.ID, groupName); + if (await _scheduler.Result.CheckExists(jobKey)) + { + //防止创建时存在数据问题 先移除,然后在执行创建操作 + await _scheduler.Result.DeleteJob(jobKey); + } + await AddTaskScheduleAsync(tasksQz); + return ApiResult.Success("修改计划成功"); + } + catch (Exception ex) + { + logger.Error(ex); + return ApiResult.Error($"修改计划失败,error={ex.Message}"); + } + } + + #region 创建触发器帮助方法 + + /// + /// 创建SimpleTrigger触发器(简单触发器) + /// + /// + /// + //private ITrigger CreateSimpleTrigger(SysTasksQz tasksQz) + //{ + // if (tasksQz.RunTimes > 0) + // { + // ITrigger trigger = TriggerBuilder.Create() + // .WithIdentity(tasksQz.ID, tasksQz.JobGroup) + // .StartAt(tasksQz.BeginTime.Value) + // .EndAt(tasksQz.EndTime.Value) + // .WithSimpleSchedule(x => + // x.WithIntervalInSeconds(tasksQz.IntervalSecond) + // .WithRepeatCount(tasksQz.RunTimes)).ForJob(tasksQz.ID, tasksQz.JobGroup).Build(); + // return trigger; + // } + // else + // { + // ITrigger trigger = TriggerBuilder.Create() + // .WithIdentity(tasksQz.ID, tasksQz.JobGroup) + // .StartAt(tasksQz.BeginTime.Value) + // .EndAt(tasksQz.EndTime.Value) + // .WithSimpleSchedule(x => + // x.WithIntervalInSeconds(tasksQz.IntervalSecond) + // .RepeatForever()).ForJob(tasksQz.ID, tasksQz.JobGroup).Build(); + // return trigger; + // } + // // 触发作业立即运行,然后每10秒重复一次,无限循环 + + //} + + /// + /// 创建类型Cron的触发器 + /// + /// + /// + private ITrigger CreateCronTrigger(SysTasksQz tasksQz) + { + // 作业触发器 + return TriggerBuilder.Create() + .WithIdentity(tasksQz.ID, tasksQz.JobGroup) + .StartAt(tasksQz.BeginTime.Value)//开始时间 + .EndAt(tasksQz.EndTime.Value)//结束数据 + .WithCronSchedule(tasksQz.Cron)//指定cron表达式 + .ForJob(tasksQz.ID, tasksQz.JobGroup)//作业名称 + .Build(); + } + + #endregion + + } +} diff --git a/ZR.Tasks/ZR.Tasks.csproj b/ZR.Tasks/ZR.Tasks.csproj new file mode 100644 index 0000000..6341468 --- /dev/null +++ b/ZR.Tasks/ZR.Tasks.csproj @@ -0,0 +1,19 @@ + + + + net5.0 + + + + + + + + + + + + + + + diff --git a/ZR.Vue/.editorconfig b/ZR.Vue/.editorconfig new file mode 100644 index 0000000..7034f9b --- /dev/null +++ b/ZR.Vue/.editorconfig @@ -0,0 +1,22 @@ +# 告诉EditorConfig插件,这是根文件,不用继续往上查找 +root = true + +# 匹配全部文件 +[*] +# 设置字符集 +charset = utf-8 +# 缩进风格,可选space、tab +indent_style = space +# 缩进的空格数 +indent_size = 2 +# 结尾换行符,可选lf、cr、crlf +end_of_line = lf +# 在文件结尾插入新行 +insert_final_newline = true +# 删除一行中的前后空格 +trim_trailing_whitespace = true + +# 匹配md结尾的文件 +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/ZR.Vue/.env.development b/ZR.Vue/.env.development new file mode 100644 index 0000000..b6be2a4 --- /dev/null +++ b/ZR.Vue/.env.development @@ -0,0 +1,11 @@ +# 开发环境配置 +ENV = 'development' + +# ZR管理系统/开发环境 +VUE_APP_BASE_API = 'http://localhost:8888/' + +# 路由懒加载 +VUE_CLI_BABEL_TRANSPILE_MODULES = true + +# 路由前缀 +VUE_ROUTER_PREFIX = '/' diff --git a/ZR.Vue/.env.production b/ZR.Vue/.env.production new file mode 100644 index 0000000..08c684f --- /dev/null +++ b/ZR.Vue/.env.production @@ -0,0 +1,8 @@ +# 生产环境配置 +ENV = 'production' + +# ZR管理系统/生产环境 +VUE_APP_BASE_API = '/prod-api' + +# 路由前缀 +VUE_ROUTER_PREFIX = '/' diff --git a/ZR.Vue/.eslintignore b/ZR.Vue/.eslintignore new file mode 100644 index 0000000..89be6f6 --- /dev/null +++ b/ZR.Vue/.eslintignore @@ -0,0 +1,10 @@ +# 忽略build目录下类型为js的文件的语法检查 +build/*.js +# 忽略src/assets目录下文件的语法检查 +src/assets +# 忽略public目录下文件的语法检查 +public +# 忽略当前目录下为js的文件的语法检查 +*.js +# 忽略当前目录下为vue的文件的语法检查 +*.vue \ No newline at end of file diff --git a/ZR.Vue/.eslintrc.js b/ZR.Vue/.eslintrc.js new file mode 100644 index 0000000..82bbdee --- /dev/null +++ b/ZR.Vue/.eslintrc.js @@ -0,0 +1,199 @@ +// ESlint 检查配置 +module.exports = { + root: true, + parserOptions: { + parser: 'babel-eslint', + sourceType: 'module' + }, + env: { + browser: true, + node: true, + es6: true, + }, + extends: ['plugin:vue/recommended', 'eslint:recommended'], + + // add your custom rules here + //it is base on https://github.com/vuejs/eslint-config-vue + rules: { + "vue/max-attributes-per-line": [2, { + "singleline": 10, + "multiline": { + "max": 1, + "allowFirstLine": false + } + }], + "vue/singleline-html-element-content-newline": "off", + "vue/multiline-html-element-content-newline":"off", + "vue/name-property-casing": ["error", "PascalCase"], + "vue/no-v-html": "off", + 'accessor-pairs': 2, + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'constructor-super': 2, + 'curly': [2, 'multi-line'], + 'dot-location': [2, 'property'], + 'eol-last': 2, + 'eqeqeq': ["error", "always", {"null": "ignore"}], + 'generator-star-spacing': [2, { + 'before': true, + 'after': true + }], + 'handle-callback-err': [2, '^(err|error)$'], + 'indent': [2, 2, { + 'SwitchCase': 1 + }], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'new-cap': [2, { + 'newIsCap': true, + 'capIsNew': false + }], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-caller': 2, + 'no-console': 'off', + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-const-assign': 2, + 'no-control-regex': 0, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-parens': [2, 'functions'], + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-implied-eval': 2, + 'no-inner-declarations': [2, 'functions'], + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': [2, { + 'allowLoop': false, + 'allowSwitch': false + }], + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-octal': 2, + 'no-octal-escape': 2, + 'no-path-concat': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-return-assign': [2, 'except-parens'], + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef': 2, + 'no-undef-init': 2, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': [2, { + 'defaultAssignment': false + }], + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unused-vars': [2, { + 'vars': 'all', + 'args': 'none' + }], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'one-var': [2, { + 'initialized': 'never' + }], + 'operator-linebreak': [2, 'after', { + 'overrides': { + '?': 'before', + ':': 'before' + } + }], + 'padded-blocks': [2, 'never'], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'semi': [2, 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'use-isnan': 2, + 'valid-typeof': 2, + 'wrap-iife': [2, 'any'], + 'yield-star-spacing': [2, 'both'], + 'yoda': [2, 'never'], + 'prefer-const': 2, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} diff --git a/ZR.Vue/.gitignore b/ZR.Vue/.gitignore new file mode 100644 index 0000000..78a752d --- /dev/null +++ b/ZR.Vue/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/ZR.Vue/README.md b/ZR.Vue/README.md new file mode 100644 index 0000000..2c257da --- /dev/null +++ b/ZR.Vue/README.md @@ -0,0 +1,71 @@ +## 开发 + +```bash +# 克隆项目 +git clone https://gitee.com/y_project/RuoYi-Vue + +# 进入项目目录 +cd ruoyi-ui + +# 安装依赖 +npm install + +# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 +npm install --registry=https://registry.npm.taobao.org + +# 启动服务 +npm run dev +``` + +浏览器访问 http://localhost:80 + +## 发布 + +```bash +# 构建测试环境 +npm run build:stage + +# 构建生产环境 +npm run build:prod +``` + +## nginx配置 + +``` shell +server { + listen 8010; + #绑定多个域名 + server_name 127.0.0.1; + + # charset koi8-r; + access_log logs/logs.access.log main; + + # 后端接口 生产环境 + location /prod-api/ { + proxy_pass http://localhost:8085/; + + # 后端的Web服务器可以通过X-Forwarded-For获取用户真实IP + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # 如果请求被负载均衡的服务器返回类似500这样的,将继续请求下一台应用服务器,默认 对post,lock,patch的请求不进行重试,如果要设置在后面添加 non_idemponent + # proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; + } + + # vue 项目 + location / { + root html/dist; + index index.html; + try_files $uri $uri/ /index.html; + } + + error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } +} +``` \ No newline at end of file diff --git a/ZR.Vue/babel.config.js b/ZR.Vue/babel.config.js new file mode 100644 index 0000000..b99f001 --- /dev/null +++ b/ZR.Vue/babel.config.js @@ -0,0 +1,13 @@ +module.exports = { + presets: [ + // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app + '@vue/cli-plugin-babel/preset' + ], + 'env': { + 'development': { + // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). + // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. + 'plugins': ['dynamic-import-node'] + } + } +} diff --git a/ZR.Vue/bat/build.bat b/ZR.Vue/bat/build.bat new file mode 100644 index 0000000..dda590d --- /dev/null +++ b/ZR.Vue/bat/build.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [ÐÅϢ] ´ò°üWeb¹¤³̣¬Éú³ÉdistÎļþ¡£ +echo. + +%~d0 +cd %~dp0 + +cd .. +npm run build:prod + +pause \ No newline at end of file diff --git a/ZR.Vue/bat/package.bat b/ZR.Vue/bat/package.bat new file mode 100644 index 0000000..8281749 --- /dev/null +++ b/ZR.Vue/bat/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [ÐÅϢ] °²װWeb¹¤³̣¬Éú³Énode_modulesÎļþ¡£ +echo. + +%~d0 +cd %~dp0 + +cd .. +npm install --registry=https://registry.npm.taobao.org + +pause \ No newline at end of file diff --git a/ZR.Vue/bat/run-web.bat b/ZR.Vue/bat/run-web.bat new file mode 100644 index 0000000..a938e89 --- /dev/null +++ b/ZR.Vue/bat/run-web.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [ÐÅϢ] ʹÓà Vue ÔËÐÐ Web ¹¤³̡£ +echo. + +%~d0 +cd %~dp0 + +cd .. +npm run dev + +pause \ No newline at end of file diff --git a/ZR.Vue/document/demoApi.js b/ZR.Vue/document/demoApi.js new file mode 100644 index 0000000..e6af219 --- /dev/null +++ b/ZR.Vue/document/demoApi.js @@ -0,0 +1,53 @@ +import request from '@/utils/request' + +// 查询列表 +export function listXXXX(query) { + return request({ + url: '/system/XXXX/list', + method: 'get', + params: query + }) +} + +// 查询详细 +export function getXXXX(Id) { + return request({ + url: '/system/XXXX/' + Id, + method: 'get', + }) +} + +// 新增 +export function addXXXX (data) { + return request({ + url: '/system/XXXX/add', + method: 'post', + data: data, + }) +} + +// 修改 +export function updateXXXX(data) { + return request({ + url: '/system/XXXX/edit', + method: 'put', + data: data + }) +} + +// 删除菜单 +export function delXXXX(id) { + return request({ + url: '/system/XXXX/' + id, + method: 'delete' + }) +} + +//排序 +export function changeSort(data) { + return request({ + url: '/system/XXXX/ChangeSort', + method: 'post', + data: data + }) +} diff --git a/ZR.Vue/document/t4.tt b/ZR.Vue/document/t4.tt new file mode 100644 index 0000000..e69de29 diff --git a/ZR.Vue/document/template.vue b/ZR.Vue/document/template.vue new file mode 100644 index 0000000..9a4ce72 --- /dev/null +++ b/ZR.Vue/document/template.vue @@ -0,0 +1,280 @@ + + diff --git a/ZR.Vue/package.json b/ZR.Vue/package.json new file mode 100644 index 0000000..4b84f42 --- /dev/null +++ b/ZR.Vue/package.json @@ -0,0 +1,89 @@ +{ + "name": "zr", + "version": "1.0.0", + "description": "管理系统", + "author": "ZR", + "scripts": { + "dev": "vue-cli-service serve", + "build:prod": "vue-cli-service build", + "build:stage": "vue-cli-service build --mode staging" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "src/**/*.{js,vue}": [ + "eslint --fix", + "git add" + ] + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "axios": "^0.21.1", + "clipboard": "2.0.4", + "core-js": "3.6.5", + "echarts": "^5.1.1", + "element-ui": "2.13.2", + "file-saver": "2.0.1", + "fuse.js": "3.4.4", + "js-beautify": "1.10.2", + "js-cookie": "2.2.0", + "jsencrypt": "3.0.0-rc.1", + "less-loader": "^6.0.0", + "mavon-editor": "^2.9.1", + "normalize.css": "7.0.0", + "nprogress": "0.2.0", + "path-to-regexp": "2.4.0", + "quill": "1.3.7", + "screenfull": "4.2.0", + "sortablejs": "1.8.4", + "vue": "2.6.10", + "vue-count-to": "1.0.13", + "vue-cropper": "0.4.9", + "vue-router": "3.0.2", + "vue-splitpane": "1.0.4", + "vuedraggable": "2.20.0", + "vuex": "3.1.0" + }, + "devDependencies": { + "@vue/cli-plugin-babel": "4.4.4", + "@vue/cli-plugin-eslint": "4.4.4", + "@vue/cli-plugin-unit-jest": "4.4.4", + "@vue/cli-service": "4.4.4", + "@vue/test-utils": "1.0.0-beta.29", + "autoprefixer": "9.5.1", + "babel-eslint": "10.1.0", + "babel-jest": "23.6.0", + "babel-plugin-dynamic-import-node": "2.3.3", + "chalk": "2.4.2", + "chokidar": "2.1.5", + "connect": "3.6.6", + "eslint": "6.7.2", + "eslint-plugin-vue": "6.2.2", + "filemanager-webpack-plugin": "^6.1.4", + "html-webpack-plugin": "3.2.0", + "husky": "1.3.1", + "lint-staged": "8.1.5", + "node-sass": "4.14.1", + "plop": "2.3.0", + "qs": "^6.9.4", + "runjs": "4.3.2", + "sass-loader": "8.0.2", + "script-ext-html-webpack-plugin": "2.1.3", + "script-loader": "0.7.2", + "serve-static": "1.13.2", + "svg-sprite-loader": "4.1.3", + "svgo": "1.2.0", + "vue-template-compiler": "2.6.10" + }, + "engines": { + "node": ">=8.9", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions" + ] +} diff --git a/ZR.Vue/public/favicon.ico b/ZR.Vue/public/favicon.ico new file mode 100644 index 0000000..fcb6999 Binary files /dev/null and b/ZR.Vue/public/favicon.ico differ diff --git a/ZR.Vue/public/index.html b/ZR.Vue/public/index.html new file mode 100644 index 0000000..6d64bf9 --- /dev/null +++ b/ZR.Vue/public/index.html @@ -0,0 +1,207 @@ + + + + + + + + + <%= webpackConfig.name %> + + + +
+
+
+
+
+
正在加载系统资源,请耐心等待
+
+
+ + diff --git a/ZR.Vue/public/robots.txt b/ZR.Vue/public/robots.txt new file mode 100644 index 0000000..77470cb --- /dev/null +++ b/ZR.Vue/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/ZR.Vue/src/App.vue b/ZR.Vue/src/App.vue new file mode 100644 index 0000000..e448b11 --- /dev/null +++ b/ZR.Vue/src/App.vue @@ -0,0 +1,11 @@ + + + diff --git a/ZR.Vue/src/api/common.js b/ZR.Vue/src/api/common.js new file mode 100644 index 0000000..51044b3 --- /dev/null +++ b/ZR.Vue/src/api/common.js @@ -0,0 +1,10 @@ +import request from '@/utils/request' + +export function upload(data) { + return request({ + url: '/upload/saveFile', + method: 'POST', + data: data, + headers: { "Content-Type": "multipart/form-data" }, + }) +} diff --git a/ZR.Vue/src/api/login.js b/ZR.Vue/src/api/login.js new file mode 100644 index 0000000..593f801 --- /dev/null +++ b/ZR.Vue/src/api/login.js @@ -0,0 +1,40 @@ +import request from '@/utils/request' + +// 登录方法 +export function login(username, password, code, uuid) { + const data = { + username, + password, + code, + uuid + } + return request({ + url: '/login', + method: 'POST', + data: data, + }) +} + +// 获取用户详细信息 +export function getInfo() { + return request({ + url: '/getInfo', + method: 'get' + }) +} + +// 退出方法 +export function logOut() { + return request({ + url: '/LogOut', + method: 'POST' + }) +} + +// 获取验证码 +export function getCodeImg() { + return request({ + url: '/captchaImage', + method: 'get' + }) +} diff --git a/ZR.Vue/src/api/monitor/cache.js b/ZR.Vue/src/api/monitor/cache.js new file mode 100644 index 0000000..59d3505 --- /dev/null +++ b/ZR.Vue/src/api/monitor/cache.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 查询缓存详细 +export function getCache() { + return request({ + url: '/monitor/cache', + method: 'get' + }) +} diff --git a/ZR.Vue/src/api/monitor/job.js b/ZR.Vue/src/api/monitor/job.js new file mode 100644 index 0000000..4273f5b --- /dev/null +++ b/ZR.Vue/src/api/monitor/job.js @@ -0,0 +1,69 @@ +import request from '@/utils/request' + +export function queryTasks(data) { + return request({ + url: '/system/tasks/list', + method: 'get', + data + }) +} + +export function getTasks(id) { + return request({ + url: '/system/tasks/get?id=' + id, + method: 'get' + }) +} + +export function getAllTasks() { + return request({ + url: '/system/tasks/getAll', + method: 'get' + }) +} + +export function createTasks(data) { + return request({ + url: '/system/tasks/create', + method: 'post', + data + }) +} + +export function updateTasks(data) { + return request({ + url: '/system/tasks/update', + method: 'post', + data + }) +} + +export function deleteTasks(id) { + return request({ + url: '/system/tasks/delete?id=' + id, + method: 'delete' + }) +} + +export function startTasks(id) { + return request({ + url: '/system/tasks/start?id=' + id, + method: 'get' + }) +} + +export function stopTasks(id) { + return request({ + url: '/system/tasks/stop?id=' + id, + method: 'get' + }) +} + +export function runTasks(id) { + return request({ + url: '/system/tasks/run?id=' + id, + method: 'get' + }) +} + +export default { queryTasks, getTasks, getAllTasks, createTasks, updateTasks, deleteTasks, startTasks, stopTasks, runTasks } diff --git a/ZR.Vue/src/api/monitor/jobLog.js b/ZR.Vue/src/api/monitor/jobLog.js new file mode 100644 index 0000000..be1fffd --- /dev/null +++ b/ZR.Vue/src/api/monitor/jobLog.js @@ -0,0 +1,35 @@ +import request from '@/utils/request' + +// 查询调度日志列表 +export function listJobLog(query) { + return request({ + url: '/monitor/jobLog/list', + method: 'get', + params: query + }) +} + +// 删除调度日志 +export function delJobLog(jobLogId) { + return request({ + url: '/monitor/jobLog/' + jobLogId, + method: 'delete' + }) +} + +// 清空调度日志 +export function cleanJobLog() { + return request({ + url: '/monitor/jobLog/clean', + method: 'delete' + }) +} + +// 导出调度日志 +export function exportJobLog(query) { + return request({ + url: '/monitor/jobLog/export', + method: 'get', + params: query + }) +} \ No newline at end of file diff --git a/ZR.Vue/src/api/monitor/logininfor.js b/ZR.Vue/src/api/monitor/logininfor.js new file mode 100644 index 0000000..7ff412d --- /dev/null +++ b/ZR.Vue/src/api/monitor/logininfor.js @@ -0,0 +1,35 @@ +import request from '@/utils/request' + +// 查询登录日志列表 +export function list(query) { + return request({ + url: '/monitor/logininfor/list', + method: 'get', + params: query + }) +} + +// 删除登录日志 +export function delLogininfor(infoId) { + return request({ + url: '/monitor/logininfor/' + infoId, + method: 'delete' + }) +} + +// 清空登录日志 +export function cleanLogininfor() { + return request({ + url: '/monitor/logininfor/clean', + method: 'delete' + }) +} + +// 导出登录日志 +export function exportLogininfor(query) { + return request({ + url: '/monitor/logininfor/export', + method: 'get', + params: query + }) +} diff --git a/ZR.Vue/src/api/monitor/online.js b/ZR.Vue/src/api/monitor/online.js new file mode 100644 index 0000000..bd22137 --- /dev/null +++ b/ZR.Vue/src/api/monitor/online.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +// 查询在线用户列表 +export function list(query) { + return request({ + url: '/monitor/online/list', + method: 'get', + params: query + }) +} + +// 强退用户 +export function forceLogout(tokenId) { + return request({ + url: '/monitor/online/' + tokenId, + method: 'delete' + }) +} diff --git a/ZR.Vue/src/api/monitor/operlog.js b/ZR.Vue/src/api/monitor/operlog.js new file mode 100644 index 0000000..07e27ab --- /dev/null +++ b/ZR.Vue/src/api/monitor/operlog.js @@ -0,0 +1,35 @@ +import request from '@/utils/request' + +// 查询操作日志列表 +export function list(query) { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query + }) +} + +// 删除操作日志 +export function delOperlog(operId) { + return request({ + url: '/monitor/operlog/' + operId, + method: 'delete' + }) +} + +// 清空操作日志 +export function cleanOperlog() { + return request({ + url: '/monitor/operlog/clean', + method: 'delete' + }) +} + +// 导出操作日志 +export function exportOperlog(query) { + return request({ + url: '/monitor/operlog/export', + method: 'get', + params: query + }) +} diff --git a/ZR.Vue/src/api/monitor/server.js b/ZR.Vue/src/api/monitor/server.js new file mode 100644 index 0000000..bdcc62b --- /dev/null +++ b/ZR.Vue/src/api/monitor/server.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 查询服务器详细 +export function getServer() { + return request({ + url: '/monitor/server', + method: 'get' + }) +} diff --git a/ZR.Vue/src/api/system/article.js b/ZR.Vue/src/api/system/article.js new file mode 100644 index 0000000..a25523f --- /dev/null +++ b/ZR.Vue/src/api/system/article.js @@ -0,0 +1,58 @@ +import request from '@/utils/request' + +// 查询列表 +export function listArticle(query) { + return request({ + url: '/Article/list', + method: 'get', + params: query + }) +} + +// 查询详细 +export function getArticle(Id) { + return request({ + url: '/Article/' + Id, + method: 'get', + }) +} + +// 新增 +export function addArticle(data) { + return request({ + url: '/Article/add', + method: 'post', + data: data, + }) +} + +// 修改 +export function updateArticle(data) { + return request({ + url: '/Article/edit', + method: 'put', + data: data + }) +} + +// 删除菜单 +export function delArticle(id) { + return request({ + url: '/Article/' + id, + method: 'delete' + }) +} +// 查询菜单目录 +export function listArticleCategory() { + return request({ + url: '/Article/CategoryList', + method: 'get' + }) +} +// 查询菜单目录树 +export function listArticleCategoryTree(){ + return request({ + url: '/Article/CategoryTreeList', + menubar: 'get' + }) +} diff --git a/ZR.Vue/src/api/system/dept.js b/ZR.Vue/src/api/system/dept.js new file mode 100644 index 0000000..2804676 --- /dev/null +++ b/ZR.Vue/src/api/system/dept.js @@ -0,0 +1,68 @@ +import request from '@/utils/request' + +// 查询部门列表 +export function listDept(query) { + return request({ + url: '/system/dept/list', + method: 'get', + params: query + }) +} + +// 查询部门列表(排除节点) +export function listDeptExcludeChild(deptId) { + return request({ + url: '/system/dept/list/exclude/' + deptId, + method: 'get' + }) +} + +// 查询部门详细 +export function getDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'get' + }) +} + +// 查询部门下拉树结构 +export function treeselect() { + return request({ + url: '/system/dept/treeselect', + method: 'get' + }) +} + +// 根据角色ID查询部门树结构 +export function roleDeptTreeselect(roleId) { + return request({ + url: '/system/dept/roleDeptTreeselect/' + roleId, + method: 'get' + }) +} + +// 新增部门 +export function addDept(data) { + return request({ + url: '/system/dept', + method: 'post', + data: data + }) +} + +// 修改部门 +export function updateDept(data) { + return request({ + url: '/system/dept', + method: 'put', + data: data + }) +} + +// 删除部门 +export function delDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ZR.Vue/src/api/system/dict/data.js b/ZR.Vue/src/api/system/dict/data.js new file mode 100644 index 0000000..8846862 --- /dev/null +++ b/ZR.Vue/src/api/system/dict/data.js @@ -0,0 +1,61 @@ +import request from '@/utils/request' + +// 查询字典数据列表 +export function listData(query) { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query + }) +} + +// 查询字典数据详细 +export function getData(dictCode) { + return request({ + url: '/system/dict/data/info/' + dictCode, + method: 'get' + }) +} + +// 根据字典类型查询字典数据信息 +export function getDicts(dictType) { + return request({ + url: '/system/dict/data/type/' + dictType, + method: 'get' + }) +} + +// 新增字典数据 +export function addData(data) { + return request({ + url: '/system/dict/data', + method: 'post', + data: data + }) +} + +// 修改字典数据 +export function updateData(data) { + return request({ + url: '/system/dict/data', + method: 'put', + data: data + }) +} + +// 删除字典数据 +export function delData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete' + }) +} + +// 导出字典数据 +export function exportData(query) { + return request({ + url: '/system/dict/data/export', + method: 'get', + params: query + }) +} diff --git a/ZR.Vue/src/api/system/dict/type.js b/ZR.Vue/src/api/system/dict/type.js new file mode 100644 index 0000000..27ae18f --- /dev/null +++ b/ZR.Vue/src/api/system/dict/type.js @@ -0,0 +1,69 @@ +import request from '@/utils/request' + +// 查询字典类型列表 +export function listType(query) { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query + }) +} + +// 查询字典类型详细 +export function getType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get' + }) +} + +// 新增字典类型 +export function addType(data) { + return request({ + url: '/system/dict/type/edit', + method: 'post', + data: data + }) +} + +// 修改字典类型 +export function updateType(data) { + return request({ + url: '/system/dict/type/edit', + method: 'put', + data: data + }) +} + +// 删除字典类型 +export function delType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete' + }) +} + +// 清理参数缓存 +export function clearCache() { + return request({ + url: '/system/dict/type/clearCache', + method: 'delete' + }) +} + +// 导出字典类型 +export function exportType(query) { + return request({ + url: '/system/dict/type/export', + method: 'get', + params: query + }) +} + +// 获取字典选择框列表 +export function optionselect() { + return request({ + url: '/system/dict/type/optionselect', + method: 'get' + }) +} diff --git a/ZR.Vue/src/api/system/menu.js b/ZR.Vue/src/api/system/menu.js new file mode 100644 index 0000000..124538b --- /dev/null +++ b/ZR.Vue/src/api/system/menu.js @@ -0,0 +1,78 @@ +import request from '@/utils/request' + +// 查询菜单列表 +export function listMenu(query) { + return request({ + url: '/system/menu/list', + method: 'get', + params: query + }) +} + +// 查询菜单详细 +export function getMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'get', + }) +} + +// 查询菜单下拉树结构 +export function treeselect() { + return request({ + url: '/system/Menu/treeSelect', + method: 'get' + }) +} + +// 根据角色ID查询菜单下拉树结构 +export function roleMenuTreeselect(roleId) { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get', + }) +} + +// 新增菜单 +export const addMenu = (data) => { + return request({ + url: '/system/menu/add', + method: 'put', + data: data, + }) +} + +// 修改菜单 +export function updateMenu(data) { + return request({ + url: '/system/Menu/edit', + method: 'post', + data: data + }) +} + +// 删除菜单 +export function delMenu(menuId) { + return request({ + url: '/system/Menu/' + menuId, + method: 'delete' + }) +} + +//排序 +export function changeMenuSort(data) { + return request({ + url: '/system/Menu/ChangeSort', + method: 'post', + data: data + }) +} + +// 获取路由 +export const getRouters = (query) => { + return request({ + url: '/getRouters', + method: 'get', + params: query + }) +} diff --git a/ZR.Vue/src/api/system/notice.js b/ZR.Vue/src/api/system/notice.js new file mode 100644 index 0000000..c274ea5 --- /dev/null +++ b/ZR.Vue/src/api/system/notice.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询公告列表 +export function listNotice(query) { + return request({ + url: '/system/notice/list', + method: 'get', + params: query + }) +} + +// 查询公告详细 +export function getNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'get' + }) +} + +// 新增公告 +export function addNotice(data) { + return request({ + url: '/system/notice', + method: 'post', + data: data + }) +} + +// 修改公告 +export function updateNotice(data) { + return request({ + url: '/system/notice', + method: 'put', + data: data + }) +} + +// 删除公告 +export function delNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ZR.Vue/src/api/system/post.js b/ZR.Vue/src/api/system/post.js new file mode 100644 index 0000000..434cd35 --- /dev/null +++ b/ZR.Vue/src/api/system/post.js @@ -0,0 +1,53 @@ +import request from '@/utils/request' + +// 查询岗位列表 +export function listPost(query) { + return request({ + url: '/system/post/list', + method: 'get', + params: query + }) +} + +// 查询岗位详细 +export function getPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'get' + }) +} + +// 新增岗位 +export function addPost(data) { + return request({ + url: '/system/post', + method: 'post', + data: data + }) +} + +// 修改岗位 +export function updatePost(data) { + return request({ + url: '/system/post', + method: 'put', + data: data + }) +} + +// 删除岗位 +export function delPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'delete' + }) +} + +// 导出岗位 +export function exportPost(query) { + return request({ + url: '/system/post/export', + method: 'get', + params: query + }) +} \ No newline at end of file diff --git a/ZR.Vue/src/api/system/role.js b/ZR.Vue/src/api/system/role.js new file mode 100644 index 0000000..788fcbf --- /dev/null +++ b/ZR.Vue/src/api/system/role.js @@ -0,0 +1,75 @@ +import request from '@/utils/request' + +// 查询角色列表 +export function listRole(query) { + return request({ + url: '/system/role/list', + method: 'get', + params: query + }) +} + +// 查询角色详细 +export function getRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'get' + }) +} + +// 新增角色 +export const addRole = (data) => { + return request({ + url: '/system/role/edit', + method: 'post', + data: data, + }) +} + +// 修改角色 +export function updateRole(data) { + return request({ + url: '/system/role/edit', + method: 'put', + data: data + }) +} + +// 角色数据权限 +export function dataScope(data) { + return request({ + url: '/system/role/dataScope', + method: 'put', + data: data + }) +} + +// 角色状态修改 +export function changeRoleStatus(roleId, status) { + const data = { + roleId, + status + } + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data + }) +} + +// 删除角色 +export function delRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'delete' + }) +} + +// 导出角色 +export function exportRole(query) { + return request({ + url: '/system/sysRole/export', + method: 'get', + params: query + }) +} diff --git a/ZR.Vue/src/api/system/user.js b/ZR.Vue/src/api/system/user.js new file mode 100644 index 0000000..0d3cf9b --- /dev/null +++ b/ZR.Vue/src/api/system/user.js @@ -0,0 +1,128 @@ +import request from '@/utils/request' +import { praseStrZero } from "@/utils/ruoyi"; + +// 查询用户列表 +export function listUser(query) { + return request({ + url: '/system/user/list', + method: 'get', + params: query + }) +} + +// 查询用户详细 +export function getUser(userId) { + return request({ + url: '/system/user/' + praseStrZero(userId), + method: 'get' + }) +} + +// 新增用户 +export function addUser(data) { + return request({ + url: '/system/user/edit', + method: 'post', + data: data + }) +} + +// 修改用户 +export function updateUser(data) { + return request({ + url: '/system/user/edit', + method: 'put', + data: data + }) +} + +// 删除用户 +export function delUser(userId) { + return request({ + url: '/system/user/' + userId, + method: 'delete' + }) +} + +// 导出用户 +export function exportUser(query) { + return request({ + url: '/system/User/export', + method: 'get', + params: query + }) +} + +// 用户密码重置 +export function resetUserPwd(userId, password) { + const data = { + userId, + password + } + return request({ + url: '/system/user/resetPwd', + method: 'put', + data: data + }) +} + +// 用户状态修改 +export function changeUserStatus(userId, status) { + const data = { + userId, + status + } + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data + }) +} + +// 查询用户个人信息 +export function getUserProfile() { + return request({ + url: '/system/user/Profile', + method: 'get' + }) +} + +// 修改用户个人信息 +export function updateUserProfile(data) { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }) +} + +// 用户密码重置 +export function updateUserPwd(oldPassword, newPassword) { + const data = { + oldPassword, + newPassword + } + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + params: data + }) +} + +// 用户头像上传 +export function uploadAvatar(data) { + return request({ + url: '/system/user/profile/avatar', + method: 'post', + data: data + }) +} + +// 下载用户导入模板 +export function importTemplate() { + return request({ + url: '/system/user/importTemplate', + method: 'get', + responseType: 'blob'//1.首先设置responseType对象格式为 blob: + }) +} diff --git a/ZR.Vue/src/api/system/userRoles.js b/ZR.Vue/src/api/system/userRoles.js new file mode 100644 index 0000000..cb026d1 --- /dev/null +++ b/ZR.Vue/src/api/system/userRoles.js @@ -0,0 +1,35 @@ +import request from '@/utils/request' + +// 查询角色用户 +export function getRoleUsers(roleId) { + return request({ + url: '/system/userRole/get/' + roleId, + method: 'get' + }) +} + +// 添加角色用户 +export function createRoleUsers(data) { + return request({ + url: '/system/userRole/create', + method: 'post', + data + }) +} +// 删除角色用户 +export function deleteRoleUsers(data) { + return request({ + url: '/system/userRole/delete', + method: 'post', + data + }) +} +// 查询角色未添加用户列表 +export function getExcludeUsers(roleId) { + return request({ + url: '/system/userRole/getExcludeUsers?roleId=' + roleId, + method: 'get' + }) +} + +// export default { getRoleUsers, getExcludeUsers } diff --git a/ZR.Vue/src/api/tool/gen.js b/ZR.Vue/src/api/tool/gen.js new file mode 100644 index 0000000..4506927 --- /dev/null +++ b/ZR.Vue/src/api/tool/gen.js @@ -0,0 +1,76 @@ +import request from '@/utils/request' + +// 查询生成表数据 +export function listTable(query) { + return request({ + url: '/tool/gen/list', + method: 'get', + params: query + }) +} +// 查询db数据库列表 +export function listDbTable(query) { + return request({ + url: '/tool/gen/db/list', + method: 'get', + params: query + }) +} + +// 查询表详细信息 +export function getGenTable(tableId) { + return request({ + url: '/tool/gen/' + tableId, + method: 'get' + }) +} + +// 修改代码生成信息 +export function updateGenTable(data) { + return request({ + url: '/tool/gen', + method: 'put', + data: data + }) +} + +// 导入表 +export function importTable(data) { + return request({ + url: '/tool/gen/importTable', + method: 'post', + params: data + }) +} + +// 预览生成代码 +export function previewTable(tableId) { + return request({ + url: '/tool/gen/preview/' + tableId, + method: 'get' + }) +} + +// 删除表数据 +export function delTable(tableId) { + return request({ + url: '/tool/gen/' + tableId, + method: 'delete' + }) +} + +// 生成代码(自定义路径) +export function genCode(tableName) { + return request({ + url: '/tool/gen/genCode/' + tableName, + method: 'get' + }) +} + +// 同步数据库 +export function synchDb(tableName) { + return request({ + url: '/tool/gen/synchDb/' + tableName, + method: 'get' + }) +} diff --git a/ZR.Vue/src/assets/401_images/401.gif b/ZR.Vue/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/ZR.Vue/src/assets/401_images/401.gif differ diff --git a/ZR.Vue/src/assets/404_images/404.png b/ZR.Vue/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/ZR.Vue/src/assets/404_images/404.png differ diff --git a/ZR.Vue/src/assets/404_images/404_cloud.png b/ZR.Vue/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/ZR.Vue/src/assets/404_images/404_cloud.png differ diff --git a/ZR.Vue/src/assets/icons/index.js b/ZR.Vue/src/assets/icons/index.js new file mode 100644 index 0000000..2c6b309 --- /dev/null +++ b/ZR.Vue/src/assets/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/ZR.Vue/src/assets/icons/svg/404.svg b/ZR.Vue/src/assets/icons/svg/404.svg new file mode 100644 index 0000000..6df5019 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/bug.svg b/ZR.Vue/src/assets/icons/svg/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/build.svg b/ZR.Vue/src/assets/icons/svg/build.svg new file mode 100644 index 0000000..97c4688 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/build.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/button.svg b/ZR.Vue/src/assets/icons/svg/button.svg new file mode 100644 index 0000000..904fddc --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/cascader.svg b/ZR.Vue/src/assets/icons/svg/cascader.svg new file mode 100644 index 0000000..e256024 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/chart.svg b/ZR.Vue/src/assets/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/checkbox.svg b/ZR.Vue/src/assets/icons/svg/checkbox.svg new file mode 100644 index 0000000..013fd3a --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/checkbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/clipboard.svg b/ZR.Vue/src/assets/icons/svg/clipboard.svg new file mode 100644 index 0000000..90923ff --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/code.svg b/ZR.Vue/src/assets/icons/svg/code.svg new file mode 100644 index 0000000..ed4d23c --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/color.svg b/ZR.Vue/src/assets/icons/svg/color.svg new file mode 100644 index 0000000..44a81aa --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/component.svg b/ZR.Vue/src/assets/icons/svg/component.svg new file mode 100644 index 0000000..29c3458 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/dashboard.svg b/ZR.Vue/src/assets/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/date-range.svg b/ZR.Vue/src/assets/icons/svg/date-range.svg new file mode 100644 index 0000000..fda571e --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/date-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/date.svg b/ZR.Vue/src/assets/icons/svg/date.svg new file mode 100644 index 0000000..52dc73e --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/date.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/dict.svg b/ZR.Vue/src/assets/icons/svg/dict.svg new file mode 100644 index 0000000..4849377 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/documentation.svg b/ZR.Vue/src/assets/icons/svg/documentation.svg new file mode 100644 index 0000000..7043122 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/download.svg b/ZR.Vue/src/assets/icons/svg/download.svg new file mode 100644 index 0000000..c896951 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/drag.svg b/ZR.Vue/src/assets/icons/svg/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/druid.svg b/ZR.Vue/src/assets/icons/svg/druid.svg new file mode 100644 index 0000000..a2b4b4e --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/druid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/edit.svg b/ZR.Vue/src/assets/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/education.svg b/ZR.Vue/src/assets/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/email.svg b/ZR.Vue/src/assets/icons/svg/email.svg new file mode 100644 index 0000000..74d25e2 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/example.svg b/ZR.Vue/src/assets/icons/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/excel.svg b/ZR.Vue/src/assets/icons/svg/excel.svg new file mode 100644 index 0000000..74d97b8 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/exit-fullscreen.svg b/ZR.Vue/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/eye-open.svg b/ZR.Vue/src/assets/icons/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/eye.svg b/ZR.Vue/src/assets/icons/svg/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/form.svg b/ZR.Vue/src/assets/icons/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/fullscreen.svg b/ZR.Vue/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/github.svg b/ZR.Vue/src/assets/icons/svg/github.svg new file mode 100644 index 0000000..db0a0d4 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/guide.svg b/ZR.Vue/src/assets/icons/svg/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/icon.svg b/ZR.Vue/src/assets/icons/svg/icon.svg new file mode 100644 index 0000000..82be8ee --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/icon1.svg b/ZR.Vue/src/assets/icons/svg/icon1.svg new file mode 100644 index 0000000..82fbdd9 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/icon1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/input.svg b/ZR.Vue/src/assets/icons/svg/input.svg new file mode 100644 index 0000000..ab91381 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/input.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/international.svg b/ZR.Vue/src/assets/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/job.svg b/ZR.Vue/src/assets/icons/svg/job.svg new file mode 100644 index 0000000..2a93a25 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/job.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/language.svg b/ZR.Vue/src/assets/icons/svg/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/link.svg b/ZR.Vue/src/assets/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/list.svg b/ZR.Vue/src/assets/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/lock.svg b/ZR.Vue/src/assets/icons/svg/lock.svg new file mode 100644 index 0000000..74fee54 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/log.svg b/ZR.Vue/src/assets/icons/svg/log.svg new file mode 100644 index 0000000..d879d33 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/logininfor.svg b/ZR.Vue/src/assets/icons/svg/logininfor.svg new file mode 100644 index 0000000..267f844 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/logininfor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/message.svg b/ZR.Vue/src/assets/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/money.svg b/ZR.Vue/src/assets/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/monitor.svg b/ZR.Vue/src/assets/icons/svg/monitor.svg new file mode 100644 index 0000000..bc308cb --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/nested.svg b/ZR.Vue/src/assets/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/number.svg b/ZR.Vue/src/assets/icons/svg/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/number.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/online.svg b/ZR.Vue/src/assets/icons/svg/online.svg new file mode 100644 index 0000000..330a202 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/password.svg b/ZR.Vue/src/assets/icons/svg/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/pdf.svg b/ZR.Vue/src/assets/icons/svg/pdf.svg new file mode 100644 index 0000000..957aa0c --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/people.svg b/ZR.Vue/src/assets/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/peoples.svg b/ZR.Vue/src/assets/icons/svg/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/permission.svg b/ZR.Vue/src/assets/icons/svg/permission.svg new file mode 100644 index 0000000..c4c7409 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/permission.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/phone.svg b/ZR.Vue/src/assets/icons/svg/phone.svg new file mode 100644 index 0000000..ab8e8c4 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/post.svg b/ZR.Vue/src/assets/icons/svg/post.svg new file mode 100644 index 0000000..2922c61 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/post.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/qq.svg b/ZR.Vue/src/assets/icons/svg/qq.svg new file mode 100644 index 0000000..ee13d4e --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/qq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/question.svg b/ZR.Vue/src/assets/icons/svg/question.svg new file mode 100644 index 0000000..cf75bd4 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/radio.svg b/ZR.Vue/src/assets/icons/svg/radio.svg new file mode 100644 index 0000000..0cde345 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/radio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/rate.svg b/ZR.Vue/src/assets/icons/svg/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/redis.svg b/ZR.Vue/src/assets/icons/svg/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/row.svg b/ZR.Vue/src/assets/icons/svg/row.svg new file mode 100644 index 0000000..0780992 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/row.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/search.svg b/ZR.Vue/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/select.svg b/ZR.Vue/src/assets/icons/svg/select.svg new file mode 100644 index 0000000..d628382 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/server.svg b/ZR.Vue/src/assets/icons/svg/server.svg new file mode 100644 index 0000000..ca37b00 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/shopping.svg b/ZR.Vue/src/assets/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/size.svg b/ZR.Vue/src/assets/icons/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/skill.svg b/ZR.Vue/src/assets/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/slider.svg b/ZR.Vue/src/assets/icons/svg/slider.svg new file mode 100644 index 0000000..fbe4f39 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/slider.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/star.svg b/ZR.Vue/src/assets/icons/svg/star.svg new file mode 100644 index 0000000..6cf86e6 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/swagger.svg b/ZR.Vue/src/assets/icons/svg/swagger.svg new file mode 100644 index 0000000..05d4e7b --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/swagger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/switch.svg b/ZR.Vue/src/assets/icons/svg/switch.svg new file mode 100644 index 0000000..0ba61e3 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/switch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/system.svg b/ZR.Vue/src/assets/icons/svg/system.svg new file mode 100644 index 0000000..dba28cf --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/system.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/tab.svg b/ZR.Vue/src/assets/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/table.svg b/ZR.Vue/src/assets/icons/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/textarea.svg b/ZR.Vue/src/assets/icons/svg/textarea.svg new file mode 100644 index 0000000..2709f29 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/textarea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/theme.svg b/ZR.Vue/src/assets/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/time-range.svg b/ZR.Vue/src/assets/icons/svg/time-range.svg new file mode 100644 index 0000000..13c1202 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/time-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/time.svg b/ZR.Vue/src/assets/icons/svg/time.svg new file mode 100644 index 0000000..b376e32 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/tool.svg b/ZR.Vue/src/assets/icons/svg/tool.svg new file mode 100644 index 0000000..c813067 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/tool.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/tree-table.svg b/ZR.Vue/src/assets/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/tree.svg b/ZR.Vue/src/assets/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/upload.svg b/ZR.Vue/src/assets/icons/svg/upload.svg new file mode 100644 index 0000000..bae49c0 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/user.svg b/ZR.Vue/src/assets/icons/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/validCode.svg b/ZR.Vue/src/assets/icons/svg/validCode.svg new file mode 100644 index 0000000..cfb1021 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/validCode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/wechat.svg b/ZR.Vue/src/assets/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/zip.svg b/ZR.Vue/src/assets/icons/svg/zip.svg new file mode 100644 index 0000000..f806fc4 --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svg/zujian.svg b/ZR.Vue/src/assets/icons/svg/zujian.svg new file mode 100644 index 0000000..2aba32f --- /dev/null +++ b/ZR.Vue/src/assets/icons/svg/zujian.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZR.Vue/src/assets/icons/svgo.yml b/ZR.Vue/src/assets/icons/svgo.yml new file mode 100644 index 0000000..d11906a --- /dev/null +++ b/ZR.Vue/src/assets/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/ZR.Vue/src/assets/image/login-background.jpg b/ZR.Vue/src/assets/image/login-background.jpg new file mode 100644 index 0000000..8a89eb8 Binary files /dev/null and b/ZR.Vue/src/assets/image/login-background.jpg differ diff --git a/ZR.Vue/src/assets/image/profile.jpg b/ZR.Vue/src/assets/image/profile.jpg new file mode 100644 index 0000000..300fca5 Binary files /dev/null and b/ZR.Vue/src/assets/image/profile.jpg differ diff --git a/ZR.Vue/src/assets/logo/logo.png b/ZR.Vue/src/assets/logo/logo.png new file mode 100644 index 0000000..9838306 Binary files /dev/null and b/ZR.Vue/src/assets/logo/logo.png differ diff --git a/ZR.Vue/src/assets/styles/btn.scss b/ZR.Vue/src/assets/styles/btn.scss new file mode 100644 index 0000000..e6ba1a8 --- /dev/null +++ b/ZR.Vue/src/assets/styles/btn.scss @@ -0,0 +1,99 @@ +@import './variables.scss'; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/ZR.Vue/src/assets/styles/eladmin.scss b/ZR.Vue/src/assets/styles/eladmin.scss new file mode 100644 index 0000000..9bc5d22 --- /dev/null +++ b/ZR.Vue/src/assets/styles/eladmin.scss @@ -0,0 +1,107 @@ +.head-container { + padding-bottom: 10px; + .filter-item { + display: inline-block; + vertical-align: middle; + margin: 0 3px 10px 0; + input { + height: 30.5px; + line-height: 30.5px; + } + } + .el-button+.el-button { + margin-left: 0 !important; + } + .el-select__caret.el-input__icon.el-icon-arrow-up{ + line-height: 30.5px; + } + .date-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + height: 30.5px !important; + width: 230px !important; + } +} +.el-avatar { + display: inline-block; + text-align: center; + background: #ccc; + color: #fff; + white-space: nowrap; + position: relative; + overflow: hidden; + vertical-align: middle; + width: 32px; + height: 32px; + line-height: 32px; + border-radius: 16px; +} + +.logo-con{ + height: 60px; + padding: 13px 0 0; + img{ + height: 32px; + width: 135px; + display: block; + //margin: 0 auto; + } +} + +#el-login-footer { + height: 40px; + line-height: 40px; + position: fixed; + bottom: 0; + width: 100%; + text-align: center; + color: #fff; + font-family: Arial, serif; + font-size: 12px; + letter-spacing: 1px; +} + +#el-main-footer { + background: none repeat scroll 0 0 white; + border-top: 1px solid #e7eaec; + overflow: hidden; + padding: 10px 6px 0 6px; + height: 33px; + font-size: 0.7rem !important; + color: #7a8b9a; + letter-spacing: 0.8px; + font-family: Arial, sans-serif !important; + position: fixed; + bottom: 0; + z-index: 99; + width: 100%; +} +.eladmin-upload { + border: 1px dashed #c0ccda; + border-radius: 5px; + height: 45px; + line-height: 45px; + width: 368px; +} +.my-blockquote{ + margin: 0 0 10px; + padding: 15px; + line-height: 22px; + border-left: 5px solid #00437B; + border-radius: 0 2px 2px 0; + background-color: #f2f2f2; +} +.my-code{ + position: relative; + padding: 15px; + line-height: 20px; + border-left: 5px solid #ddd; + color: #333; + font-family: Courier New, serif; + font-size: 12px +} + +.el-tabs{ + margin-bottom: 25px; +} diff --git a/ZR.Vue/src/assets/styles/element-ui.scss b/ZR.Vue/src/assets/styles/element-ui.scss new file mode 100644 index 0000000..955d3ca --- /dev/null +++ b/ZR.Vue/src/assets/styles/element-ui.scss @@ -0,0 +1,84 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0px; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} + +// to fix el-date-picker css style +.el-range-separator { + box-sizing: content-box; +} diff --git a/ZR.Vue/src/assets/styles/element-variables.scss b/ZR.Vue/src/assets/styles/element-variables.scss new file mode 100644 index 0000000..8b7a48e --- /dev/null +++ b/ZR.Vue/src/assets/styles/element-variables.scss @@ -0,0 +1,31 @@ +/** +* I think element-ui's default theme color is too light for long-term use. +* So I modified the default color and you can modify it to your liking. +**/ + +/* theme color */ +$--color-primary: #1890ff; +$--color-success: #13ce66; +$--color-warning: #ffba00; +$--color-danger: #ff4949; +// $--color-info: #1E1E1E; + +$--button-font-weight: 400; + +// $--color-text-regular: #1f2d3d; + +$--border-color-light: #dfe4ed; +$--border-color-lighter: #e6ebf5; + +$--table-border:1px solid#dfe6ec; + +/* icon font path, required */ +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +@import "~element-ui/packages/theme-chalk/src/index"; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + theme: $--color-primary; +} diff --git a/ZR.Vue/src/assets/styles/index.scss b/ZR.Vue/src/assets/styles/index.scss new file mode 100644 index 0000000..4133f95 --- /dev/null +++ b/ZR.Vue/src/assets/styles/index.scss @@ -0,0 +1,482 @@ +@import 'variables'; +@import 'mixin'; +@import 'transition'; +@import 'element-ui'; +@import 'sidebar'; +@import 'btn'; +@import 'eladmin'; + +body { + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0 !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container全局样式 +.app-container { + padding: 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.pagination-container { + margin-top: 30px; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +//refine vue-multiselect plugin +.multiselect { + line-height: 16px; +} + +.multiselect--active { + z-index: 1000 !important; +} + + /** 基础通用 **/ +.pt5 { + padding-top: 5px; +} +.pr5 { + padding-right: 5px; +} +.pb5 { + padding-bottom: 5px; +} +.mt5 { + margin-top: 5px; +} +.mr5 { + margin-right: 5px; +} +.mb5 { + margin-bottom: 5px; +} +.mb8 { + margin-bottom: 8px; +} +.ml5 { + margin-left: 5px; +} +.mt10 { + margin-top: 10px; +} +.mr10 { + margin-right: 10px; +} +.mb10 { + margin-bottom: 10px; +} +.ml10 { + margin-left: 10px; +} +.mt20 { + margin-top: 20px; +} +.mr20 { + margin-right: 20px; +} +.mb20 { + margin-bottom: 20px; +} +.ml20 { + margin-left: 20px; +} +.ml { + margin-left: auto; +} +.mr { + margin-right: auto; +} +.mt { + margin-top: auto; +} +.mb { + margin-bottom: auto; +} + +.el-dialog:not(.is-fullscreen){ + margin-top: 6vh !important; +} + +/** 表格布局 **/ +.pagination-container { + position: relative; + height: 25px; + margin-top: 5px; + padding: 10px 20px !important; +} + +.pagination-container .el-pagination { + right: 0; + position: absolute; +} + +.pull-right { + float: right !important; +} + +/* text color */ +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} + +/* image */ +.img-circle { + border-radius: 50%; +} + +.img-lg { + width: 120px; + height: 120px; +} +/* 解决element-ui的table表格控件表头与内容列不对齐问题 */ + +.el-table th.gutter { + display: table-cell !important; +} + +.el-checkbox.mini .el-checkbox__label{ + font-size: 12px !important; +} +.el-checkbox.small .el-checkbox__label { + font-size: 12px !important; +} +.el-checkbox.medium .el-checkbox__label{ + font-size: 14px !important; +} + +.vue-treeselect.mini { + font-size: 12px !important; +} +.vue-treeselect.small { + font-size: 13px !important; +} +.vue-treeselect.medium { + font-size: 14px !important; +} +.vue-treeselect.mini .vue-treeselect__control{ + height: 28px; +} +.vue-treeselect.small .vue-treeselect__control{ + height: 32px; +} +.vue-treeselect.medium .vue-treeselect__control{ + height: 34px; +} +.vue-treeselect.mini .vue-treeselect__single-value{ + line-height: 28px; +} +.vue-treeselect.small .vue-treeselect__single-value{ + line-height: 32px; +} +.vue-treeselect.medium .vue-treeselect__single-value{ + line-height: 34px; +} + + +.el-table .fixed-width .el-button--mini { + color: #409EFF; + padding-left: 0; + padding-right: 0; + width: inherit; +} + +.el-tree-node__content > .el-checkbox { + margin-right: 8px; +} + +.list-group-striped > .list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; +} + +.list-group { + padding-left: 0px; + list-style: none; +} + +.list-group-item { + border-bottom: 1px solid #e7eaec; + border-top: 1px solid #e7eaec; + margin-bottom: -1px; + padding: 11px 0px; + font-size: 13px; +} + +.pull-right { + float: right !important; +} + +.el-card__header { + padding: 14px 15px 7px; + min-height: 40px; +} + +.el-card__body { + padding: 15px 20px 20px 20px; +} + +.card-box { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 10px; +} + +/* button color */ +.el-button--cyan.is-active, +.el-button--cyan:active { + background: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +.el-button--cyan:focus, +.el-button--cyan:hover { + background: #48D1CC; + border-color: #48D1CC; + color: #FFFFFF; +} + +.el-button--cyan { + background-color: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +/* text color */ +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} + +/* image */ +.img-circle { + border-radius: 50%; +} + +.img-lg { + width: 120px; + height: 120px; +} + +.avatar-upload-preview { + position: absolute; + top: 50%; + transform: translate(50%, -50%); + width: 180px; + height: 180px; + border-radius: 50%; + box-shadow: 0 0 4px #ccc; + overflow: hidden; +} + +/* 拖拽列样式 */ +.sortable-ghost{ + opacity: .8; + color: #fff!important; + background: #42b983!important; +} + +.top-right-btn { + position: relative; + float: right; +} diff --git a/ZR.Vue/src/assets/styles/mixin.scss b/ZR.Vue/src/assets/styles/mixin.scss new file mode 100644 index 0000000..06fa061 --- /dev/null +++ b/ZR.Vue/src/assets/styles/mixin.scss @@ -0,0 +1,66 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/ZR.Vue/src/assets/styles/sidebar.scss b/ZR.Vue/src/assets/styles/sidebar.scss new file mode 100644 index 0000000..17381fc --- /dev/null +++ b/ZR.Vue/src/assets/styles/sidebar.scss @@ -0,0 +1,209 @@ +#app { + + .main-container { + min-height: 100%; + transition: margin-left .28s; + margin-left: $sideBarWidth; + position: relative; + } + + .sidebar-container { + transition: width 0.28s; + width: $sideBarWidth !important; + background-color: $menuBg; + height: 100%; + position: fixed; + font-size: 0; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-submenu__title { + &:hover { + background-color: $menuHover !important; + } + } + + .is-active>.el-submenu__title { + color: $subMenuActiveText !important; + } + + & .nest-menu .el-submenu>.el-submenu__title, + & .el-submenu .el-menu-item { + min-width: $sideBarWidth !important; + background-color: $subMenuBg !important; + + &:hover { + background-color: $subMenuHover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-submenu { + overflow: hidden; + + &>.el-submenu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + .el-submenu__icon-arrow { + display: none; + } + } + } + + .el-menu--collapse { + .el-submenu { + &>.el-submenu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-submenu { + min-width: $sideBarWidth !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0; + } + + .sidebar-container { + transition: transform .28s; + width: $sideBarWidth !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$sideBarWidth, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-submenu>.el-submenu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: $menuHover !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/ZR.Vue/src/assets/styles/transition.scss b/ZR.Vue/src/assets/styles/transition.scss new file mode 100644 index 0000000..4cb27cc --- /dev/null +++ b/ZR.Vue/src/assets/styles/transition.scss @@ -0,0 +1,48 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/ZR.Vue/src/assets/styles/variables.scss b/ZR.Vue/src/assets/styles/variables.scss new file mode 100644 index 0000000..5688f25 --- /dev/null +++ b/ZR.Vue/src/assets/styles/variables.scss @@ -0,0 +1,35 @@ +// base color +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +// sidebar +$menuText:#bfcbd9; +$menuActiveText:#409EFF; +$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951 + +$menuBg:#304156; +$menuHover:#263445; + +$subMenuBg:#1f2d3d; +$subMenuHover:#001528; + +$sideBarWidth: 200px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuText: $menuText; + menuActiveText: $menuActiveText; + subMenuActiveText: $subMenuActiveText; + menuBg: $menuBg; + menuHover: $menuHover; + subMenuBg: $subMenuBg; + subMenuHover: $subMenuHover; + sideBarWidth: $sideBarWidth; +} diff --git a/ZR.Vue/src/components/Breadcrumb/index.vue b/ZR.Vue/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..97603c4 --- /dev/null +++ b/ZR.Vue/src/components/Breadcrumb/index.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/ZR.Vue/src/components/DateRangePicker/index.vue b/ZR.Vue/src/components/DateRangePicker/index.vue new file mode 100644 index 0000000..eef79bd --- /dev/null +++ b/ZR.Vue/src/components/DateRangePicker/index.vue @@ -0,0 +1,45 @@ + diff --git a/ZR.Vue/src/components/Editor/index.vue b/ZR.Vue/src/components/Editor/index.vue new file mode 100644 index 0000000..ad35bbf --- /dev/null +++ b/ZR.Vue/src/components/Editor/index.vue @@ -0,0 +1,195 @@ + + + + + \ No newline at end of file diff --git a/ZR.Vue/src/components/Hamburger/index.vue b/ZR.Vue/src/components/Hamburger/index.vue new file mode 100644 index 0000000..368b002 --- /dev/null +++ b/ZR.Vue/src/components/Hamburger/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/ZR.Vue/src/components/HeaderSearch/index.vue b/ZR.Vue/src/components/HeaderSearch/index.vue new file mode 100644 index 0000000..2980c06 --- /dev/null +++ b/ZR.Vue/src/components/HeaderSearch/index.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/ZR.Vue/src/components/IconSelect/index.vue b/ZR.Vue/src/components/IconSelect/index.vue new file mode 100644 index 0000000..b0ec9fa --- /dev/null +++ b/ZR.Vue/src/components/IconSelect/index.vue @@ -0,0 +1,68 @@ + + + + + + diff --git a/ZR.Vue/src/components/IconSelect/requireIcons.js b/ZR.Vue/src/components/IconSelect/requireIcons.js new file mode 100644 index 0000000..99e5c54 --- /dev/null +++ b/ZR.Vue/src/components/IconSelect/requireIcons.js @@ -0,0 +1,11 @@ + +const req = require.context('../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const icons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default icons diff --git a/ZR.Vue/src/components/Pagination/index.vue b/ZR.Vue/src/components/Pagination/index.vue new file mode 100644 index 0000000..c815e13 --- /dev/null +++ b/ZR.Vue/src/components/Pagination/index.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/ZR.Vue/src/components/PanThumb/index.vue b/ZR.Vue/src/components/PanThumb/index.vue new file mode 100644 index 0000000..1bcf417 --- /dev/null +++ b/ZR.Vue/src/components/PanThumb/index.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/ZR.Vue/src/components/ParentView/index.vue b/ZR.Vue/src/components/ParentView/index.vue new file mode 100644 index 0000000..7bf6148 --- /dev/null +++ b/ZR.Vue/src/components/ParentView/index.vue @@ -0,0 +1,3 @@ + diff --git a/ZR.Vue/src/components/RightPanel/index.vue b/ZR.Vue/src/components/RightPanel/index.vue new file mode 100644 index 0000000..fbf27eb --- /dev/null +++ b/ZR.Vue/src/components/RightPanel/index.vue @@ -0,0 +1,149 @@ + + + + + + + diff --git a/ZR.Vue/src/components/RightToolbar/index.vue b/ZR.Vue/src/components/RightToolbar/index.vue new file mode 100644 index 0000000..21376b1 --- /dev/null +++ b/ZR.Vue/src/components/RightToolbar/index.vue @@ -0,0 +1,89 @@ + + + + diff --git a/ZR.Vue/src/components/Screenfull/index.vue b/ZR.Vue/src/components/Screenfull/index.vue new file mode 100644 index 0000000..260c90d --- /dev/null +++ b/ZR.Vue/src/components/Screenfull/index.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/ZR.Vue/src/components/SizeSelect/index.vue b/ZR.Vue/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..e88065b --- /dev/null +++ b/ZR.Vue/src/components/SizeSelect/index.vue @@ -0,0 +1,57 @@ + + + diff --git a/ZR.Vue/src/components/SvgIcon/index.vue b/ZR.Vue/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..e4bf5ad --- /dev/null +++ b/ZR.Vue/src/components/SvgIcon/index.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/ZR.Vue/src/components/ThemePicker/index.vue b/ZR.Vue/src/components/ThemePicker/index.vue new file mode 100644 index 0000000..3879c5a --- /dev/null +++ b/ZR.Vue/src/components/ThemePicker/index.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/ZR.Vue/src/components/TopNav/index.vue b/ZR.Vue/src/components/TopNav/index.vue new file mode 100644 index 0000000..8cc019e --- /dev/null +++ b/ZR.Vue/src/components/TopNav/index.vue @@ -0,0 +1,177 @@ + + + + + diff --git a/ZR.Vue/src/components/UploadImage/index.vue b/ZR.Vue/src/components/UploadImage/index.vue new file mode 100644 index 0000000..87b3ee9 --- /dev/null +++ b/ZR.Vue/src/components/UploadImage/index.vue @@ -0,0 +1,68 @@ + + + + + \ No newline at end of file diff --git a/ZR.Vue/src/components/Zr/Git/index.vue b/ZR.Vue/src/components/Zr/Git/index.vue new file mode 100644 index 0000000..9dad992 --- /dev/null +++ b/ZR.Vue/src/components/Zr/Git/index.vue @@ -0,0 +1,21 @@ + + + diff --git a/ZR.Vue/src/directive/permission/hasPermi.js b/ZR.Vue/src/directive/permission/hasPermi.js new file mode 100644 index 0000000..c507eda --- /dev/null +++ b/ZR.Vue/src/directive/permission/hasPermi.js @@ -0,0 +1,28 @@ + /** + * 操作权限处理 + * + */ + +import store from '@/store' + +export default { + inserted(el, binding, vnode) { + const { value } = binding + const all_permission = "*:*:*"; + const permissions = store.getters && store.getters.permissions + + if (value && value instanceof Array && value.length > 0) { + const permissionFlag = value + + const hasPermissions = permissions.some(permission => { + return all_permission === permission || permissionFlag.includes(permission) + }) + + if (!hasPermissions) { + el.parentNode && el.parentNode.removeChild(el) + } + } else { + throw new Error(`请设置操作权限标签值`) + } + } +} diff --git a/ZR.Vue/src/directive/permission/hasRole.js b/ZR.Vue/src/directive/permission/hasRole.js new file mode 100644 index 0000000..77480db --- /dev/null +++ b/ZR.Vue/src/directive/permission/hasRole.js @@ -0,0 +1,28 @@ + /** + * 角色权限处理 + * + */ + +import store from '@/store' + +export default { + inserted(el, binding, vnode) { + const { value } = binding + const super_admin = "admin"; + const roles = store.getters && store.getters.roles + + if (value && value instanceof Array && value.length > 0) { + const roleFlag = value + + const hasRole = roles.some(role => { + return super_admin === role || roleFlag.includes(role) + }) + + if (!hasRole) { + el.parentNode && el.parentNode.removeChild(el) + } + } else { + throw new Error(`请设置角色权限标签值"`) + } + } +} diff --git a/ZR.Vue/src/directive/permission/index.js b/ZR.Vue/src/directive/permission/index.js new file mode 100644 index 0000000..e3d76d3 --- /dev/null +++ b/ZR.Vue/src/directive/permission/index.js @@ -0,0 +1,15 @@ +import hasRole from './hasRole' +import hasPermi from './hasPermi' + +const install = function(Vue) { + Vue.directive('hasRole', hasRole) + Vue.directive('hasPermi', hasPermi) +} + +if (window.Vue) { + window['hasRole'] = hasRole + window['hasPermi'] = hasPermi + Vue.use(install); // eslint-disable-line +} + +export default install diff --git a/ZR.Vue/src/layout/components/AppMain.vue b/ZR.Vue/src/layout/components/AppMain.vue new file mode 100644 index 0000000..a897638 --- /dev/null +++ b/ZR.Vue/src/layout/components/AppMain.vue @@ -0,0 +1,57 @@ + + + + + + + diff --git a/ZR.Vue/src/layout/components/Navbar.vue b/ZR.Vue/src/layout/components/Navbar.vue new file mode 100644 index 0000000..25997fa --- /dev/null +++ b/ZR.Vue/src/layout/components/Navbar.vue @@ -0,0 +1,184 @@ + + + + + diff --git a/ZR.Vue/src/layout/components/Settings/index.vue b/ZR.Vue/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..90d99df --- /dev/null +++ b/ZR.Vue/src/layout/components/Settings/index.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/ZR.Vue/src/layout/components/Sidebar/FixiOSBug.js b/ZR.Vue/src/layout/components/Sidebar/FixiOSBug.js new file mode 100644 index 0000000..6823726 --- /dev/null +++ b/ZR.Vue/src/layout/components/Sidebar/FixiOSBug.js @@ -0,0 +1,25 @@ +export default { + computed: { + device() { + return this.$store.state.app.device + } + }, + mounted() { + // In order to fix the click on menu on the ios device will trigger the mouseleave bug + this.fixBugIniOS() + }, + methods: { + fixBugIniOS() { + const $subMenu = this.$refs.subMenu + if ($subMenu) { + const handleMouseleave = $subMenu.handleMouseleave + $subMenu.handleMouseleave = (e) => { + if (this.device === 'mobile') { + return + } + handleMouseleave(e) + } + } + } + } +} diff --git a/ZR.Vue/src/layout/components/Sidebar/Item.vue b/ZR.Vue/src/layout/components/Sidebar/Item.vue new file mode 100644 index 0000000..b515f61 --- /dev/null +++ b/ZR.Vue/src/layout/components/Sidebar/Item.vue @@ -0,0 +1,29 @@ + diff --git a/ZR.Vue/src/layout/components/Sidebar/Link.vue b/ZR.Vue/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..530b3d5 --- /dev/null +++ b/ZR.Vue/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,43 @@ + + + diff --git a/ZR.Vue/src/layout/components/Sidebar/Logo.vue b/ZR.Vue/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..1bc9b7a --- /dev/null +++ b/ZR.Vue/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/ZR.Vue/src/layout/components/Sidebar/SidebarItem.vue b/ZR.Vue/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..a307530 --- /dev/null +++ b/ZR.Vue/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,91 @@ + + + diff --git a/ZR.Vue/src/layout/components/Sidebar/index.vue b/ZR.Vue/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..0e2ab99 --- /dev/null +++ b/ZR.Vue/src/layout/components/Sidebar/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/ZR.Vue/src/layout/components/TagsView/ScrollPane.vue b/ZR.Vue/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..34a7e55 --- /dev/null +++ b/ZR.Vue/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/ZR.Vue/src/layout/components/TagsView/index.vue b/ZR.Vue/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..bc6ce18 --- /dev/null +++ b/ZR.Vue/src/layout/components/TagsView/index.vue @@ -0,0 +1,303 @@ + + + + + + + diff --git a/ZR.Vue/src/layout/components/index.js b/ZR.Vue/src/layout/components/index.js new file mode 100644 index 0000000..104bd3a --- /dev/null +++ b/ZR.Vue/src/layout/components/index.js @@ -0,0 +1,5 @@ +export { default as AppMain } from './AppMain' +export { default as Navbar } from './Navbar' +export { default as Settings } from './Settings' +export { default as Sidebar } from './Sidebar/index.vue' +export { default as TagsView } from './TagsView/index.vue' diff --git a/ZR.Vue/src/layout/index.vue b/ZR.Vue/src/layout/index.vue new file mode 100644 index 0000000..889b7dc --- /dev/null +++ b/ZR.Vue/src/layout/index.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/ZR.Vue/src/layout/mixin/ResizeHandler.js b/ZR.Vue/src/layout/mixin/ResizeHandler.js new file mode 100644 index 0000000..e8d0df8 --- /dev/null +++ b/ZR.Vue/src/layout/mixin/ResizeHandler.js @@ -0,0 +1,45 @@ +import store from '@/store' + +const { body } = document +const WIDTH = 992 // refer to Bootstrap's responsive design + +export default { + watch: { + $route(route) { + if (this.device === 'mobile' && this.sidebar.opened) { + store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } + }, + beforeMount() { + window.addEventListener('resize', this.$_resizeHandler) + }, + beforeDestroy() { + window.removeEventListener('resize', this.$_resizeHandler) + }, + mounted() { + const isMobile = this.$_isMobile() + if (isMobile) { + store.dispatch('app/toggleDevice', 'mobile') + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_isMobile() { + const rect = body.getBoundingClientRect() + return rect.width - 1 < WIDTH + }, + $_resizeHandler() { + if (!document.hidden) { + const isMobile = this.$_isMobile() + store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') + + if (isMobile) { + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + } + } + } +} diff --git a/ZR.Vue/src/main.js b/ZR.Vue/src/main.js new file mode 100644 index 0000000..912ad47 --- /dev/null +++ b/ZR.Vue/src/main.js @@ -0,0 +1,75 @@ +import Vue from 'vue' + +import Cookies from 'js-cookie' + +import 'normalize.css/normalize.css' // a modern alternative to CSS resets + +import Element from 'element-ui' +import './assets/styles/element-variables.scss' + +import '@/assets/styles/index.scss' // global css + +import App from './App' +import store from './store' +import router from './router' +import permission from './directive/permission' + +import './assets/icons' // icon +import './permission' // permission control +import { getDicts } from "@/api/system/dict/data"; +// import { getConfigKey } from "@/api/system/config"; +import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree } from "@/utils/ruoyi"; +import Pagination from "@/components/Pagination"; +//自定义表格工具扩展 +import RightToolbar from "@/components/RightToolbar" + +// 全局方法挂载 +Vue.prototype.getDicts = getDicts +// Vue.prototype.getConfigKey = getConfigKey +Vue.prototype.parseTime = parseTime +Vue.prototype.resetForm = resetForm +Vue.prototype.addDateRange = addDateRange +Vue.prototype.selectDictLabel = selectDictLabel +Vue.prototype.selectDictLabels = selectDictLabels +Vue.prototype.download = download +Vue.prototype.handleTree = handleTree + +Vue.prototype.msgSuccess = function (msg) { + this.$message({ showClose: true, message: msg, type: "success" }); +} + +Vue.prototype.msgError = function (msg) { + this.$message({ showClose: true, message: msg, type: "error" }); +} + +Vue.prototype.msgInfo = function (msg) { + this.$message.info(msg); +} + +// 全局组件挂载 +Vue.component('Pagination', Pagination) +Vue.component('RightToolbar', RightToolbar) + +Vue.use(permission) + +/** + * If you don't want to use mock-server + * you want to use MockJs for mock api + * you can execute: mockXHR() + * + * Currently MockJs will be used in the production environment, + * please remove it before going online! ! ! + */ + +Vue.use(Element, { + size: Cookies.get('size') || 'mini' // set element-ui default size +}) + +Vue.config.productionTip = false + +new Vue({ + el: '#app', + router, + store, + render: h => h(App) +}) diff --git a/ZR.Vue/src/permission.js b/ZR.Vue/src/permission.js new file mode 100644 index 0000000..2b3b080 --- /dev/null +++ b/ZR.Vue/src/permission.js @@ -0,0 +1,73 @@ +import router from './router' +import store from './store' +import { Message } from 'element-ui' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import { getToken } from '@/utils/auth' + +NProgress.configure({ showSpinner: false }) + +const whiteList = ['/login', '/auth-redirect', '/bind', '/register', '/demo'] + +router.beforeEach((to, from, next) => { + NProgress.start() + console.log(to.path); + const hasToken = getToken() + + if (hasToken) { + /* has token*/ + if (to.path === '/login') { + next({ path: '/' }) + NProgress.done() + } else { + if (store.getters.roles.length === 0) { + + // 判断当前用户是否已拉取完user_info信息 + store.dispatch('GetInfo').then(res => { + //console.log('拉取userInfo', JSON.stringify(res)) + // 拉取user_info + const roles = res.data.roles + store.dispatch('GenerateRoutes', { roles }).then(accessRoutes => { + + // 测试 默认静态页面 + // store.dispatch('permission/generateRoutes', { roles }).then(accessRoutes => { + // 根据roles权限生成可访问的路由表 + router.addRoutes(accessRoutes) // 动态添加可访问路由表 + next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 + }) + next() + }).catch(err => { + console.error(err) + //这部不能少,否则会出现死循环 + store.dispatch('FedLogOut').then(() => { + Message.error(err) + next({ path: '/' }) + }) + next(`/login?redirect=${to.path}`) + }) + } else { + next() + // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓ + // if (hasPermission(store.getters.roles, to.meta.roles)) { + // next() + // } else { + // next({ path: '/401', replace: true, query: { noGoBack: true }}) + // } + // 可删 ↑ + } + } + } else { + // 没有token + if (whiteList.indexOf(to.path) !== -1) { + // 在免登录白名单,直接进入 + next() + } else { + next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 + NProgress.done() + } + } +}) + +router.afterEach(() => { + NProgress.done() +}) diff --git a/ZR.Vue/src/router/index.js b/ZR.Vue/src/router/index.js new file mode 100644 index 0000000..d0fd641 --- /dev/null +++ b/ZR.Vue/src/router/index.js @@ -0,0 +1,120 @@ +import Vue from 'vue' +import Router from 'vue-router' + +Vue.use(Router) + +/* Layout */ +import Layout from '@/layout' + +/** + * Note: 路由配置项 + * + * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 + * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 + * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 + * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 + * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + * name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题 + * meta : { + noCache: true // 如果设置为true,则不会被 缓存(默认 false) + title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 + icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg + breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 + } + */ + +// 公共路由 +export const constantRoutes = [ + { + path: '/redirect', + component: Layout, + hidden: true, + children: [ + { + path: '/redirect/:path(.*)', + component: (resolve) => require(['@/views/redirect'], resolve) + } + ] + }, + { + path: '/login', + component: (resolve) => require(['@/views/login'], resolve), + hidden: true + }, + { + path: '/demo', + component: (resolve) => require(['@/views/demo'], resolve), + hidden: true + }, + { + path: '/404', + component: (resolve) => require(['@/views/error/404'], resolve), + hidden: true + }, + { + path: '/401', + component: (resolve) => require(['@/views/error/401'], resolve), + hidden: true + }, + { + path: '', + component: Layout, + redirect: 'index', + children: [ + { + path: 'index', + component: (resolve) => require(['@/views/index'], resolve), + name: '首页', + meta: { title: '首页', icon: 'icon1', noCache: true, affix: true } + } + ], + hidden: true + }, + // { + // path: '', + // component: Layout, + // redirect: 'index', + // children: [ + // { + // path: 'dashboard', + // component: (resolve) => require(['@/views/index_v1'], resolve), + // name: '控制台', + // meta: { title: '控制台', icon: 'dashboard', noCache: true, affix: true } + // } + // ] + // }, + { + path: '/user', + component: Layout, + hidden: true, + redirect: 'noredirect', + children: [ + { + path: 'profile', + component: (resolve) => require(['@/views/system/user/profile/index'], resolve), + name: 'Profile', + meta: { title: '个人中心', icon: 'user' } + } + ] + }, + { + path: '/dict', + component: Layout, + hidden: true, + children: [ + { + path: 'type/data/:dictId(\\d+)', + component: (resolve) => require(['@/views/system/dict/data'], resolve), + name: 'Data', + meta: { title: '字典数据', icon: '' } + } + ] + } +] + +export default new Router({ + mode: 'history', // 去掉url中的# + scrollBehavior: () => ({ y: 0 }), + routes: constantRoutes +}) diff --git a/ZR.Vue/src/settings.js b/ZR.Vue/src/settings.js new file mode 100644 index 0000000..2d837ad --- /dev/null +++ b/ZR.Vue/src/settings.js @@ -0,0 +1,31 @@ +module.exports = { + title: 'ZrAdmin.NET', + + /** + * 是否系统布局配置 + */ + showSettings: false, + + /** + * 是否显示 tagsView + */ + tagsView: true, + + /** + * 是否固定头部 + */ + fixedHeader: false, + + /** + * 是否显示logo + */ + sidebarLogo: true, + + /** + * @type {string | array} 'production' | ['production', 'development'] + * @description Need show err logs component. + * The default is only used in the production env + * If you want to also use it in dev, you can pass ['production', 'development'] + */ + errorLog: 'production' +} diff --git a/ZR.Vue/src/store/getters.js b/ZR.Vue/src/store/getters.js new file mode 100644 index 0000000..b6847db --- /dev/null +++ b/ZR.Vue/src/store/getters.js @@ -0,0 +1,16 @@ +const getters = { + sidebar: state => state.app.sidebar, + size: state => state.app.size, + device: state => state.app.device, + visitedViews: state => state.tagsView.visitedViews, + cachedViews: state => state.tagsView.cachedViews, + token: state => state.user.token, + avatar: state => state.user.avatar, + name: state => state.user.name, + introduction: state => state.user.introduction, + roles: state => state.user.roles, + permissions: state => state.user.permissions, + permission_routes: state => state.permission.routes, + userinfo: state => state.user.userInfo, +} +export default getters diff --git a/ZR.Vue/src/store/index.js b/ZR.Vue/src/store/index.js new file mode 100644 index 0000000..300a9c8 --- /dev/null +++ b/ZR.Vue/src/store/index.js @@ -0,0 +1,30 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import app from './modules/app' +import user from './modules/user' +import tagsView from './modules/tagsView' +import permission from './modules/permission' +import settings from './modules/settings' +import getters from './getters' + +Vue.use(Vuex) + +/** + * 当前用户数据 + */ +const state = { + appVersionInfo: '' +} +const store = new Vuex.Store({ + modules: { + app, + user, + tagsView, + permission, + settings + }, + state: state,//这里放全局参数 + getters +}) + +export default store diff --git a/ZR.Vue/src/store/modules/app.js b/ZR.Vue/src/store/modules/app.js new file mode 100644 index 0000000..45d89bb --- /dev/null +++ b/ZR.Vue/src/store/modules/app.js @@ -0,0 +1,56 @@ +import Cookies from 'js-cookie' + +const state = { + sidebar: { + opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, + withoutAnimation: false + }, + device: 'desktop', + size: Cookies.get('size') || 'medium' +} + +const mutations = { + TOGGLE_SIDEBAR: state => { + state.sidebar.opened = !state.sidebar.opened + state.sidebar.withoutAnimation = false + if (state.sidebar.opened) { + Cookies.set('sidebarStatus', 1) + } else { + Cookies.set('sidebarStatus', 0) + } + }, + CLOSE_SIDEBAR: (state, withoutAnimation) => { + Cookies.set('sidebarStatus', 0) + state.sidebar.opened = false + state.sidebar.withoutAnimation = withoutAnimation + }, + TOGGLE_DEVICE: (state, device) => { + state.device = device + }, + SET_SIZE: (state, size) => { + state.size = size + Cookies.set('size', size) + } +} + +const actions = { + toggleSideBar({ commit }) { + commit('TOGGLE_SIDEBAR') + }, + closeSideBar({ commit }, { withoutAnimation }) { + commit('CLOSE_SIDEBAR', withoutAnimation) + }, + toggleDevice({ commit }, device) { + commit('TOGGLE_DEVICE', device) + }, + setSize({ commit }, size) { + commit('SET_SIZE', size) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/ZR.Vue/src/store/modules/permission.js b/ZR.Vue/src/store/modules/permission.js new file mode 100644 index 0000000..2cc1253 --- /dev/null +++ b/ZR.Vue/src/store/modules/permission.js @@ -0,0 +1,58 @@ +import { constantRoutes } from '@/router' +import { getRouters } from '@/api/system/menu' +import Layout from '@/layout/index' +import ParentView from '@/components/ParentView'; + +const permission = { + state: { + routes: [], + addRoutes: [] + }, + mutations: { + SET_ROUTES: (state, routes) => { + state.addRoutes = routes + state.routes = constantRoutes.concat(routes) + } + }, + actions: { + // 生成路由 + GenerateRoutes({ commit }) { + return new Promise(resolve => { + // 向后端请求路由数据 + getRouters().then(res => { + const accessedRoutes = filterAsyncRouter(res.data) + // accessedRoutes.push({ path: '*', redirect: '/404', hidden: true }) + commit('SET_ROUTES', accessedRoutes) + resolve(accessedRoutes) + }) + }) + } + } +} + +// 遍历后台传来的路由字符串,转换为组件对象 +function filterAsyncRouter(asyncRouterMap) { + return asyncRouterMap.filter(route => { + // console.log(JSON.stringify(route)) + if (route.component) { + // Layout ParentView 组件特殊处理 + if (route.component === 'Layout') { + route.component = Layout + } else if (route.component === 'ParentView') { + route.component = ParentView + } else { + route.component = loadView(route.component) + } + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children) + } + return true + }) +} + +export const loadView = (view) => { // 路由懒加载 + return (resolve) => require([`@/views/${view}`], resolve) +} + +export default permission diff --git a/ZR.Vue/src/store/modules/settings.js b/ZR.Vue/src/store/modules/settings.js new file mode 100644 index 0000000..a1152a9 --- /dev/null +++ b/ZR.Vue/src/store/modules/settings.js @@ -0,0 +1,34 @@ +import variables from '@/assets/styles/element-variables.scss' +import defaultSettings from '@/settings' + +const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings + +const state = { + theme: variables.theme, + showSettings: showSettings, + tagsView: tagsView, + fixedHeader: fixedHeader, + sidebarLogo: sidebarLogo +} + +const mutations = { + CHANGE_SETTING: (state, { key, value }) => { + if (state.hasOwnProperty(key)) { + state[key] = value + } + } +} + +const actions = { + changeSetting({ commit }, data) { + commit('CHANGE_SETTING', data) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/ZR.Vue/src/store/modules/tagsView.js b/ZR.Vue/src/store/modules/tagsView.js new file mode 100644 index 0000000..2e30d15 --- /dev/null +++ b/ZR.Vue/src/store/modules/tagsView.js @@ -0,0 +1,159 @@ +const state = { + visitedViews: [], + cachedViews: [] +} + +const mutations = { + ADD_VISITED_VIEW: (state, view) => { + if (state.visitedViews.some(v => v.path === view.path)) return + state.visitedViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + ADD_CACHED_VIEW: (state, view) => { + if (state.cachedViews.includes(view.name)) return + if (!view.meta.noCache) { + state.cachedViews.push(view.name) + } + }, + + DEL_VISITED_VIEW: (state, view) => { + for (const [i, v] of state.visitedViews.entries()) { + if (v.path === view.path) { + state.visitedViews.splice(i, 1) + break + } + } + }, + DEL_CACHED_VIEW: (state, view) => { + const index = state.cachedViews.indexOf(view.name) + index > -1 && state.cachedViews.splice(index, 1) + }, + + DEL_OTHERS_VISITED_VIEWS: (state, view) => { + state.visitedViews = state.visitedViews.filter(v => { + return v.meta.affix || v.path === view.path + }) + }, + DEL_OTHERS_CACHED_VIEWS: (state, view) => { + const index = state.cachedViews.indexOf(view.name) + if (index > -1) { + state.cachedViews = state.cachedViews.slice(index, index + 1) + } else { + state.cachedViews = [] + } + }, + + DEL_ALL_VISITED_VIEWS: state => { + // keep affix tags + const affixTags = state.visitedViews.filter(tag => tag.meta.affix) + state.visitedViews = affixTags + }, + DEL_ALL_CACHED_VIEWS: state => { + state.cachedViews = [] + }, + + UPDATE_VISITED_VIEW: (state, view) => { + for (let v of state.visitedViews) { + if (v.path === view.path) { + v = Object.assign(v, view) + break + } + } + } +} + +const actions = { + addView({ dispatch }, view) { + dispatch('addVisitedView', view) + dispatch('addCachedView', view) + }, + addVisitedView({ commit }, view) { + commit('ADD_VISITED_VIEW', view) + }, + addCachedView({ commit }, view) { + commit('ADD_CACHED_VIEW', view) + }, + + delView({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delVisitedView', view) + dispatch('delCachedView', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delVisitedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_VISITED_VIEW', view) + resolve([...state.visitedViews]) + }) + }, + delCachedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_CACHED_VIEW', view) + resolve([...state.cachedViews]) + }) + }, + + delOthersViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delOthersVisitedViews', view) + dispatch('delOthersCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delOthersVisitedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_VISITED_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delOthersCachedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_CACHED_VIEWS', view) + resolve([...state.cachedViews]) + }) + }, + + delAllViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delAllVisitedViews', view) + dispatch('delAllCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delAllVisitedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_VISITED_VIEWS') + resolve([...state.visitedViews]) + }) + }, + delAllCachedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_CACHED_VIEWS') + resolve([...state.cachedViews]) + }) + }, + + updateVisitedView({ commit }, view) { + commit('UPDATE_VISITED_VIEW', view) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/ZR.Vue/src/store/modules/user.js b/ZR.Vue/src/store/modules/user.js new file mode 100644 index 0000000..cc944ee --- /dev/null +++ b/ZR.Vue/src/store/modules/user.js @@ -0,0 +1,110 @@ +import { login, logOut, getInfo } from '@/api/login' +import { getToken, setToken, removeToken } from '@/utils/auth' + +const user = { + state: { + userInfo: '', + token: getToken(), + name: '', + avatar: '', + roles: [], + permissions: [] + }, + + mutations: { + SET_TOKEN: (state, token) => { + state.token = token + }, + SET_NAME: (state, name) => { + state.name = name + }, + SET_AVATAR: (state, avatar) => { + state.avatar = avatar + }, + SET_ROLES: (state, roles) => { + state.roles = roles + }, + SET_PERMISSIONS: (state, permissions) => { + state.permissions = permissions + }, + SET_USERINFO: (state, value) => { + state.userInfo = value + } + }, + + actions: { + // 登录 + Login({ commit }, userInfo) { + const username = userInfo.username.trim() + const password = userInfo.password + const code = userInfo.code + const uuid = userInfo.uuid + return new Promise((resolve, reject) => { + login(username, password, code, uuid).then(res => { + if (res.code == 200) { + //提交上面的mutaions方法 + setToken(res.data) + commit('SET_TOKEN', res.data) + resolve()//then处理 + } else { + reject(res)//catch处理 + } + }) + }) + }, + + // 获取用户信息 + GetInfo({ commit, state }) { + return new Promise((resolve, reject) => { + getInfo(state.token).then(res => { + if (!res || res.code != 200) { + return reject(res.msg) + } + + const data = res.data + const avatar = data.user.avatar == "" ? require("@/assets/image/profile.jpg") : data.user.avatar; + + if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组 + commit('SET_ROLES', data.roles) + commit('SET_PERMISSIONS', data.permissions) + } else { + commit('SET_ROLES', ['ROLE_DEFAULT']) + } + + commit('SET_NAME', data.user.nickName) + commit('SET_AVATAR', avatar) + commit('SET_USERINFO', data.user)//新加 + resolve(res) + }).catch(error => { + reject(error) + }) + }) + }, + + // 退出系统 + LogOut({ commit, state }) { + return new Promise((resolve, reject) => { + logOut(state.token).then(() => { + removeToken()// 必须先移除token + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + commit('SET_PERMISSIONS', []) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 前端 登出 + FedLogOut({ commit }) { + return new Promise(resolve => { + commit('SET_TOKEN', '') + removeToken() + resolve() + }) + } + } +} + +export default user diff --git a/ZR.Vue/src/utils/auth.js b/ZR.Vue/src/utils/auth.js new file mode 100644 index 0000000..6163854 --- /dev/null +++ b/ZR.Vue/src/utils/auth.js @@ -0,0 +1,17 @@ + +import Cookies from 'js-cookie' + +const TokenKey = 'ZR-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + console.log('set token=' + token) + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/ZR.Vue/src/utils/datetime.js b/ZR.Vue/src/utils/datetime.js new file mode 100644 index 0000000..67f808a --- /dev/null +++ b/ZR.Vue/src/utils/datetime.js @@ -0,0 +1,216 @@ +/* eslint-disable */ + +/** + * Date对象的补充函数,包括类似Python中的strftime() + * 阿债 https://gitee.com/azhai/datetime.js + */ + +Date.prototype.toMidnight = function() { + this.setHours(0) + this.setMinutes(0) + this.setSeconds(0) + this.setMilliseconds(0) + return this +} + +Date.prototype.daysAgo = function(days, midnight) { + days = days ? days - 0 : 0 + const date = new Date(this.getTime() - days * 8.64E7) + return midnight ? date.toMidnight() : date +} + +Date.prototype.monthBegin = function(offset) { + offset = offset ? offset - 0 : 0 + const days = this.getDate() - 1 - offset + return this.daysAgo(days, true) +} + +Date.prototype.quarterBegin = function() { + const month = this.getMonth() - this.getMonth() % 3 + return new Date(this.getFullYear(), month, 1).toMidnight() +} + +Date.prototype.yearBegin = function() { + return new Date(this.getFullYear(), 0, 1).toMidnight() +} + +Date.prototype.strftime = function(format, local) { + if (!format) { + const str = new Date(this.getTime() + 2.88E7).toISOString() + return str.substr(0, 16).replace('T', ' ') + } + local = local && local.startsWith('zh') ? 'zh' : 'en' + const padZero = function(str, len) { + const pads = len - str.toString().length + return (pads && pads > 0 ? '0'.repeat(pads) : '') + str + } + format = format.replace('%F', '%Y-%m-%d') + format = format.replace(/%D|%x/, '%m/%d/%y') + format = format.replace(/%T|%X/, '%H:%M:%S') + format = format.replace('%R', '%H:%M') + format = format.replace('%r', '%H:%M:%S %p') + format = format.replace('%c', '%a %b %e %H:%M:%S %Y') + const _this = this + return format.replace(/%[A-Za-z%]/g, function(f) { + let ans = f + switch (f) { + case '%%': + ans = '%' + break + + case '%Y': + case '%G': + ans = _this.getFullYear() + break + + case '%y': + ans = _this.getFullYear() % 100 + break + + case '%C': + ans = _this.getFullYear() / 100 + break + + case '%m': + case '%n': + ans = _this.getMonth() + 1 + break + + case '%B': + local = local.startsWith('en') ? 'english' : local + + case '%b': + const m = _this.getMonth() + ans = local_labels.monthes[local][m] + break + + case '%d': + case '%e': + ans = _this.getDate() + break + + case '%j': + ans = _this.getDaysOfYear() + break + + case '%U': + case '%W': + const ws = _this.getWeeksOfYear(f === '%W') + ans = padZero(ws, 2) + break + + case '%w': + ans = _this.getDay() + + case '%u': + ans = ans === 0 ? 7 : ans + break + + case '%A': + local = local.startsWith('en') ? 'english' : local + + case '%a': + const d = _this.getDay() + ans = local_labels.weekdays[local][d] + break + + case '%H': + case '%k': + ans = _this.getHours() + break + + case '%I': + case '%l': + ans = _this.getHours() + ans = ans % 12 + break + + case '%M': + ans = _this.getMinutes() + break + + case '%S': + ans = _this.getSeconds() + break + + case '%s': + ans = parseInt(_this.getTime() / 1E3) + break + + case '%f': + const ms = _this.getMilliseconds() + ans = padZero(ms * 1E3, 6) + break + + case '%P': + local = local.startsWith('en') ? 'english' : local + + case '%p': + const h = _this.getHours() + ans = local_labels.meridians[local][h < 12 ? 0 : 1] + break + + case '%z': + let tzo = _this.getTimezoneOffset() + const sign = tzo < 0 ? '-' : '+' + tzo = Math.abs(tzo) + const ho = padZero(tzo / 60, 2) + const mo = padZero(tzo % 60, 2) + ans = sign + ho + mo + break + + default: + break + } + if (f === '%C' || f === '%y' || f === '%m' || f === '%d' || f === '%H' || f === '%M' || f === '%S') { + ans = padZero(ans, 2) + } + return ans.toString() + }) +} + +Date.prototype.humanize = function(local) { + local = local && local.startsWith('zh') ? 'zh' : 'en' + const result = this.strftime('', local) + const days = (Date.today() - this.toMidnight().getTime()) / 8.64E7 + if (days <= -10 || days >= 10) { + return result + } + const labels = local_labels.dayagos[local] + let lbl = '' + if (days === 0 || days === 1) { + lbl = labels[days] + } else if (days === -1) { + lbl = labels[2] + } else if (days >= 2) { + lbl = days + labels[3] + } else { + lbl = days + labels[4] + } + return lbl + result.substr(10, 6) +} + +const local_labels = { + monthes: { + english: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + zh: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'] + }, + weekdays: { + english: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + en: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + zh: ['日', '一', '二', '三', '四', '五', '六'] + }, + meridians: { + english: ['a.m.', 'p.m.'], + en: ['AM', 'PM'], + zh: ['上午', '下午'] + }, + dayagos: { + english: ['Today', 'Yesterday', 'Tomorrow', ' days ago', ' days late'], + en: ['Today', 'Yesterday', 'Tomorrow', ' days ago', ' days late'], + zh: ['今天', '昨天', '明天', '天前', '天后'] + } +} + +export default Date diff --git a/ZR.Vue/src/utils/errorCode.js b/ZR.Vue/src/utils/errorCode.js new file mode 100644 index 0000000..d2111ee --- /dev/null +++ b/ZR.Vue/src/utils/errorCode.js @@ -0,0 +1,6 @@ +export default { + '401': '认证失败,无法访问系统资源', + '403': '当前操作没有权限', + '404': '访问资源不存在', + 'default': '系统未知错误,请反馈给管理员' +} diff --git a/ZR.Vue/src/utils/generator/config.js b/ZR.Vue/src/utils/generator/config.js new file mode 100644 index 0000000..7abf227 --- /dev/null +++ b/ZR.Vue/src/utils/generator/config.js @@ -0,0 +1,438 @@ +export const formConf = { + formRef: 'elForm', + formModel: 'formData', + size: 'medium', + labelPosition: 'right', + labelWidth: 100, + formRules: 'rules', + gutter: 15, + disabled: false, + span: 24, + formBtns: true +} + +export const inputComponents = [ + { + label: '单行文本', + tag: 'el-input', + tagIcon: 'input', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': '', + 'suffix-icon': '', + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '多行文本', + tag: 'el-input', + tagIcon: 'textarea', + type: 'textarea', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + labelWidth: null, + autosize: { + minRows: 4, + maxRows: 4 + }, + style: { width: '100%' }, + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '密码', + tag: 'el-input', + tagIcon: 'password', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + 'show-password': true, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': '', + 'suffix-icon': '', + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '计数器', + tag: 'el-input-number', + tagIcon: 'number', + placeholder: '', + defaultValue: undefined, + span: 24, + labelWidth: null, + min: undefined, + max: undefined, + step: undefined, + 'step-strictly': false, + precision: undefined, + 'controls-position': '', + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input-number' + } +] + +export const selectComponents = [ + { + label: '下拉选择', + tag: 'el-select', + tagIcon: 'select', + placeholder: '请选择', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + disabled: false, + required: true, + filterable: false, + multiple: false, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/select' + }, + { + label: '级联选择', + tag: 'el-cascader', + tagIcon: 'cascader', + placeholder: '请选择', + defaultValue: [], + span: 24, + labelWidth: null, + style: { width: '100%' }, + props: { + props: { + multiple: false + } + }, + 'show-all-levels': true, + disabled: false, + clearable: true, + filterable: false, + required: true, + options: [{ + id: 1, + value: 1, + label: '选项1', + children: [{ + id: 2, + value: 2, + label: '选项1-1' + }] + }], + dataType: 'dynamic', + labelKey: 'label', + valueKey: 'value', + childrenKey: 'children', + separator: '/', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/cascader' + }, + { + label: '单选框组', + tag: 'el-radio-group', + tagIcon: 'radio', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: {}, + optionType: 'default', + border: false, + size: 'medium', + disabled: false, + required: true, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/radio' + }, + { + label: '多选框组', + tag: 'el-checkbox-group', + tagIcon: 'checkbox', + defaultValue: [], + span: 24, + labelWidth: null, + style: {}, + optionType: 'default', + border: false, + size: 'medium', + disabled: false, + required: true, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/checkbox' + }, + { + label: '开关', + tag: 'el-switch', + tagIcon: 'switch', + defaultValue: false, + span: 24, + labelWidth: null, + style: {}, + disabled: false, + required: true, + 'active-text': '', + 'inactive-text': '', + 'active-color': null, + 'inactive-color': null, + 'active-value': true, + 'inactive-value': false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/switch' + }, + { + label: '滑块', + tag: 'el-slider', + tagIcon: 'slider', + defaultValue: null, + span: 24, + labelWidth: null, + disabled: false, + required: true, + min: 0, + max: 100, + step: 1, + 'show-stops': false, + range: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/slider' + }, + { + label: '时间选择', + tag: 'el-time-picker', + tagIcon: 'time', + placeholder: '请选择', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + 'picker-options': { + selectableRange: '00:00:00-23:59:59' + }, + format: 'HH:mm:ss', + 'value-format': 'HH:mm:ss', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' + }, + { + label: '时间范围', + tag: 'el-time-picker', + tagIcon: 'time-range', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + 'is-range': true, + 'range-separator': '至', + 'start-placeholder': '开始时间', + 'end-placeholder': '结束时间', + format: 'HH:mm:ss', + 'value-format': 'HH:mm:ss', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' + }, + { + label: '日期选择', + tag: 'el-date-picker', + tagIcon: 'date', + placeholder: '请选择', + defaultValue: null, + type: 'date', + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + format: 'yyyy-MM-dd', + 'value-format': 'yyyy-MM-dd', + readonly: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' + }, + { + label: '日期范围', + tag: 'el-date-picker', + tagIcon: 'date-range', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + type: 'daterange', + 'range-separator': '至', + 'start-placeholder': '开始日期', + 'end-placeholder': '结束日期', + disabled: false, + clearable: true, + required: true, + format: 'yyyy-MM-dd', + 'value-format': 'yyyy-MM-dd', + readonly: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' + }, + { + label: '评分', + tag: 'el-rate', + tagIcon: 'rate', + defaultValue: 0, + span: 24, + labelWidth: null, + style: {}, + max: 5, + 'allow-half': false, + 'show-text': false, + 'show-score': false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/rate' + }, + { + label: '颜色选择', + tag: 'el-color-picker', + tagIcon: 'color', + defaultValue: null, + labelWidth: null, + 'show-alpha': false, + 'color-format': '', + disabled: false, + required: true, + size: 'medium', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/color-picker' + }, + { + label: '上传', + tag: 'el-upload', + tagIcon: 'upload', + action: 'https://jsonplaceholder.typicode.com/posts/', + defaultValue: null, + labelWidth: null, + disabled: false, + required: true, + accept: '', + name: 'file', + 'auto-upload': true, + showTip: false, + buttonText: '点击上传', + fileSize: 2, + sizeUnit: 'MB', + 'list-type': 'text', + multiple: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/upload' + } +] + +export const layoutComponents = [ + { + layout: 'rowFormItem', + tagIcon: 'row', + type: 'default', + justify: 'start', + align: 'top', + label: '行容器', + layoutTree: true, + children: [], + document: 'https://element.eleme.cn/#/zh-CN/component/layout' + }, + { + layout: 'colFormItem', + label: '按钮', + changeTag: true, + labelWidth: null, + tag: 'el-button', + tagIcon: 'button', + span: 24, + default: '主要按钮', + type: 'primary', + icon: 'el-icon-search', + size: 'medium', + disabled: false, + document: 'https://element.eleme.cn/#/zh-CN/component/button' + } +] + +// 组件rule的触发方式,无触发方式的组件不生成rule +export const trigger = { + 'el-input': 'blur', + 'el-input-number': 'blur', + 'el-select': 'change', + 'el-radio-group': 'change', + 'el-checkbox-group': 'change', + 'el-cascader': 'change', + 'el-time-picker': 'change', + 'el-date-picker': 'change', + 'el-rate': 'change' +} diff --git a/ZR.Vue/src/utils/generator/css.js b/ZR.Vue/src/utils/generator/css.js new file mode 100644 index 0000000..c1c62e6 --- /dev/null +++ b/ZR.Vue/src/utils/generator/css.js @@ -0,0 +1,18 @@ +const styles = { + 'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}', + 'el-upload': '.el-upload__tip{line-height: 1.2;}' +} + +function addCss(cssList, el) { + const css = styles[el.tag] + css && cssList.indexOf(css) === -1 && cssList.push(css) + if (el.children) { + el.children.forEach(el2 => addCss(cssList, el2)) + } +} + +export function makeUpCss(conf) { + const cssList = [] + conf.fields.forEach(el => addCss(cssList, el)) + return cssList.join('\n') +} diff --git a/ZR.Vue/src/utils/generator/drawingDefalut.js b/ZR.Vue/src/utils/generator/drawingDefalut.js new file mode 100644 index 0000000..09f133c --- /dev/null +++ b/ZR.Vue/src/utils/generator/drawingDefalut.js @@ -0,0 +1,29 @@ +export default [ + { + layout: 'colFormItem', + tagIcon: 'input', + label: '手机号', + vModel: 'mobile', + formId: 6, + tag: 'el-input', + placeholder: '请输入手机号', + defaultValue: '', + span: 24, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': 'el-icon-mobile', + 'suffix-icon': '', + maxlength: 11, + 'show-word-limit': true, + readonly: false, + disabled: false, + required: true, + changeTag: true, + regList: [{ + pattern: '/^1(3|4|5|7|8|9)\\d{9}$/', + message: '手机号格式错误' + }] + } +] diff --git a/ZR.Vue/src/utils/generator/html.js b/ZR.Vue/src/utils/generator/html.js new file mode 100644 index 0000000..ebf628d --- /dev/null +++ b/ZR.Vue/src/utils/generator/html.js @@ -0,0 +1,359 @@ +/* eslint-disable max-len */ +import { trigger } from './config' + +let confGlobal +let someSpanIsNot24 + +export function dialogWrapper(str) { + return ` + ${str} +
+ 取消 + 确定 +
+
` +} + +export function vueTemplate(str) { + return `` +} + +export function vueScript(str) { + return `` +} + +export function cssStyle(cssStr) { + return `` +} + +function buildFormTemplate(conf, child, type) { + let labelPosition = '' + if (conf.labelPosition !== 'right') { + labelPosition = `label-position="${conf.labelPosition}"` + } + const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : '' + let str = ` + ${child} + ${buildFromBtns(conf, type)} + ` + if (someSpanIsNot24) { + str = ` + ${str} + ` + } + return str +} + +function buildFromBtns(conf, type) { + let str = '' + if (conf.formBtns && type === 'file') { + str = ` + 提交 + 重置 + ` + if (someSpanIsNot24) { + str = ` + ${str} + ` + } + } + return str +} + +// span不为24的用el-col包裹 +function colWrapper(element, str) { + if (someSpanIsNot24 || element.span !== 24) { + return ` + ${str} + ` + } + return str +} + +const layouts = { + colFormItem(element) { + let labelWidth = '' + if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) { + labelWidth = `label-width="${element.labelWidth}px"` + } + const required = !trigger[element.tag] && element.required ? 'required' : '' + const tagDom = tags[element.tag] ? tags[element.tag](element) : null + let str = ` + ${tagDom} + ` + str = colWrapper(element, str) + return str + }, + rowFormItem(element) { + const type = element.type === 'default' ? '' : `type="${element.type}"` + const justify = element.type === 'default' ? '' : `justify="${element.justify}"` + const align = element.type === 'default' ? '' : `align="${element.align}"` + const gutter = element.gutter ? `gutter="${element.gutter}"` : '' + const children = element.children.map(el => layouts[el.layout](el)) + let str = ` + ${children.join('\n')} + ` + str = colWrapper(element, str) + return str + } +} + +const tags = { + 'el-button': el => { + const { + tag, disabled + } = attrBuilder(el) + const type = el.type ? `type="${el.type}"` : '' + const icon = el.icon ? `icon="${el.icon}"` : '' + const size = el.size ? `size="${el.size}"` : '' + let child = buildElButtonChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}` + }, + 'el-input': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : '' + const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : '' + const readonly = el.readonly ? 'readonly' : '' + const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : '' + const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : '' + const showPassword = el['show-password'] ? 'show-password' : '' + const type = el.type ? `type="${el.type}"` : '' + const autosize = el.autosize && el.autosize.minRows + ? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"` + : '' + let child = buildElInputChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}` + }, + 'el-input-number': el => { + const { disabled, vModel, placeholder } = attrBuilder(el) + const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : '' + const min = el.min ? `:min='${el.min}'` : '' + const max = el.max ? `:max='${el.max}'` : '' + const step = el.step ? `:step='${el.step}'` : '' + const stepStrictly = el['step-strictly'] ? 'step-strictly' : '' + const precision = el.precision ? `:precision='${el.precision}'` : '' + + return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}>` + }, + 'el-select': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const filterable = el.filterable ? 'filterable' : '' + const multiple = el.multiple ? 'multiple' : '' + let child = buildElSelectChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}` + }, + 'el-radio-group': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + let child = buildElRadioGroupChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${size} ${disabled}>${child}` + }, + 'el-checkbox-group': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + const min = el.min ? `:min="${el.min}"` : '' + const max = el.max ? `:max="${el.max}"` : '' + let child = buildElCheckboxGroupChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}` + }, + 'el-switch': el => { + const { disabled, vModel } = attrBuilder(el) + const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : '' + const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : '' + const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : '' + const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : '' + const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : '' + const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : '' + + return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}>` + }, + 'el-cascader': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const options = el.options ? `:options="${el.vModel}Options"` : '' + const props = el.props ? `:props="${el.vModel}Props"` : '' + const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"' + const filterable = el.filterable ? 'filterable' : '' + const separator = el.separator === '/' ? '' : `separator="${el.separator}"` + + return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}>` + }, + 'el-slider': el => { + const { disabled, vModel } = attrBuilder(el) + const min = el.min ? `:min='${el.min}'` : '' + const max = el.max ? `:max='${el.max}'` : '' + const step = el.step ? `:step='${el.step}'` : '' + const range = el.range ? 'range' : '' + const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : '' + + return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}>` + }, + 'el-time-picker': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' + const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' + const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' + const isRange = el['is-range'] ? 'is-range' : '' + const format = el.format ? `format="${el.format}"` : '' + const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' + const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : '' + + return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}>` + }, + 'el-date-picker': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' + const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' + const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' + const format = el.format ? `format="${el.format}"` : '' + const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' + const type = el.type === 'date' ? '' : `type="${el.type}"` + const readonly = el.readonly ? 'readonly' : '' + + return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}>` + }, + 'el-rate': el => { + const { disabled, vModel } = attrBuilder(el) + const max = el.max ? `:max='${el.max}'` : '' + const allowHalf = el['allow-half'] ? 'allow-half' : '' + const showText = el['show-text'] ? 'show-text' : '' + const showScore = el['show-score'] ? 'show-score' : '' + + return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}>` + }, + 'el-color-picker': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + const showAlpha = el['show-alpha'] ? 'show-alpha' : '' + const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : '' + + return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}>` + }, + 'el-upload': el => { + const disabled = el.disabled ? ':disabled=\'true\'' : '' + const action = el.action ? `:action="${el.vModel}Action"` : '' + const multiple = el.multiple ? 'multiple' : '' + const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : '' + const accept = el.accept ? `accept="${el.accept}"` : '' + const name = el.name !== 'file' ? `name="${el.name}"` : '' + const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : '' + const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"` + const fileList = `:file-list="${el.vModel}fileList"` + const ref = `ref="${el.vModel}"` + let child = buildElUploadChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}` + } +} + +function attrBuilder(el) { + return { + vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`, + clearable: el.clearable ? 'clearable' : '', + placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '', + width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '', + disabled: el.disabled ? ':disabled=\'true\'' : '' + } +} + +// el-buttin 子级 +function buildElButtonChild(conf) { + const children = [] + if (conf.default) { + children.push(conf.default) + } + return children.join('\n') +} + +// el-input innerHTML +function buildElInputChild(conf) { + const children = [] + if (conf.prepend) { + children.push(``) + } + if (conf.append) { + children.push(``) + } + return children.join('\n') +} + +function buildElSelectChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + children.push(``) + } + return children.join('\n') +} + +function buildElRadioGroupChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + } + return children.join('\n') +} + +function buildElCheckboxGroupChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + } + return children.join('\n') +} + +function buildElUploadChild(conf) { + const list = [] + if (conf['list-type'] === 'picture-card') list.push('') + else list.push(`${conf.buttonText}`) + if (conf.showTip) list.push(`
只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件
`) + return list.join('\n') +} + +export function makeUpHtml(conf, type) { + const htmlList = [] + confGlobal = conf + someSpanIsNot24 = conf.fields.some(item => item.span !== 24) + conf.fields.forEach(el => { + htmlList.push(layouts[el.layout](el)) + }) + const htmlStr = htmlList.join('\n') + + let temp = buildFormTemplate(conf, htmlStr, type) + if (type === 'dialog') { + temp = dialogWrapper(temp) + } + confGlobal = null + return temp +} diff --git a/ZR.Vue/src/utils/generator/icon.json b/ZR.Vue/src/utils/generator/icon.json new file mode 100644 index 0000000..2d9999a --- /dev/null +++ b/ZR.Vue/src/utils/generator/icon.json @@ -0,0 +1 @@ +["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"] \ No newline at end of file diff --git a/ZR.Vue/src/utils/generator/js.js b/ZR.Vue/src/utils/generator/js.js new file mode 100644 index 0000000..c6c06a9 --- /dev/null +++ b/ZR.Vue/src/utils/generator/js.js @@ -0,0 +1,236 @@ +import { isArray } from 'util' +import { exportDefault, titleCase } from '@/utils/index' +import { trigger } from './config' + +const units = { + KB: '1024', + MB: '1024 / 1024', + GB: '1024 / 1024 / 1024' +} +let confGlobal +const inheritAttrs = { + file: '', + dialog: 'inheritAttrs: false,' +} + + +export function makeUpJs(conf, type) { + confGlobal = conf = JSON.parse(JSON.stringify(conf)) + const dataList = [] + const ruleList = [] + const optionsList = [] + const propsList = [] + const methodList = mixinMethod(type) + const uploadVarList = [] + + conf.fields.forEach(el => { + buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) + }) + + const script = buildexport( + conf, + type, + dataList.join('\n'), + ruleList.join('\n'), + optionsList.join('\n'), + uploadVarList.join('\n'), + propsList.join('\n'), + methodList.join('\n') + ) + confGlobal = null + return script +} + +function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) { + buildData(el, dataList) + buildRules(el, ruleList) + + if (el.options && el.options.length) { + buildOptions(el, optionsList) + if (el.dataType === 'dynamic') { + const model = `${el.vModel}Options` + const options = titleCase(model) + buildOptionMethod(`get${options}`, model, methodList) + } + } + + if (el.props && el.props.props) { + buildProps(el, propsList) + } + + if (el.action && el.tag === 'el-upload') { + uploadVarList.push( + `${el.vModel}Action: '${el.action}', + ${el.vModel}fileList: [],` + ) + methodList.push(buildBeforeUpload(el)) + if (!el['auto-upload']) { + methodList.push(buildSubmitUpload(el)) + } + } + + if (el.children) { + el.children.forEach(el2 => { + buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) + }) + } +} + +function mixinMethod(type) { + const list = []; const + minxins = { + file: confGlobal.formBtns ? { + submitForm: `submitForm() { + this.$refs['${confGlobal.formRef}'].validate(valid => { + if(!valid) return + // TODO 提交表单 + }) + },`, + resetForm: `resetForm() { + this.$refs['${confGlobal.formRef}'].resetFields() + },` + } : null, + dialog: { + onOpen: 'onOpen() {},', + onClose: `onClose() { + this.$refs['${confGlobal.formRef}'].resetFields() + },`, + close: `close() { + this.$emit('update:visible', false) + },`, + handelConfirm: `handelConfirm() { + this.$refs['${confGlobal.formRef}'].validate(valid => { + if(!valid) return + this.close() + }) + },` + } + } + + const methods = minxins[type] + if (methods) { + Object.keys(methods).forEach(key => { + list.push(methods[key]) + }) + } + + return list +} + +function buildData(conf, dataList) { + if (conf.vModel === undefined) return + let defaultValue + if (typeof (conf.defaultValue) === 'string' && !conf.multiple) { + defaultValue = `'${conf.defaultValue}'` + } else { + defaultValue = `${JSON.stringify(conf.defaultValue)}` + } + dataList.push(`${conf.vModel}: ${defaultValue},`) +} + +function buildRules(conf, ruleList) { + if (conf.vModel === undefined) return + const rules = [] + if (trigger[conf.tag]) { + if (conf.required) { + const type = isArray(conf.defaultValue) ? 'type: \'array\',' : '' + let message = isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder + if (message === undefined) message = `${conf.label}不能为空` + rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`) + } + if (conf.regList && isArray(conf.regList)) { + conf.regList.forEach(item => { + if (item.pattern) { + rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`) + } + }) + } + ruleList.push(`${conf.vModel}: [${rules.join(',')}],`) + } +} + +function buildOptions(conf, optionsList) { + if (conf.vModel === undefined) return + if (conf.dataType === 'dynamic') { conf.options = [] } + const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},` + optionsList.push(str) +} + +function buildProps(conf, propsList) { + if (conf.dataType === 'dynamic') { + conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey) + conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey) + conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey) + } + const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},` + propsList.push(str) +} + +function buildBeforeUpload(conf) { + const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const + returnList = [] + if (conf.fileSize) { + rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize} + if(!isRightSize){ + this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}') + }` + returnList.push('isRightSize') + } + if (conf.accept) { + acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type) + if(!isAccept){ + this.$message.error('应该选择${conf.accept}类型的文件') + }` + returnList.push('isAccept') + } + const str = `${conf.vModel}BeforeUpload(file) { + ${rightSizeCode} + ${acceptCode} + return ${returnList.join('&&')} + },` + return returnList.length ? str : '' +} + +function buildSubmitUpload(conf) { + const str = `submitUpload() { + this.$refs['${conf.vModel}'].submit() + },` + return str +} + +function buildOptionMethod(methodName, model, methodList) { + const str = `${methodName}() { + // TODO 发起请求获取数据 + this.${model} + },` + methodList.push(str) +} + +function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) { + const str = `${exportDefault}{ + ${inheritAttrs[type]} + components: {}, + props: [], + data () { + return { + ${conf.formModel}: { + ${data} + }, + ${conf.formRules}: { + ${rules} + }, + ${uploadVar} + ${selectOptions} + ${props} + } + }, + computed: {}, + watch: {}, + created () {}, + mounted () {}, + methods: { + ${methods} + } +}` + return str +} diff --git a/ZR.Vue/src/utils/generator/render.js b/ZR.Vue/src/utils/generator/render.js new file mode 100644 index 0000000..e8640f0 --- /dev/null +++ b/ZR.Vue/src/utils/generator/render.js @@ -0,0 +1,126 @@ +import { makeMap } from '@/utils/index' + +// 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js +const isAttr = makeMap( + 'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' + + 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,' + + 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,' + + 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + + 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,' + + 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,' + + 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,' + + 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,' + + 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,' + + 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' + + 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' + + 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' + + 'target,title,type,usemap,value,width,wrap' +) + +function vModel(self, dataObject, defaultValue) { + dataObject.props.value = defaultValue + + dataObject.on.input = val => { + self.$emit('input', val) + } +} + +const componentChild = { + 'el-button': { + default(h, conf, key) { + return conf[key] + }, + }, + 'el-input': { + prepend(h, conf, key) { + return + }, + append(h, conf, key) { + return + } + }, + 'el-select': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + list.push() + }) + return list + } + }, + 'el-radio-group': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + if (conf.optionType === 'button') list.push({item.label}) + else list.push({item.label}) + }) + return list + } + }, + 'el-checkbox-group': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + if (conf.optionType === 'button') { + list.push({item.label}) + } else { + list.push({item.label}) + } + }) + return list + } + }, + 'el-upload': { + 'list-type': (h, conf, key) => { + const list = [] + if (conf['list-type'] === 'picture-card') { + list.push() + } else { + list.push({conf.buttonText}) + } + if (conf.showTip) { + list.push(
只能上传不超过 {conf.fileSize}{conf.sizeUnit} 的{conf.accept}文件
) + } + return list + } + } +} + +export default { + render(h) { + const dataObject = { + attrs: {}, + props: {}, + on: {}, + style: {} + } + const confClone = JSON.parse(JSON.stringify(this.conf)) + const children = [] + + const childObjs = componentChild[confClone.tag] + if (childObjs) { + Object.keys(childObjs).forEach(key => { + const childFunc = childObjs[key] + if (confClone[key]) { + children.push(childFunc(h, confClone, key)) + } + }) + } + + Object.keys(confClone).forEach(key => { + const val = confClone[key] + if (key === 'vModel') { + vModel(this, dataObject, confClone.defaultValue) + } else if (dataObject[key]) { + dataObject[key] = val + } else if (!isAttr(key)) { + dataObject.props[key] = val + } else { + dataObject.attrs[key] = val + } + }) + return h(this.conf.tag, dataObject, children) + }, + props: ['conf'] +} diff --git a/ZR.Vue/src/utils/index.js b/ZR.Vue/src/utils/index.js new file mode 100644 index 0000000..918580f --- /dev/null +++ b/ZR.Vue/src/utils/index.js @@ -0,0 +1,390 @@ +import { parseTime } from './ruoyi' + +/** + * 表格时间格式化 + */ +export function formatDate(cellValue) { + if (cellValue == null || cellValue == "") return ""; + var date = new Date(cellValue) + var year = date.getFullYear() + var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 + var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() + var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() + var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() + var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() + return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds +} + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return ( + d.getMonth() + + 1 + + '月' + + d.getDate() + + '日' + + d.getHours() + + '时' + + d.getMinutes() + + '分' + ) + } +} + +/** + * @param {string} url + * @returns {Object} + */ +export function getQueryObject(url) { + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj = {} + const reg = /([^?&=]+)=([^?&=]*)/g + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj +} + +/** + * @param {string} input value + * @returns {number} output value + */ +export function byteLength(str) { + // returns the byte length of an utf8 string + let s = str.length + for (var i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i) + if (code > 0x7f && code <= 0x7ff) s++ + else if (code > 0x7ff && code <= 0xffff) s += 2 + if (code >= 0xDC00 && code <= 0xDFFF) i-- + } + return s +} + +/** + * @param {Array} actual + * @returns {Array} + */ +export function cleanArray(actual) { + const newArray = [] + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]) + } + } + return newArray +} + +/** + * @param {Object} json + * @returns {Array} + */ +export function param(json) { + if (!json) return '' + return cleanArray( + Object.keys(json).map(key => { + if (json[key] === undefined) return '' + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) + }) + ).join('&') +} + +/** + * @param {string} url + * @returns {Object} + */ +export function param2Obj(url) { + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') + if (!search) { + return {} + } + const obj = {} + const searchArr = search.split('&') + searchArr.forEach(v => { + const index = v.indexOf('=') + if (index !== -1) { + const name = v.substring(0, index) + const val = v.substring(index + 1, v.length) + obj[name] = val + } + }) + return obj +} + +/** + * @param {string} val + * @returns {string} + */ +export function html2Text(val) { + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText +} + +/** + * Merges two objects, giving the last one precedence + * @param {Object} target + * @param {(Object|Array)} source + * @returns {Object} + */ +export function objectMerge(target, source) { + if (typeof target !== 'object') { + target = {} + } + if (Array.isArray(source)) { + return source.slice() + } + Object.keys(source).forEach(property => { + const sourceProperty = source[property] + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty) + } else { + target[property] = sourceProperty + } + }) + return target +} + +/** + * @param {HTMLElement} element + * @param {string} className + */ +export function toggleClass(element, className) { + if (!element || !className) { + return + } + let classString = element.className + const nameIndex = classString.indexOf(className) + if (nameIndex === -1) { + classString += '' + className + } else { + classString = + classString.substr(0, nameIndex) + + classString.substr(nameIndex + className.length) + } + element.className = classString +} + +/** + * @param {string} type + * @returns {Date} + */ +export function getTime(type) { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90 + } else { + return new Date(new Date().toDateString()) + } +} + +/** + * @param {Function} func + * @param {number} wait + * @param {boolean} immediate + * @return {*} + */ +export function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result + + const later = function() { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp + + // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + } + + return function(...args) { + context = this + timestamp = +new Date() + const callNow = immediate && !timeout + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait) + if (callNow) { + result = func.apply(context, args) + context = args = null + } + + return result + } +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +export function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone') + } + const targetObj = source.constructor === Array ? [] : {} + Object.keys(source).forEach(keys => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + }) + return targetObj +} + +/** + * @param {Array} arr + * @returns {Array} + */ +export function uniqueArr(arr) { + return Array.from(new Set(arr)) +} + +/** + * @returns {string} + */ +export function createUniqueString() { + const timestamp = +new Date() + '' + const randomNum = parseInt((1 + Math.random()) * 65536) + '' + return (+(randomNum + timestamp)).toString(32) +} + +/** + * Check if an element has a class + * @param {HTMLElement} elm + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele, cls) { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) +} + +/** + * Add class to element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function addClass(ele, cls) { + if (!hasClass(ele, cls)) ele.className += ' ' + cls +} + +/** + * Remove class from element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function removeClass(ele, cls) { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') + ele.className = ele.className.replace(reg, ' ') + } +} + +export function makeMap(str, expectsLowerCase) { + const map = Object.create(null) + const list = str.split(',') + for (let i = 0; i < list.length; i++) { + map[list[i]] = true + } + return expectsLowerCase + ? val => map[val.toLowerCase()] + : val => map[val] +} + +export const exportDefault = 'export default ' + +export const beautifierConf = { + html: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'separate', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: false, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + }, + js: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'normal', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: true, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + } +} + +// 首字母大小 +export function titleCase(str) { + return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) +} + +// 下划转驼峰 +export function camelCase(str) { + return str.replace(/-[a-z]/g, str1 => str1.substr(-1).toUpperCase()) +} + +export function isNumberStr(str) { + return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) +} + diff --git a/ZR.Vue/src/utils/jsencrypt.js b/ZR.Vue/src/utils/jsencrypt.js new file mode 100644 index 0000000..a132a02 --- /dev/null +++ b/ZR.Vue/src/utils/jsencrypt.js @@ -0,0 +1,34 @@ +import JSEncrypt from 'jsencrypt/bin/jsencrypt' + +// 密钥对生成 http://web.chacuo.net/netrsakeypair + +const publicKey = '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlJjf2lmK4zrpiATJSYfjkP2YhZ3SzKO0kqgX3Hg9o4NrgRgg9Abxw9vwZL7Weg22cJmahkx6waRYo8wXDqa2adJPntp60SAwWXou4cQTzHlySGQAC2U4CX54iHsKgqY7ifD2gmqJAkDnQkSXl4OKDTISnZcmlwIwo3Tj8jb9TWwIDAQAB-----END PUBLIC KEY-----'; + +const privateKey = '-----BEGIN ENCRYPTED PRIVATE KEY-----MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIV5E4J9jebHICAggAMBQGCCqGSIb3DQMHBAhdEG1zo87qTASCAoB8kR8KXgHIOQJIppp/2qGb+I8qcx/++Mb3yCAceHRMFZyczqlbKlwaDWzUVS3PQwbktb4s2coJt1uq/YL8CxAP/EJyMWsqiJxSl+4Mql0cHPhociwC7avGLtB+J2+BJU/ytF4u+/sSRGeSddoqx2glmGtYuF9U' + +'qv3jVElfUUOr3iq0aGSQRjCguVHk8fW1vy6QYi9ipPT0/BjWu4NutRLr9TI+Sy/4' + +'F9e6PeIKefT8ZX/q6vO0C1kVoG3GT1EjQfIF5XjmqGPUX7AR4J8R+uplDakHKZdH' + +'l6tqH+nJNQQH2RuFzYOWmUU2BiMEuU6ortJc8HkOy7Ca4FVJywnNxjOqzikI8DFW' + +'M9NuhMOwUCBae4582qW0zlc+ZGY1uWAoan4f3kmNdfeLU0xzk1yeR8KkprDwRk0m' + +'6jNSSOkBlNPerCcYm+pIaOXw+NQaaU4RQwHfNZnOWjteSuOmcszXuzdniuclHLzG' + +'zEwTolwUX9ez1sZCCmrjJpYMG4b9NQait5gx77Ogg7ymxBKnK89Gfuh3v3xvV6Td' + +'01r1GLd+OWPaI2kxAqruD/ZsMwXMpOLAc9ljz6cgtqlmvEk6tpI+WpGCaJBZXUA6' + +'/dv7KQeiWFQF5Oao3kX0s0wW3s2B4A2QFDE9HZHAw12nlsST8nmh+8zTxQNXhBMx' + +'B/UBkFND4UseKwYT4k+L0MmYcyExCagDiFa7k2/08D8DSbCVSWBXTH6xKWPMvFwr' + +'z4f5znLZFdbXYHFp0LyAw4HJ3LeTj0q8f2hNMG7lWZKYI7XZfmvoBkhODdOt2wTv' + +'U/T0f/ibcUf9Q9YXZQQkv1NOwmDtAB1KvHIkh2LALc/BY4qEoUyEVtLg' + +'-----END ENCRYPTED PRIVATE KEY-----' + +// 加密 +export function encrypt(txt) { + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) // 设置公钥 + return encryptor.encrypt(txt) // 对数据进行加密 +} + +// 解密 +export function decrypt(txt) { + const encryptor = new JSEncrypt() + encryptor.setPrivateKey(privateKey) // 设置私钥 + return encryptor.decrypt(txt) // 对数据进行解密 +} + diff --git a/ZR.Vue/src/utils/permission.js b/ZR.Vue/src/utils/permission.js new file mode 100644 index 0000000..ceca18f --- /dev/null +++ b/ZR.Vue/src/utils/permission.js @@ -0,0 +1,49 @@ +import store from '@/store' + +/** + * 字符权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export function checkPermi(value) { + if (value && value instanceof Array && value.length > 0) { + const permissions = store.getters && store.getters.permissions + const permissionDatas = value + + const hasPermission = permissions.some(permission => { + return permissionDatas.includes(permission) + }) + + if (!hasPermission) { + return false + } + return true + } else { + console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`) + return false + } +} + +/** + * 角色权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export function checkRole(value) { + if (value && value instanceof Array && value.length > 0) { + const roles = store.getters && store.getters.roles + const permissionRoles = value + + const hasRole = roles.some(role => { + return permissionRoles.includes(role) + }) + + if (!hasRole) { + return false + } + return true + } else { + console.error(`need roles! Like checkRole="['admin','editor']"`) + return false + } +} \ No newline at end of file diff --git a/ZR.Vue/src/utils/request.js b/ZR.Vue/src/utils/request.js new file mode 100644 index 0000000..1a883eb --- /dev/null +++ b/ZR.Vue/src/utils/request.js @@ -0,0 +1,145 @@ +import axios from 'axios' +import { MessageBox, Message } from 'element-ui' +import store from '@/store' +import { getToken } from '@/utils/auth' +// import errorCode from '@/utils/errorCode' + +// 解决后端跨域获取不到cookie问题 +axios.defaults.withCredentials = true +axios.defaults.headers['Content-Type'] = 'application/json' +// 创建axios实例 +const service = axios.create({ + // axios中请求配置有baseURL选项,表示请求URL公共部分 + baseURL: process.env.VUE_APP_BASE_API, + // 超时 + timeout: 30000 +}) + +// request拦截器 +service.interceptors.request.use(config => { + // 是否需要设置 token + // const isToken = (config.headers || {}).isToken === false + // if (getToken() && !isToken) { + // config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 + // } + // return config + // console.log(store.getters) + if (getToken()) { + //将token放到请求头发送给服务器,将tokenkey放在请求头中 + config.headers.Token = getToken(); + } else { + console.log(config) + } + return config; +}, error => { + console.log(error) + Promise.reject(error) +}) + +// 响应拦截器 +service.interceptors.response.use(res => { + if (res.status !== 200) { + Promise.reject('network error'); + return; + } + // 未设置状态码则默认成功状态 + const code = res.data.code; + const msg = res.data.msg; + + if (code == 401) { + MessageBox.confirm('登录状态已过期,请重新登录', '系统提示', { + confirmButtonText: '重新登录', + cancelButtonText: '取消', + type: 'warning' + } + ).then(() => { + store.dispatch('LogOut').then(() => { + location.href = '/index'; + }) + }) + } + else if (code == 0 || code == 110 || code == 101 || code == 403 || code == 500) { + Message({ + message: msg, + type: 'error' + }) + return Promise.reject() + } + else { + //返回标准 code/msg/data字段 + return res.data; + } +}, + error => { + console.log('err' + error) + let { message } = error; + if (message == "Network Error") { + message = "后端接口连接异常"; + } + else if (message.includes("timeout")) { + message = "系统接口请求超时"; + } + else if (message.includes("Request failed with status code")) { + message = "系统接口" + message.substr(message.length - 3) + "异常"; + } + Message({ + message: message, + type: 'error', + duration: 5 * 1000 + }) + return Promise.reject(error) + } +) + + + +/** + * get方法,对应get请求 + * @param {String} url [请求的url地址] + * @param {Object} params [请求时携带的参数] + */ +export function get(url, params) { + return new Promise((resolve, reject) => { + axios + .get(url, { + params: params + }) + .then(res => { + resolve(res.data) + }) + .catch(err => { + reject(err) + }) + }) +} + +export function post(url, params) { + return new Promise((resolve, reject) => { + axios + .post(url, { + params: params + }) + .then(res => { + resolve(res.data) + }) + .catch(err => { + reject(err) + }) + }) +} + +/** +* 提交表单 +* @param {*} url +* @param {*} data +*/ +export function postForm(url, data, config) { + return new Promise((resolve, reject) => { + axios.post(url, data, config).then(res => { + resolve(res.data) + }).catch(err => { + reject(err) + }) + }) +} +export default service diff --git a/ZR.Vue/src/utils/ruoyi.js b/ZR.Vue/src/utils/ruoyi.js new file mode 100644 index 0000000..2e8b635 --- /dev/null +++ b/ZR.Vue/src/utils/ruoyi.js @@ -0,0 +1,163 @@ +/** + * 通用js方法封装处理 + * + */ + +const baseURL = process.env.VUE_APP_BASE_API + +// 日期格式化 +export function parseTime(time, pattern) { + if (arguments.length === 0 || !time) { + return null + } + const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time + } else { + if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { + time = parseInt(time) + } else if (typeof time === 'string') { + time = time.replace(new RegExp(/-/gm), '/'); + } + if ((typeof time === 'number') && (time.toString().length === 10)) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key] + // Note: getDay() returns 0 on Sunday + if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +// 表单重置 +export function resetForm(refName) { + if (this.$refs[refName]) { + this.$refs[refName].resetFields(); + } +} + +// 添加日期范围 +export function addDateRange(params, dateRange) { + var search = params; + search.beginTime = ""; + search.endTime = ""; + if (null != dateRange && '' != dateRange) { + search.beginTime = dateRange[0]; + search.endTime = dateRange[1]; + } + return search; +} + +// 回显数据字典 +export function selectDictLabel(datas, value) { + var actions = []; + Object.keys(datas).some((key) => { + if (datas[key].dictValue == ('' + value)) { + actions.push(datas[key].dictLabel); + return true; + } + }) + return actions.join(''); +} + +// 回显数据字典(字符串数组) +export function selectDictLabels(datas, value, separator) { + var actions = []; + var currentSeparator = undefined === separator ? "," : separator; + var temp = value.split(currentSeparator); + Object.keys(value.split(currentSeparator)).some((val) => { + Object.keys(datas).some((key) => { + if (datas[key].dictValue == ('' + temp[val])) { + actions.push(datas[key].dictLabel + currentSeparator); + } + }) + }) + return actions.join('').substring(0, actions.join('').length - 1); +} + +// table是否显示当前列 +export function showColumn(columns, value) { + columns.filter((item,index) => { + // console.log(item); + return item.key == value; + }); +} + +// 通用下载方法 +export function download(fileName) { + // window.location.href = baseURL + "/common/download?fileName=" + encodeURI(fileName) + "&delete=" + true; + window.open(baseURL + "/common/download?fileName=" + encodeURI(fileName) + "&delete=" + true) +} + +// 字符串格式化(%s ) +export function sprintf(str) { + var args = arguments, flag = true, i = 1; + str = str.replace(/%s/g, function () { + var arg = args[i++]; + if (typeof arg === 'undefined') { + flag = false; + return ''; + } + return arg; + }); + return flag ? str : ''; +} + +// 转换字符串,undefined,null等转化为"" +export function praseStrEmpty(str) { + if (!str || str == "undefined" || str == "null") { + return ""; + } + return str; +} +export function praseStrZero(str) { + if (!str || str == "undefined" || str == "null") { + console.log('zero') + return 0; + } + return str; +} +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + * @param {*} rootId 根Id 默认 0 + */ +export function handleTree(data, id, parentId, children, rootId) { + id = id || 'id' + parentId = parentId || 'parentId' + children = children || 'children' + rootId = rootId || Math.min.apply(Math, data.map(item => { return item[parentId] })) || 0 + //对源数据深度克隆 + const cloneData = JSON.parse(JSON.stringify(data)) + //循环所有项 + const treeData = cloneData.filter(father => { + let branchArr = cloneData.filter(child => { + //返回每一项的子级数组 + return father[id] === child[parentId] + }); + branchArr.length > 0 ? father.children = branchArr : ''; + //返回第一层 + return father[parentId] === rootId; + }); + return treeData != '' ? treeData : data; +} diff --git a/ZR.Vue/src/utils/scroll-to.js b/ZR.Vue/src/utils/scroll-to.js new file mode 100644 index 0000000..c5d8e04 --- /dev/null +++ b/ZR.Vue/src/utils/scroll-to.js @@ -0,0 +1,58 @@ +Math.easeInOutQuad = function(t, b, c, d) { + t /= d / 2 + if (t < 1) { + return c / 2 * t * t + b + } + t-- + return -c / 2 * (t * (t - 2) - 1) + b +} + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +var requestAnimFrame = (function() { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } +})() + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +function move(amount) { + document.documentElement.scrollTop = amount + document.body.parentNode.scrollTop = amount + document.body.scrollTop = amount +} + +function position() { + return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop +} + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export function scrollTo(to, duration, callback) { + const start = position() + const change = to - start + const increment = 20 + let currentTime = 0 + duration = (typeof (duration) === 'undefined') ? 500 : duration + var animateScroll = function() { + // increment the time + currentTime += increment + // find the value with the quadratic in-out easing function + var val = Math.easeInOutQuad(currentTime, start, change, duration) + // move the document.body + move(val) + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll) + } else { + if (callback && typeof (callback) === 'function') { + // the animation is done so lets callback + callback() + } + } + } + animateScroll() +} diff --git a/ZR.Vue/src/utils/shortcuts.js b/ZR.Vue/src/utils/shortcuts.js new file mode 100644 index 0000000..eff6686 --- /dev/null +++ b/ZR.Vue/src/utils/shortcuts.js @@ -0,0 +1,108 @@ +import Date from './datetime.js' + +export const calendarBaseShortcuts = [{ + text: '今天', + onClick(picker) { + const startTime = new Date(new Date().setHours(0, 0, 0)) + const endTime = new Date(new Date().setHours(23, 59, 59)) + picker.$emit('pick', [startTime, endTime]) + } +}, { + text: '昨天', + onClick(picker) { + const startTime = new Date(new Date().daysAgo(1).setHours(0, 0, 0)) + const endTime = new Date(new Date().daysAgo(1).setHours(23, 59, 59)) + picker.$emit('pick', [startTime, endTime]) + } +}, { + text: '最近一周', + onClick(picker) { + const startTime = new Date(new Date().daysAgo(7).setHours(0, 0, 0)) + const endTime = new Date(new Date().setHours(23, 59, 59)) + picker.$emit('pick', [startTime, endTime]) + } +}, { + text: '最近30天', + onClick(picker) { + const startTime = new Date(new Date().daysAgo(30).setHours(0, 0, 0)) + const endTime = new Date(new Date().setHours(23, 59, 59)) + picker.$emit('pick', [startTime, endTime]) + } +}, { + text: '这个月', + onClick(picker) { + const startTime = new Date(new Date().monthBegin().setHours(0, 0, 0)) + const endTime = new Date(new Date().setHours(23, 59, 59)) + picker.$emit('pick', [startTime, endTime]) + } +}, { + text: '本季度', + onClick(picker) { + const startTime = new Date(new Date().quarterBegin().setHours(0, 0, 0)) + const endTime = new Date(new Date().setHours(23, 59, 59)) + picker.$emit('pick', [startTime, endTime]) + } +}] + +export const calendarMoveShortcuts = [{ + text: '‹ 往前一天 ', + onClick(picker) { + let startTime = new Date(new Date().daysAgo(1).setHours(0, 0, 0)) + let endTime = new Date(new Date().daysAgo(1).setHours(23, 59, 59)) + if (!picker.value) { + picker.value = [startTime, endTime] + } + startTime = picker.value[0].daysAgo(1) + endTime = picker.value[1].daysAgo(1) + picker.$emit('pick', [startTime, picker]) + } +}, { + text: ' 往后一天 ›', + onClick(picker) { + let startTime = new Date(new Date().setHours(0, 0, 0)) + let endTime = new Date(new Date().setHours(23, 59, 59)) + if (!picker.value) { + picker.value = [startTime, endTime] + } + startTime = picker.value[0].daysAgo(-1) + endTime = picker.value[1].daysAgo(-1) + picker.$emit('pick', [startTime, endTime]) + } +}, { + text: '« 往前一周 ', + onClick(picker) { + let startTime = new Date(new Date().setHours(0, 0, 0)) + let endTime = new Date(new Date().setHours(23, 59, 59)) + if (!picker.value) { + picker.value = [startTime.daysAgo(new Date().getDay()), + endTime.daysAgo(new Date().getDay() + 1)] + } else { + picker.value = [picker.value[0].daysAgo(picker.value[0].getDay()), + picker.value[1].daysAgo(picker.value[1].getDay() + 1)] + } + startTime = picker.value[0].daysAgo(7) + endTime = picker.value[1] + picker.$emit('pick', [startTime, endTime]) + } +}, { + text: ' 往后一周 »', + onClick(picker) { + let startTime = new Date(new Date().setHours(0, 0, 0)) + let endTime = new Date(new Date().setHours(23, 59, 59)) + if (!picker.value) { + picker.value = [startTime.daysAgo(new Date().getDay() - 7), + endTime.daysAgo(new Date().getDay() - 6)] + } else { + picker.value = [picker.value[0].daysAgo(picker.value[0].getDay() - 7), + picker.value[1].daysAgo(picker.value[1].getDay() - 6)] + } + startTime = picker.value[0] + endTime = picker.value[1].daysAgo(-7) + picker.$emit('pick', [startTime, endTime]) + } +}] + +export const calendarShortcuts = [ + ...calendarBaseShortcuts, + ...calendarMoveShortcuts +] diff --git a/ZR.Vue/src/utils/validate.js b/ZR.Vue/src/utils/validate.js new file mode 100644 index 0000000..adfa254 --- /dev/null +++ b/ZR.Vue/src/utils/validate.js @@ -0,0 +1,83 @@ +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUsername(str) { + const valid_map = ['admin', 'editor'] + return valid_map.indexOf(str.trim()) >= 0 +} + +/** + * @param {string} url + * @returns {Boolean} + */ +export function validURL(url) { + const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return reg.test(url) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validLowerCase(str) { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUpperCase(str) { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validAlphabets(str) { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + +/** + * @param {string} email + * @returns {Boolean} + */ +export function validEmail(email) { + const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return reg.test(email) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function isString(str) { + if (typeof str === 'string' || str instanceof String) { + return true + } + return false +} + +/** + * @param {Array} arg + * @returns {Boolean} + */ +export function isArray(arg) { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]' + } + return Array.isArray(arg) +} diff --git a/ZR.Vue/src/utils/zipdownload.js b/ZR.Vue/src/utils/zipdownload.js new file mode 100644 index 0000000..faa613c --- /dev/null +++ b/ZR.Vue/src/utils/zipdownload.js @@ -0,0 +1,70 @@ +import axios from 'axios' +import { getToken } from '@/utils/auth' + +const mimeMap = { + xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + zip: 'application/zip' +} + +const baseUrl = process.env.VUE_APP_BASE_API +export function downLoadZip(str, filename) { + var url = baseUrl + str + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Token': getToken() } + }).then(res => { + resolveBlob(res, mimeMap.zip) + }) +} +export function downLoadExcel(str, filename) { + var url = baseUrl + str + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Token': getToken() } + }).then(res => { + resolveExcel(res, filename) + }) +} +/** + * 解析blob响应内容并下载 + * @param {*} res blob响应内容 + * @param {String} mimeType MIME类型 + */ +export function resolveBlob(res, mimeType) { + const aLink = document.createElement('a') + var blob = new Blob([res.data], { type: mimeType }) + + // //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名; + var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*') + var contentDisposition = decodeURI(res.headers['content-disposition']) + var result = patt.exec(contentDisposition) + var fileName = result[1] + fileName = fileName.replace(/\"/g, '') + aLink.href = URL.createObjectURL(blob) + aLink.setAttribute('download', fileName) // 设置下载文件名称 + document.body.appendChild(aLink) + aLink.click() + document.body.appendChild(aLink) +} +/** + * 解析blob响应内容并下载 + * @param {*} res blob响应内容 + * @param {String} fileName 文件名 + */ +export function resolveExcel(res, fileName) { + const aLink = document.createElement('a') + var blob = new Blob([res.data], { type: 'application/vnd.ms-excel' }) + + fileName = fileName.replace(/\"/g, '') + aLink.href = URL.createObjectURL(blob) + aLink.setAttribute('download', fileName) // 设置下载文件名称 + document.body.appendChild(aLink) + aLink.click() + document.body.appendChild(aLink) + // 5.释放这个临时的对象url + // window.URL.revokeObjectURL(aLink.href); +} diff --git a/ZR.Vue/src/views/article/manager.vue b/ZR.Vue/src/views/article/manager.vue new file mode 100644 index 0000000..e8bc72d --- /dev/null +++ b/ZR.Vue/src/views/article/manager.vue @@ -0,0 +1,143 @@ + + diff --git a/ZR.Vue/src/views/article/publish.vue b/ZR.Vue/src/views/article/publish.vue new file mode 100644 index 0000000..b1802f1 --- /dev/null +++ b/ZR.Vue/src/views/article/publish.vue @@ -0,0 +1,225 @@ + + + diff --git a/ZR.Vue/src/views/components/dictData.vue b/ZR.Vue/src/views/components/dictData.vue new file mode 100644 index 0000000..05ad92b --- /dev/null +++ b/ZR.Vue/src/views/components/dictData.vue @@ -0,0 +1,275 @@ + + diff --git a/ZR.Vue/src/views/components/icons/element-icons.js b/ZR.Vue/src/views/components/icons/element-icons.js new file mode 100644 index 0000000..9ea4d63 --- /dev/null +++ b/ZR.Vue/src/views/components/icons/element-icons.js @@ -0,0 +1,3 @@ +const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round'] + +export default elementIcons diff --git a/ZR.Vue/src/views/components/icons/index.vue b/ZR.Vue/src/views/components/icons/index.vue new file mode 100644 index 0000000..d3c9a71 --- /dev/null +++ b/ZR.Vue/src/views/components/icons/index.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/ZR.Vue/src/views/components/icons/svg-icons.js b/ZR.Vue/src/views/components/icons/svg-icons.js new file mode 100644 index 0000000..724cd8e --- /dev/null +++ b/ZR.Vue/src/views/components/icons/svg-icons.js @@ -0,0 +1,10 @@ +const req = require.context('../../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const svgIcons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default svgIcons diff --git a/ZR.Vue/src/views/dashboard/BarChart.vue b/ZR.Vue/src/views/dashboard/BarChart.vue new file mode 100644 index 0000000..96b2ea5 --- /dev/null +++ b/ZR.Vue/src/views/dashboard/BarChart.vue @@ -0,0 +1,101 @@ + + + diff --git a/ZR.Vue/src/views/dashboard/LineChart.vue b/ZR.Vue/src/views/dashboard/LineChart.vue new file mode 100644 index 0000000..2df455e --- /dev/null +++ b/ZR.Vue/src/views/dashboard/LineChart.vue @@ -0,0 +1,134 @@ + + + diff --git a/ZR.Vue/src/views/dashboard/PanelGroup.vue b/ZR.Vue/src/views/dashboard/PanelGroup.vue new file mode 100644 index 0000000..1a1081f --- /dev/null +++ b/ZR.Vue/src/views/dashboard/PanelGroup.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/ZR.Vue/src/views/dashboard/PieChart.vue b/ZR.Vue/src/views/dashboard/PieChart.vue new file mode 100644 index 0000000..fe25c2c --- /dev/null +++ b/ZR.Vue/src/views/dashboard/PieChart.vue @@ -0,0 +1,78 @@ + + + diff --git a/ZR.Vue/src/views/dashboard/RaddarChart.vue b/ZR.Vue/src/views/dashboard/RaddarChart.vue new file mode 100644 index 0000000..55f5893 --- /dev/null +++ b/ZR.Vue/src/views/dashboard/RaddarChart.vue @@ -0,0 +1,115 @@ + + + diff --git a/ZR.Vue/src/views/dashboard/bigdata.vue b/ZR.Vue/src/views/dashboard/bigdata.vue new file mode 100644 index 0000000..f6caa73 --- /dev/null +++ b/ZR.Vue/src/views/dashboard/bigdata.vue @@ -0,0 +1,218 @@ + + + + + + + + diff --git a/ZR.Vue/src/views/dashboard/bigdata/IviewCircle.vue b/ZR.Vue/src/views/dashboard/bigdata/IviewCircle.vue new file mode 100644 index 0000000..a0deaed --- /dev/null +++ b/ZR.Vue/src/views/dashboard/bigdata/IviewCircle.vue @@ -0,0 +1,88 @@ + + + + diff --git a/ZR.Vue/src/views/dashboard/bigdata/chart-options.js b/ZR.Vue/src/views/dashboard/bigdata/chart-options.js new file mode 100644 index 0000000..98dfb75 --- /dev/null +++ b/ZR.Vue/src/views/dashboard/bigdata/chart-options.js @@ -0,0 +1,471 @@ +var echarts = require("echarts"); +let chartLeft1 = { + tooltip: { + trigger: "axis", + axisPointer: { + type: "shadow" + } + }, + grid: { + left: "0%", + top: "10px", + right: "0%", + bottom: "4%", + containLabel: true + }, + xAxis: [ + { + type: "category", + data: [ + "商超门店", + "教育培训", + "房地产", + "生活服务", + "汽车销售", + "旅游酒店", + "五金建材" + ], + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1)", + width: 1, + type: "solid" + } + }, + + axisTick: { + show: false + }, + axisLabel: { + interval: 0, + show: true, + splitNumber: 15, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + } + } + ], + yAxis: [ + { + type: "value", + axisLabel: { + show: true, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1 )", + width: 1, + type: "solid" + } + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + } + } + ], + series: [ + { + type: "bar", + data: [200, 600, 300, 900, 1500, 1200, 600], + barWidth: "35%", + itemStyle: { + normal: { + color: "#2f89cf", + opacity: 1, + barBorderRadius: 5 + } + } + } + ] +}; + + +let chartLeft2 = { + tooltip: { + trigger: "axis", + axisPointer: { + type: "shadow" + } + }, + grid: { + left: "0%", + top: "10px", + right: "0%", + bottom: "4%", + containLabel: true + }, + xAxis: [ + { + type: "category", + data: [ + "07.01", + "07.02", + "07.03", + "07.04", + "07.05", + "07.06", + ], + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1)", + width: 1, + type: "solid" + } + }, + axisTick: { + show: false + }, + axisLabel: { + interval: 0, + // rotate:50, + show: true, + splitNumber: 15, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + } + } + ], + yAxis: [ + { + type: "value", + axisLabel: { + show: true, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1 )", + width: 1, + type: "solid" + } + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + } + }, + { + type: "value", + axisLabel: { + show: true, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1 )", + width: 1, + type: "solid" + } + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + } + } + ], series: [ + { + type: "bar", + name: "销量", + data: [1200, 800, 300, 500, 560, 220], + barWidth: "25%", + itemStyle: { + normal: { + color: "#2f89cf", + opacity: 1, + barBorderRadius: 5 + } + } + }, { + type: "bar", + name: "订单", + data: [1000, 750, 380, 450, 450, 120], + barWidth: "25%", + itemStyle: { + normal: { + color: "#46d000", + opacity: 1, + barBorderRadius: 5 + } + } + } + ] +}; + +let chartLeft3 = { + tooltip: { + trigger: 'axis', + axisPointer: { // 坐标轴指示器,坐标轴触发有效 + type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' + } + }, + grid: { + left: "0%", + top: "-5px", + right: "3%", + bottom: "4%", + containLabel: true + }, + xAxis: { + type: 'value', + axisLabel: { + show: true, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1 )", + width: 1, + type: "solid" + } + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + } + }, + yAxis: { + type: 'category', + axisLabel: { + show: true, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1 )", + width: 1, + type: "solid" + } + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + }, + data: ['周一', '周二', '周三', '周四', '周五'] + }, + series: [ + { + name: '直接访问', + type: 'bar', + stack: '总量', + label: { + show: true, + position: 'insideRight' + }, + barWidth: "55%", + itemStyle: { + normal: { + color: "#2f89cf", + opacity: 1, + barBorderRadius: 5 + } + }, + data: [120, 302, 400, 200, 700] + } + ] +}; + +let chartRight1 = { + title: {}, + tooltip: { + trigger: "axis", + axisPointer: { + type: "cross", + label: { + backgroundColor: "#6a7985" + } + } + }, + + color: ["#ffab6f", "#09b916", "#83cddc"], //图例颜色 + legend: { + top: "0%", + icon: "roundRect", + data: ["销售订单", "退货订单", "折扣订单"], + textStyle: { + color: "rgba(255,255,255,.5)", + fontSize: "12" + } + }, + toolbox: { + feature: {} + }, + grid: { + left: "10", + top: "20", + right: "10", + bottom: "10", + containLabel: true + }, + xAxis: [ + { + type: "category", + boundaryGap: false, + axisLabel: { + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: 12 + } + }, + axisLine: { + lineStyle: { + color: "rgba(255,255,255,.2)" + } + }, + data: [ + "2020.06.15", + "2020.06.16", + "2020.06.17", + "2020.06.18", + "2020.06.19", + "2020.06.20", + "2020.06.21", + "2020.06.22" + ] + } + ], + yAxis: [ + { + type: "value", + axisTick: { show: false }, + minInterval: 60, + axisLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + }, + axisLabel: { + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: 12 + } + }, + + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + } + } + ], + series: [ + { + name: "销售订单", + type: "line", + smooth: true, + lineStyle: { + color: "#45d4ba", + width: 1 + }, //线条的样式 + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: "#83cddc" + }, + { + offset: 1, + color: "#bfdffbb5" + } + ]) + }, + data: [5, 22, 150, 54, 1, 230, 4, 1] + }, + { + name: "退货订单", + type: "line", + + smooth: true, + lineStyle: { + color: "#04a710", + width: 1 + }, // + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: "#0cbf22" + }, + { + offset: 1, + color: "#b8f7d1b5" + } + ]) + }, + data: [10, 150, 1, 250, 20, 100, 10, 150] + }, + { + name: "折扣订单", + type: "line", + + lineStyle: { + color: "#0864c3", + width: 1 + }, //线条的样式 + smooth: true, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: "#29d7ff" + }, + { + offset: 1, + color: "#34ccef85" + } + ]) + }, + data: [100, 2, 260, 1, 200, 30, 101, 40] + } + ] +}; + + +export { chartLeft1, chartLeft2,chartLeft3, chartRight1 } diff --git a/ZR.Vue/src/views/dashboard/bigdata/head_bg.png b/ZR.Vue/src/views/dashboard/bigdata/head_bg.png new file mode 100644 index 0000000..a2e45f6 Binary files /dev/null and b/ZR.Vue/src/views/dashboard/bigdata/head_bg.png differ diff --git a/ZR.Vue/src/views/dashboard/bigdata/layout.less b/ZR.Vue/src/views/dashboard/bigdata/layout.less new file mode 100644 index 0000000..3f6cffd --- /dev/null +++ b/ZR.Vue/src/views/dashboard/bigdata/layout.less @@ -0,0 +1,197 @@ + +.big-data-container { + position: absolute; + overflow: hidden; + height: 100%; + width: 100%; + background-color: #1400a8; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 1200 800'%3E%3Cdefs%3E%3CradialGradient id='a' cx='0' cy='800' r='800' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%230e0077'/%3E%3Cstop offset='1' stop-color='%230e0077' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='b' cx='1200' cy='800' r='800' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%2314057c'/%3E%3Cstop offset='1' stop-color='%2314057c' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='c' cx='600' cy='0' r='600' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%230d0524'/%3E%3Cstop offset='1' stop-color='%230d0524' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='d' cx='600' cy='800' r='600' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%231400a8'/%3E%3Cstop offset='1' stop-color='%231400a8' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='e' cx='0' cy='0' r='800' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23000000'/%3E%3Cstop offset='1' stop-color='%23000000' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='f' cx='1200' cy='0' r='800' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23130733'/%3E%3Cstop offset='1' stop-color='%23130733' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect fill='url(%23a)' width='1200' height='800'/%3E%3Crect fill='url(%23b)' width='1200' height='800'/%3E%3Crect fill='url(%23c)' width='1200' height='800'/%3E%3Crect fill='url(%23d)' width='1200' height='800'/%3E%3Crect fill='url(%23e)' width='1200' height='800'/%3E%3Crect fill='url(%23f)' width='1200' height='800'/%3E%3C/svg%3E"); + background-attachment: fixed; + background-size: cover; + .head { + height: 75px; + /* height: 1.05rem; */ + background: url(./head_bg.png) no-repeat center center; + background-size: 100% 100%; + position: relative; + z-index: 100; + } +} + +.head h1 { + margin: 0; + color: #fff; + text-align: center; + /* font-size: .4rem; */ + /* line-height: .8rem; */ + line-height: 71px; +} + +.data-container { + /* margin: 5px 15px; + height:100%; */ + + margin: 0px 15px; + position: absolute; + left: 0; + right: 0; + top: 76px; + bottom: 0; +} + +.data-container > div { + float: left; + /* border: 1px solid white; */ + height: 100%; +} + +.data-center { + padding: 0 0.9rem; + width: 40%; + display: flex; + flex-direction: column; + // .center-top{ + // height: 210px; + // background: red; + // } +.chart-center{ + flex: 1; +} +} +.chart-center{ + width: 100%; +display: flex; +// background: white; +} +.data-left, +.data-right { + width: 30%; + display: flex; + + flex-direction: column; +} + +.data-left-item, +.data-right-item,.center-top,.center-top-num,.chart-center { + border: 1px solid rgba(25, 186, 139, 0.17); + padding: 0 0.2rem 0.4rem 0.15rem; + background: rgba(255, 255, 255, 0.04); + background-size: 100% auto; + position: relative; + margin-bottom: 0.15rem; + z-index: 10; +} + +.data-foot-line { + position: absolute; + bottom: 0; + width: 100%; + left: 0; +} + +.data-foot-line:before, +.data-foot-line:after { + position: absolute; + width: 10px; + height:10px; + content: ""; + border-bottom: 2px solid #02a6b5; + bottom: 0; +} + +.boxall:before, +.data-foot-line:before { + border-left: 2px solid #02a6b5; + left: 0; +} + +.boxall:after, +.data-foot-line:after { + border-right: 2px solid #02a6b5; + right: 0; +} + +.boxall:before, +.boxall:after { + position: absolute; + width: 10px; + height: 10px; + content: ""; + border-top: 2px solid #02a6b5; + top: 0; +} + +.data-left-item:before, +.data-right-item:before, +.center-top-num:before, +.center-top:before{ + border-left: 2px solid #02a6b5; + left: 0; + position: absolute; + width: 10px; + height:10px; + content: ""; + border-top: 2px solid #02a6b5; + top: 0; +} + +.data-left-item:after, +.data-right-item:after, +.center-top-num:after, +.center-top:after { + border-right: 2px solid #02a6b5; + right: 0; + position: absolute; + width: 10px; + height: 10px; + content: ""; + border-top: 2px solid #02a6b5; + top: 0; +} + +.data-left, +.data-right { + /* display: flex; */ +} + +.data-left > .data-left-item, +.data-right > .data-right-item { + flex: 1; + margin-bottom: 0.9rem; +} + +.data-center .title, +.data-left > .data-left-item .title, +.data-right > .data-right-item .title { + /* font-size: .2rem; */ + font-size: 1rem; + padding: 7px 0; + color: #fff; + text-align: center; + /* line-height: .5rem; */ +} + +.data-center .chart-center{ + width: 100%; +} + +.center-top-num{ + height: 80px; + padding-top: 7px; + margin-bottom: 0.8rem; + display: flex; + .item{ + flex: 1; + text-align: center; + } + .text{ + color: #fcf0d8; + font-size: 14px; + } + .num{ + font-size: 34px; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + font-weight: bold; + color: #67caca; + } +} diff --git a/ZR.Vue/src/views/dashboard/mixins/resize.js b/ZR.Vue/src/views/dashboard/mixins/resize.js new file mode 100644 index 0000000..b1e76e9 --- /dev/null +++ b/ZR.Vue/src/views/dashboard/mixins/resize.js @@ -0,0 +1,56 @@ +import { debounce } from '@/utils' + +export default { + data() { + return { + $_sidebarElm: null, + $_resizeHandler: null + } + }, + mounted() { + this.initListener() + }, + activated() { + if (!this.$_resizeHandler) { + // avoid duplication init + this.initListener() + } + + // when keep-alive chart activated, auto resize + this.resize() + }, + beforeDestroy() { + this.destroyListener() + }, + deactivated() { + this.destroyListener() + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_sidebarResizeHandler(e) { + if (e.propertyName === 'width') { + this.$_resizeHandler() + } + }, + initListener() { + this.$_resizeHandler = debounce(() => { + this.resize() + }, 100) + window.addEventListener('resize', this.$_resizeHandler) + + this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] + this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) + }, + destroyListener() { + window.removeEventListener('resize', this.$_resizeHandler) + this.$_resizeHandler = null + + this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) + }, + resize() { + const { chart } = this + chart && chart.resize() + } + } +} diff --git a/ZR.Vue/src/views/demo.vue b/ZR.Vue/src/views/demo.vue new file mode 100644 index 0000000..4f7996b --- /dev/null +++ b/ZR.Vue/src/views/demo.vue @@ -0,0 +1,280 @@ + + + diff --git a/ZR.Vue/src/views/error/401.vue b/ZR.Vue/src/views/error/401.vue new file mode 100644 index 0000000..448b6ec --- /dev/null +++ b/ZR.Vue/src/views/error/401.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/ZR.Vue/src/views/error/404.vue b/ZR.Vue/src/views/error/404.vue new file mode 100644 index 0000000..96f075c --- /dev/null +++ b/ZR.Vue/src/views/error/404.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/ZR.Vue/src/views/index.vue b/ZR.Vue/src/views/index.vue new file mode 100644 index 0000000..c1be6dd --- /dev/null +++ b/ZR.Vue/src/views/index.vue @@ -0,0 +1,154 @@ + + + + + + diff --git a/ZR.Vue/src/views/index_v1.vue b/ZR.Vue/src/views/index_v1.vue new file mode 100644 index 0000000..405cb9e --- /dev/null +++ b/ZR.Vue/src/views/index_v1.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/ZR.Vue/src/views/login.vue b/ZR.Vue/src/views/login.vue new file mode 100644 index 0000000..4b241eb --- /dev/null +++ b/ZR.Vue/src/views/login.vue @@ -0,0 +1,206 @@ + + + + + diff --git a/ZR.Vue/src/views/monitor/cache/index.vue b/ZR.Vue/src/views/monitor/cache/index.vue new file mode 100644 index 0000000..5965dfb --- /dev/null +++ b/ZR.Vue/src/views/monitor/cache/index.vue @@ -0,0 +1,151 @@ + + + diff --git a/ZR.Vue/src/views/monitor/job/index.vue b/ZR.Vue/src/views/monitor/job/index.vue new file mode 100644 index 0000000..b17a013 --- /dev/null +++ b/ZR.Vue/src/views/monitor/job/index.vue @@ -0,0 +1,464 @@ + + + diff --git a/ZR.Vue/src/views/monitor/job/log.vue b/ZR.Vue/src/views/monitor/job/log.vue new file mode 100644 index 0000000..2698848 --- /dev/null +++ b/ZR.Vue/src/views/monitor/job/log.vue @@ -0,0 +1,250 @@ + + + diff --git a/ZR.Vue/src/views/monitor/logininfor/index.vue b/ZR.Vue/src/views/monitor/logininfor/index.vue new file mode 100644 index 0000000..a528284 --- /dev/null +++ b/ZR.Vue/src/views/monitor/logininfor/index.vue @@ -0,0 +1,189 @@ + + + + diff --git a/ZR.Vue/src/views/monitor/online/index.vue b/ZR.Vue/src/views/monitor/online/index.vue new file mode 100644 index 0000000..a33614b --- /dev/null +++ b/ZR.Vue/src/views/monitor/online/index.vue @@ -0,0 +1,128 @@ + + + + diff --git a/ZR.Vue/src/views/monitor/operlog/index.vue b/ZR.Vue/src/views/monitor/operlog/index.vue new file mode 100644 index 0000000..081b728 --- /dev/null +++ b/ZR.Vue/src/views/monitor/operlog/index.vue @@ -0,0 +1,273 @@ + + + + diff --git a/ZR.Vue/src/views/monitor/server/index.vue b/ZR.Vue/src/views/monitor/server/index.vue new file mode 100644 index 0000000..8f6c5df --- /dev/null +++ b/ZR.Vue/src/views/monitor/server/index.vue @@ -0,0 +1,340 @@ + + + diff --git a/ZR.Vue/src/views/redirect.vue b/ZR.Vue/src/views/redirect.vue new file mode 100644 index 0000000..db4c1d6 --- /dev/null +++ b/ZR.Vue/src/views/redirect.vue @@ -0,0 +1,12 @@ + diff --git a/ZR.Vue/src/views/system/dept/index.vue b/ZR.Vue/src/views/system/dept/index.vue new file mode 100644 index 0000000..ce82cc5 --- /dev/null +++ b/ZR.Vue/src/views/system/dept/index.vue @@ -0,0 +1,283 @@ + + + diff --git a/ZR.Vue/src/views/system/dict/data.vue b/ZR.Vue/src/views/system/dict/data.vue new file mode 100644 index 0000000..8398ddb --- /dev/null +++ b/ZR.Vue/src/views/system/dict/data.vue @@ -0,0 +1,282 @@ + + + diff --git a/ZR.Vue/src/views/system/dict/index.vue b/ZR.Vue/src/views/system/dict/index.vue new file mode 100644 index 0000000..41317a5 --- /dev/null +++ b/ZR.Vue/src/views/system/dict/index.vue @@ -0,0 +1,292 @@ + + + diff --git a/ZR.Vue/src/views/system/file/index.vue b/ZR.Vue/src/views/system/file/index.vue new file mode 100644 index 0000000..86d6824 --- /dev/null +++ b/ZR.Vue/src/views/system/file/index.vue @@ -0,0 +1,236 @@ + + diff --git a/ZR.Vue/src/views/system/menu/index.vue b/ZR.Vue/src/views/system/menu/index.vue new file mode 100644 index 0000000..bd0cda2 --- /dev/null +++ b/ZR.Vue/src/views/system/menu/index.vue @@ -0,0 +1,424 @@ + + + diff --git a/ZR.Vue/src/views/system/notice/index.vue b/ZR.Vue/src/views/system/notice/index.vue new file mode 100644 index 0000000..3160e37 --- /dev/null +++ b/ZR.Vue/src/views/system/notice/index.vue @@ -0,0 +1,340 @@ + + + \ No newline at end of file diff --git a/ZR.Vue/src/views/system/post/index.vue b/ZR.Vue/src/views/system/post/index.vue new file mode 100644 index 0000000..a9729d0 --- /dev/null +++ b/ZR.Vue/src/views/system/post/index.vue @@ -0,0 +1,272 @@ + + + diff --git a/ZR.Vue/src/views/system/role/index.vue b/ZR.Vue/src/views/system/role/index.vue new file mode 100644 index 0000000..ee82f70 --- /dev/null +++ b/ZR.Vue/src/views/system/role/index.vue @@ -0,0 +1,505 @@ + + + diff --git a/ZR.Vue/src/views/system/roleusers/index.vue b/ZR.Vue/src/views/system/roleusers/index.vue new file mode 100644 index 0000000..65130bb --- /dev/null +++ b/ZR.Vue/src/views/system/roleusers/index.vue @@ -0,0 +1,217 @@ + + diff --git a/ZR.Vue/src/views/system/user/index.vue b/ZR.Vue/src/views/system/user/index.vue new file mode 100644 index 0000000..1490266 --- /dev/null +++ b/ZR.Vue/src/views/system/user/index.vue @@ -0,0 +1,592 @@ + + + diff --git a/ZR.Vue/src/views/system/user/profile/index.vue b/ZR.Vue/src/views/system/user/profile/index.vue new file mode 100644 index 0000000..3e4481e --- /dev/null +++ b/ZR.Vue/src/views/system/user/profile/index.vue @@ -0,0 +1,94 @@ + + + diff --git a/ZR.Vue/src/views/system/user/profile/resetPwd.vue b/ZR.Vue/src/views/system/user/profile/resetPwd.vue new file mode 100644 index 0000000..ee65240 --- /dev/null +++ b/ZR.Vue/src/views/system/user/profile/resetPwd.vue @@ -0,0 +1,72 @@ + + + diff --git a/ZR.Vue/src/views/system/user/profile/userAvatar.vue b/ZR.Vue/src/views/system/user/profile/userAvatar.vue new file mode 100644 index 0000000..69b2661 --- /dev/null +++ b/ZR.Vue/src/views/system/user/profile/userAvatar.vue @@ -0,0 +1,157 @@ + + + + diff --git a/ZR.Vue/src/views/system/user/profile/userInfo.vue b/ZR.Vue/src/views/system/user/profile/userInfo.vue new file mode 100644 index 0000000..2e3948a --- /dev/null +++ b/ZR.Vue/src/views/system/user/profile/userInfo.vue @@ -0,0 +1,76 @@ + + + diff --git a/ZR.Vue/src/views/tool/build/CodeTypeDialog.vue b/ZR.Vue/src/views/tool/build/CodeTypeDialog.vue new file mode 100644 index 0000000..941ec36 --- /dev/null +++ b/ZR.Vue/src/views/tool/build/CodeTypeDialog.vue @@ -0,0 +1,106 @@ + + diff --git a/ZR.Vue/src/views/tool/build/DraggableItem.vue b/ZR.Vue/src/views/tool/build/DraggableItem.vue new file mode 100644 index 0000000..e881778 --- /dev/null +++ b/ZR.Vue/src/views/tool/build/DraggableItem.vue @@ -0,0 +1,100 @@ + diff --git a/ZR.Vue/src/views/tool/build/IconsDialog.vue b/ZR.Vue/src/views/tool/build/IconsDialog.vue new file mode 100644 index 0000000..958be50 --- /dev/null +++ b/ZR.Vue/src/views/tool/build/IconsDialog.vue @@ -0,0 +1,123 @@ + + + diff --git a/ZR.Vue/src/views/tool/build/RightPanel.vue b/ZR.Vue/src/views/tool/build/RightPanel.vue new file mode 100644 index 0000000..abaec43 --- /dev/null +++ b/ZR.Vue/src/views/tool/build/RightPanel.vue @@ -0,0 +1,944 @@ + + + + + diff --git a/ZR.Vue/src/views/tool/build/TreeNodeDialog.vue b/ZR.Vue/src/views/tool/build/TreeNodeDialog.vue new file mode 100644 index 0000000..c225c4c --- /dev/null +++ b/ZR.Vue/src/views/tool/build/TreeNodeDialog.vue @@ -0,0 +1,149 @@ + + diff --git a/ZR.Vue/src/views/tool/build/index.vue b/ZR.Vue/src/views/tool/build/index.vue new file mode 100644 index 0000000..92291e9 --- /dev/null +++ b/ZR.Vue/src/views/tool/build/index.vue @@ -0,0 +1,789 @@ + + + + + diff --git a/ZR.Vue/src/views/tool/gen/basicInfoForm.vue b/ZR.Vue/src/views/tool/gen/basicInfoForm.vue new file mode 100644 index 0000000..757962c --- /dev/null +++ b/ZR.Vue/src/views/tool/gen/basicInfoForm.vue @@ -0,0 +1,61 @@ + + diff --git a/ZR.Vue/src/views/tool/gen/importTable.vue b/ZR.Vue/src/views/tool/gen/importTable.vue new file mode 100644 index 0000000..56b2c0c --- /dev/null +++ b/ZR.Vue/src/views/tool/gen/importTable.vue @@ -0,0 +1,117 @@ + + + diff --git a/ZR.Vue/src/views/tool/swagger/index.vue b/ZR.Vue/src/views/tool/swagger/index.vue new file mode 100644 index 0000000..c6ca3b4 --- /dev/null +++ b/ZR.Vue/src/views/tool/swagger/index.vue @@ -0,0 +1,26 @@ +