diff --git a/Bootstrap.Admin/BootstrapAdmin.db b/Bootstrap.Admin/BootstrapAdmin.db index 415a47b1..46aab330 100644 Binary files a/Bootstrap.Admin/BootstrapAdmin.db and b/Bootstrap.Admin/BootstrapAdmin.db differ diff --git a/Bootstrap.Admin/Controllers/AccountController.cs b/Bootstrap.Admin/Controllers/AccountController.cs index d55d294d..93dccc9b 100644 --- a/Bootstrap.Admin/Controllers/AccountController.cs +++ b/Bootstrap.Admin/Controllers/AccountController.cs @@ -33,7 +33,7 @@ namespace Bootstrap.Admin.Controllers ViewBag.UserName = "Admin"; ViewBag.Password = "123789"; } - return User.Identity.IsAuthenticated ? (ActionResult)Redirect("~/Home/Index") : View("Login", new ModelBase()); + return User.Identity.IsAuthenticated ? (ActionResult)Redirect("~/Home/Index") : View("Login", new LoginModel()); } /// @@ -41,13 +41,14 @@ namespace Bootstrap.Admin.Controllers /// /// The login. /// + /// /// User name. /// Password. /// Remember. [HttpPost] - public async Task Login([FromServices]IOnlineUsers onlineUserSvr, string userName, string password, string remember) + public async Task Login([FromServices]IOnlineUsers onlineUserSvr, [FromServices]IIPLocatorProvider ipLocator, string userName, string password, string remember) { - if (UserHelper.Authenticate(userName, password, loginUser => CreateLoginUser(onlineUserSvr, HttpContext, loginUser))) + if (UserHelper.Authenticate(userName, password, loginUser => CreateLoginUser(onlineUserSvr, ipLocator, HttpContext, loginUser))) { var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.Name, userName)); @@ -55,21 +56,22 @@ namespace Bootstrap.Admin.Controllers // redirect origin url var originUrl = Request.Query[CookieAuthenticationDefaults.ReturnUrlParameter].FirstOrDefault() ?? "~/Home/Index"; return Redirect(originUrl); - } - return View("Login", new ModelBase()); + } + return View("Login", new LoginModel()); } /// /// /// /// + /// /// /// - internal static void CreateLoginUser(IOnlineUsers onlineUserSvr, HttpContext context, LoginUser loginUser) + internal static void CreateLoginUser(IOnlineUsers onlineUserSvr, IIPLocatorProvider ipLocator, HttpContext context, LoginUser loginUser) { var agent = new UserAgent(context.Request.Headers["User-Agent"]); loginUser.Ip = (context.Connection.RemoteIpAddress ?? IPAddress.IPv6Loopback).ToString(); - loginUser.City = onlineUserSvr.RetrieveLocaleByIp(loginUser.Ip); + loginUser.City = ipLocator.Locate(loginUser.Ip); loginUser.Browser = $"{agent.Browser.Name} {agent.Browser.Version}"; loginUser.OS = $"{agent.OS.Name} {agent.OS.Version}"; } diff --git a/Bootstrap.Admin/Controllers/Api/LoginController.cs b/Bootstrap.Admin/Controllers/Api/LoginController.cs index 2b5596f9..373e10d1 100644 --- a/Bootstrap.Admin/Controllers/Api/LoginController.cs +++ b/Bootstrap.Admin/Controllers/Api/LoginController.cs @@ -1,5 +1,6 @@ using Bootstrap.DataAccess; using Bootstrap.Security; +using Longbow.Web; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json.Linq; @@ -28,17 +29,18 @@ namespace Bootstrap.Admin.Controllers.Api /// /// /// + /// /// /// [AllowAnonymous] [HttpPost] - public string Post([FromServices]IOnlineUsers onlineUserSvr, [FromBody]JObject value) + public string Post([FromServices]IOnlineUsers onlineUserSvr, [FromServices]IIPLocatorProvider ipLocator, [FromBody]JObject value) { string token = null; dynamic user = value; string userName = user.userName; string password = user.password; - if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password) && UserHelper.Authenticate(userName, password, loginUser => AccountController.CreateLoginUser(onlineUserSvr, HttpContext, loginUser))) + if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password) && UserHelper.Authenticate(userName, password, loginUser => AccountController.CreateLoginUser(onlineUserSvr, ipLocator, HttpContext, loginUser))) { token = BootstrapAdminJwtTokenHandler.CreateToken(userName); } diff --git a/Bootstrap.Admin/Controllers/Api/LogsController.cs b/Bootstrap.Admin/Controllers/Api/LogsController.cs index 60c74cb3..d96b8ab2 100644 --- a/Bootstrap.Admin/Controllers/Api/LogsController.cs +++ b/Bootstrap.Admin/Controllers/Api/LogsController.cs @@ -29,16 +29,17 @@ namespace Bootstrap.Admin.Controllers.Api /// /// /// + /// /// /// [HttpPost] - public bool Post([FromServices]IOnlineUsers onlineUserSvr, [FromBody]Log value) + public bool Post([FromServices]IOnlineUsers onlineUserSvr, [FromServices]IIPLocatorProvider ipLocator, [FromBody]Log value) { var agent = new UserAgent(Request.Headers["User-Agent"]); value.Ip = (HttpContext.Connection.RemoteIpAddress ?? IPAddress.IPv6Loopback).ToString(); value.Browser = $"{agent.Browser.Name} {agent.Browser.Version}"; value.OS = $"{agent.OS.Name} {agent.OS.Version}"; - value.City = onlineUserSvr.RetrieveLocaleByIp(value.Ip); + value.City = ipLocator.Locate(value.Ip); value.UserName = User.Identity.Name; return LogHelper.Save(value); } diff --git a/Bootstrap.Admin/Controllers/Api/OnlineUsersController.cs b/Bootstrap.Admin/Controllers/Api/OnlineUsersController.cs index 2d9c7665..3fa52cc7 100644 --- a/Bootstrap.Admin/Controllers/Api/OnlineUsersController.cs +++ b/Bootstrap.Admin/Controllers/Api/OnlineUsersController.cs @@ -1,3 +1,4 @@ +using Longbow.Web; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System; diff --git a/Bootstrap.Admin/Models/LoginModel.cs b/Bootstrap.Admin/Models/LoginModel.cs new file mode 100644 index 00000000..af634097 --- /dev/null +++ b/Bootstrap.Admin/Models/LoginModel.cs @@ -0,0 +1,23 @@ +using Bootstrap.DataAccess; + +namespace Bootstrap.Admin.Models +{ + /// + /// + /// + public class LoginModel : ModelBase + { + /// + /// + /// + public LoginModel() + { + ImageLibUrl = DictHelper.RetrieveImagesLibUrl(); + } + + /// + /// 验证码图床地址 + /// + public string ImageLibUrl { get; protected set; } + } +} diff --git a/Bootstrap.Admin/OnlineUsers/DefaultOnlineUsers.cs b/Bootstrap.Admin/OnlineUsers/DefaultOnlineUsers.cs deleted file mode 100644 index 6f92fc7b..00000000 --- a/Bootstrap.Admin/OnlineUsers/DefaultOnlineUsers.cs +++ /dev/null @@ -1,187 +0,0 @@ -using Bootstrap.DataAccess; -using Longbow.Cache; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; - -namespace Bootstrap.Admin -{ - /// - /// - /// - internal class DefaultOnlineUsers : IOnlineUsers - { - private readonly ConcurrentDictionary> _onlineUsers = new ConcurrentDictionary>(); - private readonly ConcurrentDictionary> _ipLocator = new ConcurrentDictionary>(); - private readonly HttpClient _client; - private readonly IEnumerable _local = new string[] { "::1", "127.0.0.1" }; - /// - /// - /// - /// - public DefaultOnlineUsers(IHttpClientFactory factory) - { - _client = factory.CreateClient(OnlineUsersServicesCollectionExtensions.IPSvrHttpClientName); - } - - /// - /// - /// - /// - public IEnumerable OnlineUsers - { - get { return _onlineUsers.Values.Select(v => v.Value); } - } - - /// - /// - /// - /// - /// - /// - /// - public AutoExpireCacheEntry AddOrUpdate(string key, Func> addValueFactory, Func, AutoExpireCacheEntry> updateValueFactory) => _onlineUsers.AddOrUpdate(key, addValueFactory, updateValueFactory); - - /// - /// - /// - /// - /// - /// - public bool TryRemove(string key, out AutoExpireCacheEntry onlineUserCache) => _onlineUsers.TryRemove(key, out onlineUserCache); - - /// - /// - /// - /// - /// - public string RetrieveLocaleByIp(string ip = null) => _ipLocator.GetOrAdd(ip, key => new AutoExpireCacheEntry(IPProxy(key), 1000 * 60, __ => _ipLocator.TryRemove(key, out _))).Value; - - private string IPProxy(string ip) - { - var ipSvr = DictHelper.RetrieveLocaleIPSvr(); - if (ipSvr.IsNullOrEmpty() || ipSvr.Equals("None", StringComparison.OrdinalIgnoreCase) || ip.IsNullOrEmpty() || _local.Any(p => p == ip)) return "本地连接"; - - var ipSvrUrl = DictHelper.RetrieveLocaleIPSvrUrl(ipSvr); - if (ipSvrUrl.IsNullOrEmpty()) return "本地连接"; - - var url = $"{ipSvrUrl}{ip}"; - var task = ipSvr == "BaiDuIPSvr" ? RetrieveLocator(url) : RetrieveLocator(url); - task.Wait(); - return task.Result; - } - - private async Task RetrieveLocator(string url) - { - var ret = default(T); - try - { - ret = await _client.GetAsJsonAsync(url); - } - catch (Exception ex) - { - ex.Log(new NameValueCollection() - { - ["Url"] = url - }); - } - return ret?.ToString(); - } - - /// - /// - /// - private class BaiDuIPLocator - { - /// - /// 详细地址信息 - /// - public string Address { get; set; } - - /// - /// 结果状态返回码 - /// - public string Status { get; set; } - - /// - /// - /// - /// - public override string ToString() - { - return Status == "0" ? string.Join(" ", Address.SpanSplit("|").Skip(1).Take(2)) : "XX XX"; - } - } - - private class JuheIPLocator - { - /// - /// - /// - public string ResultCode { get; set; } - - /// - /// - /// - public string Reason { get; set; } - - /// - /// - /// - public JuheIPLocatorResult Result { get; set; } - - /// - /// - /// - /// - public int Error_Code { get; set; } - - /// - /// - /// - /// - public override string ToString() - { - return Error_Code != 0 ? "XX XX" : Result.ToString(); - } - } - - private class JuheIPLocatorResult - { - /// - /// - /// - public string Country { get; set; } - - /// - /// - /// - public string Province { get; set; } - - /// - /// - /// - public string City { get; set; } - - /// - /// - /// - public string Isp { get; set; } - - /// - /// - /// - /// - public override string ToString() - { - return Country != "中国" ? $"{Country} {Province} {Isp}" : $"{Province} {City} {Isp}"; - } - } - } -} diff --git a/Bootstrap.Admin/OnlineUsers/IOnlineUsers.cs b/Bootstrap.Admin/OnlineUsers/IOnlineUsers.cs deleted file mode 100644 index 08d134f2..00000000 --- a/Bootstrap.Admin/OnlineUsers/IOnlineUsers.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Longbow.Cache; -using System; -using System.Collections.Generic; - -namespace Bootstrap.Admin -{ - /// - /// - /// - public interface IOnlineUsers - { - /// - /// - /// - IEnumerable OnlineUsers { get; } - - /// - /// - /// - /// - /// - /// - /// - AutoExpireCacheEntry AddOrUpdate(string key, Func> addValueFactory, Func, AutoExpireCacheEntry> updateValueFactory); - - /// - /// - /// - /// - /// - /// - bool TryRemove(string key, out AutoExpireCacheEntry onlineUserCache); - - /// - /// - /// - /// - /// - string RetrieveLocaleByIp(string ip = null); - } -} diff --git a/Bootstrap.Admin/OnlineUsers/OnlineUser.cs b/Bootstrap.Admin/OnlineUsers/OnlineUser.cs deleted file mode 100644 index 35d3f8f4..00000000 --- a/Bootstrap.Admin/OnlineUsers/OnlineUser.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; - -namespace Bootstrap.Admin -{ - - /// - /// - /// - public class OnlineUser - { - private ConcurrentQueue> _requestUrls = new ConcurrentQueue>(); - - /// - /// - /// - public string ConnectionId { get; set; } - - /// - /// - /// - public string UserName { get; set; } - - /// - /// - /// - public string DisplayName { get; set; } - - /// - /// - /// - public DateTime FirstAccessTime { get; set; } - - /// - /// - /// - public DateTime LastAccessTime { get; set; } - - /// - /// - /// - public string Location { get; set; } - - /// - /// - /// - public string Method { get; set; } - - /// - /// - /// - public string Ip { get; set; } - - /// - /// - /// - public string Browser { get; set; } - - /// - /// - /// - public string OS { get; set; } - - /// - /// - /// - public string RequestUrl { get; set; } - - /// - /// - /// - public IEnumerable> RequestUrls - { - get - { - return _requestUrls.ToArray(); - } - } - - /// - /// - /// - /// - public void AddRequestUrl(string url) - { - _requestUrls.Enqueue(new KeyValuePair(DateTime.Now, url)); - if (_requestUrls.Count > 5) - { - _requestUrls.TryDequeue(out _); - } - } - } -} diff --git a/Bootstrap.Admin/OnlineUsers/OnlineUsersMiddlewareExtensions.cs b/Bootstrap.Admin/OnlineUsers/OnlineUsersMiddlewareExtensions.cs deleted file mode 100644 index d4792ba8..00000000 --- a/Bootstrap.Admin/OnlineUsers/OnlineUsersMiddlewareExtensions.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Bootstrap.Admin; -using Bootstrap.DataAccess; -using Longbow.Cache; -using Longbow.Web; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Linq; -using System.Net; - -namespace Microsoft.AspNetCore.Builder -{ - /// - /// - /// - public static class OnlineUsersMiddlewareExtensions - { - /// - /// - /// - /// - /// - public static IApplicationBuilder UseOnlineUsers(this IApplicationBuilder builder) => builder.UseWhen(context => context.Filter(), app => app.Use(async (context, next) => - { - await System.Threading.Tasks.Task.Run(() => - { - var user = UserHelper.RetrieveUserByUserName(context.User.Identity.Name); - if (user == null) return; - - var onlineUserSvr = context.RequestServices.GetRequiredService(); - var proxy = new Func, Action, AutoExpireCacheEntry>((c, action) => - { - var v = c.Value; - v.UserName = user.UserName; - v.DisplayName = user.DisplayName; - v.LastAccessTime = DateTime.Now; - v.Method = context.Request.Method; - v.RequestUrl = context.Request.Path; - v.AddRequestUrl(context.Request.Path); - action?.Invoke(); - TraceHelper.Save(new Trace { Ip = v.Ip, RequestUrl = v.RequestUrl, LogTime = v.LastAccessTime, City = v.Location, Browser = v.Browser, OS = v.OS, UserName = v.UserName }); - return c; - }); - onlineUserSvr.AddOrUpdate(context.Connection.Id ?? "", key => - { - var agent = new UserAgent(context.Request.Headers["User-Agent"]); - var v = new OnlineUser(); - v.ConnectionId = key; - v.Ip = (context.Connection.RemoteIpAddress ?? IPAddress.IPv6Loopback).ToString(); - v.Location = onlineUserSvr.RetrieveLocaleByIp(v.Ip); - v.Browser = $"{agent.Browser.Name} {agent.Browser.Version}"; - v.OS = $"{agent.OS.Name} {agent.OS.Version}"; - v.FirstAccessTime = DateTime.Now; - return proxy(new AutoExpireCacheEntry(v, 1000 * 60, __ => onlineUserSvr.TryRemove(key, out _)), null); - }, (key, v) => proxy(v, () => v.Reset())); - }); - await next(); - })); - - private static bool Filter(this HttpContext context) - { - var url = context.Request.Path; - return !new string[] { "/api", "/NotiHub", "/swagger" }.Any(r => url.StartsWithSegments(r, StringComparison.OrdinalIgnoreCase)); - } - } -} diff --git a/Bootstrap.Admin/OnlineUsers/OnlineUsersServicesCollectionExtensions.cs b/Bootstrap.Admin/OnlineUsers/OnlineUsersServicesCollectionExtensions.cs deleted file mode 100644 index 6bbfa3f5..00000000 --- a/Bootstrap.Admin/OnlineUsers/OnlineUsersServicesCollectionExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Bootstrap.Admin; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Microsoft.Extensions.DependencyInjection -{ - /// - /// - /// - public static class OnlineUsersServicesCollectionExtensions - { - /// - /// - /// - internal const string IPSvrHttpClientName = "IPSvr"; - - /// - /// - /// - /// - /// - public static IServiceCollection AddOnlineUsers(this IServiceCollection services) - { - services.TryAddSingleton(); - services.AddHttpClient(IPSvrHttpClientName, client => - { - client.DefaultRequestHeaders.Connection.Add("keep-alive"); - }); - return services; - } - } -} diff --git a/Bootstrap.Admin/Startup.cs b/Bootstrap.Admin/Startup.cs index 7bf5ee87..c95dd5ba 100644 --- a/Bootstrap.Admin/Startup.cs +++ b/Bootstrap.Admin/Startup.cs @@ -1,4 +1,4 @@ -using Bootstrap.DataAccess; +using Bootstrap.DataAccess; using Bootstrap.Security.Filter; using Longbow.Web; using Longbow.Web.SignalR; @@ -60,6 +60,7 @@ namespace Bootstrap.Admin services.AddConfigurationManager(Configuration); services.AddCacheManager(Configuration); services.AddDbAdapter(); + services.AddIPLocator(DictHelper.ConfigIPLocator); services.AddOnlineUsers(); var dataProtectionBuilder = services.AddDataProtection(op => op.ApplicationDiscriminator = Configuration["ApplicationDiscriminator"]) .SetApplicationName(Configuration["ApplicationName"]) @@ -127,7 +128,7 @@ namespace Bootstrap.Admin app.UseStaticFiles(); app.UseAuthentication(); app.UseBootstrapAdminAuthorization(RoleHelper.RetrieveRolesByUserName, RoleHelper.RetrieveRolesByUrl, AppHelper.RetrievesByUserName); - app.UseOnlineUsers(); + app.UseOnlineUsers(callback: TraceHelper.Save); app.UseCacheManagerCorsHandler(); app.UseSignalR(routes => { routes.MapHub("/NotiHub"); }); app.UseMvc(routes => diff --git a/Bootstrap.Admin/Views/Account/Login.cshtml b/Bootstrap.Admin/Views/Account/Login.cshtml index c60ab59e..1d13da13 100644 --- a/Bootstrap.Admin/Views/Account/Login.cshtml +++ b/Bootstrap.Admin/Views/Account/Login.cshtml @@ -1,219 +1,220 @@ -@model ModelBase -@{ - ViewBag.Title = Model.Title; - Layout = "_Layout"; -} -@section css { - - - - - - - - - - - - - - - @if (!string.IsNullOrEmpty(Model.Theme)) - { - - } -} -@section javascript { - - - - - - - - - - - - - - - - -} -
- -
- - +@model LoginModel +@{ + ViewBag.Title = Model.Title; + Layout = "_Layout"; +} +@section css { + + + + + + + + + + + + + + + @if (!string.IsNullOrEmpty(Model.Theme)) + { + + } +} +@section javascript { + + + + + + + + + + + + + + + + +} +
+ + +
+ + diff --git a/Bootstrap.Admin/wwwroot/css/site.css b/Bootstrap.Admin/wwwroot/css/site.css index ed01f5f5..f55e0c25 100644 --- a/Bootstrap.Admin/wwwroot/css/site.css +++ b/Bootstrap.Admin/wwwroot/css/site.css @@ -34,7 +34,7 @@ .nav [data-toggle="dropdown"] { border-radius: 4px; padding: 6px 12px; - transition: color .25s linear, border-color .25s linear; + transition: all .25s linear; } .nav .dropdown-menu { diff --git a/Bootstrap.Admin/wwwroot/js/login.js b/Bootstrap.Admin/wwwroot/js/login.js index d6de20b6..f2581b3f 100644 --- a/Bootstrap.Admin/wwwroot/js/login.js +++ b/Bootstrap.Admin/wwwroot/js/login.js @@ -1,4 +1,5 @@ $(function () { + var $imgUrl = $('#imgUrl'); $(".container").autoCenter(); $("a[data-method]").on('click', function () { @@ -94,8 +95,11 @@ $('#captcha, #regcap, #forgotcap').sliderCaptcha({ width: $.capWidth(), height: $.capHeight(), + localImages: function () { + return '../../lib/captcha/images/Pic' + Math.round(Math.random() * 4) + '.jpg'; + }, setSrc: function () { - return 'http://pocoafrro.bkt.clouddn.com/Pic' + Math.round(Math.random() * 136) + '.jpg'; + return $imgUrl.val() + 'Pic' + Math.round(Math.random() * 136) + '.jpg'; }, onSuccess: function () { var parent = this.parents('.slidercaptcha').removeClass('d-block'); diff --git a/Bootstrap.Admin/wwwroot/images/Pic0.jpg b/Bootstrap.Admin/wwwroot/lib/captcha/images/Pic0.jpg similarity index 100% rename from Bootstrap.Admin/wwwroot/images/Pic0.jpg rename to Bootstrap.Admin/wwwroot/lib/captcha/images/Pic0.jpg diff --git a/Bootstrap.Admin/wwwroot/images/Pic1.jpg b/Bootstrap.Admin/wwwroot/lib/captcha/images/Pic1.jpg similarity index 100% rename from Bootstrap.Admin/wwwroot/images/Pic1.jpg rename to Bootstrap.Admin/wwwroot/lib/captcha/images/Pic1.jpg diff --git a/Bootstrap.Admin/wwwroot/images/Pic2.jpg b/Bootstrap.Admin/wwwroot/lib/captcha/images/Pic2.jpg similarity index 100% rename from Bootstrap.Admin/wwwroot/images/Pic2.jpg rename to Bootstrap.Admin/wwwroot/lib/captcha/images/Pic2.jpg diff --git a/Bootstrap.Admin/wwwroot/images/Pic3.jpg b/Bootstrap.Admin/wwwroot/lib/captcha/images/Pic3.jpg similarity index 100% rename from Bootstrap.Admin/wwwroot/images/Pic3.jpg rename to Bootstrap.Admin/wwwroot/lib/captcha/images/Pic3.jpg diff --git a/Bootstrap.Admin/wwwroot/images/Pic4.jpg b/Bootstrap.Admin/wwwroot/lib/captcha/images/Pic4.jpg similarity index 100% rename from Bootstrap.Admin/wwwroot/images/Pic4.jpg rename to Bootstrap.Admin/wwwroot/lib/captcha/images/Pic4.jpg diff --git a/Bootstrap.Admin/wwwroot/lib/captcha/longbow.slidercaptcha.js b/Bootstrap.Admin/wwwroot/lib/captcha/longbow.slidercaptcha.js index a3677a7c..800c4380 100644 --- a/Bootstrap.Admin/wwwroot/lib/captcha/longbow.slidercaptcha.js +++ b/Bootstrap.Admin/wwwroot/lib/captcha/longbow.slidercaptcha.js @@ -1,4 +1,4 @@ -(function ($) { +(function ($) { 'use strict'; var SliderCaptcha = function (element, options) { @@ -16,11 +16,15 @@ PI: Math.PI, sliderL: 42, // 滑块边长 sliderR: 9, // 滑块半径 + offset: 5, // 容错偏差 loadingText: '正在加载中...', failedText: '再试一次', barText: '向右滑动填充拼图', repeatIcon: 'fa fa-repeat', - maxLoadCount: 3 + maxLoadCount: 3, + localImages: function () { + return 'images/Pic' + Math.round(Math.random() * 4) + '.jpg'; + } }; function Plugin(option) { @@ -40,9 +44,9 @@ var _proto = SliderCaptcha.prototype; _proto.init = function () { - this.initDOM() - this.initImg() - this.bindEvents() + this.initDOM(); + this.initImg(); + this.bindEvents(); }; _proto.initDOM = function () { @@ -127,9 +131,6 @@ var getRandomNumberByRange = function (start, end) { return Math.round(Math.random() * (end - start) + start); }; - var localImg = function () { - return '../images/Pic' + Math.round(Math.random() * 4) + '.jpg'; - }; var img = new Image(); img.crossOrigin = "Anonymous"; var loadCount = 0; @@ -158,8 +159,8 @@ that.text.text('加载失败').addClass('text-danger'); return; } - img.src = localImg(); - } + img.src = that.options.localImages(); + }; img.setSrc = function () { var src = ''; loadCount = 0; @@ -274,7 +275,7 @@ var stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length); var left = parseInt(this.block.style.left); return { - spliced: Math.abs(left - this.x) < 4, + spliced: Math.abs(left - this.x) < this.options.offset, verified: stddev !== 0, // 简单验证下拖动轨迹,为零时表示Y轴上下没有波动,可能非人为操作 } }; diff --git a/Bootstrap.Client.DataAccess/Bootstrap.Client.DataAccess.csproj b/Bootstrap.Client.DataAccess/Bootstrap.Client.DataAccess.csproj index 466392b1..5ab729f0 100644 --- a/Bootstrap.Client.DataAccess/Bootstrap.Client.DataAccess.csproj +++ b/Bootstrap.Client.DataAccess/Bootstrap.Client.DataAccess.csproj @@ -1,21 +1,21 @@ - - - - netstandard2.0 - true - ..\Keys\Longbow.Utility.snk - - - - - - - - - - - - - - - + + + + netstandard2.0 + true + ..\Keys\Longbow.Utility.snk + + + + + + + + + + + + + + + diff --git a/Bootstrap.Client.DataAccess/DictHelper.cs b/Bootstrap.Client.DataAccess/DictHelper.cs index b4ca3970..70db656e 100644 --- a/Bootstrap.Client.DataAccess/DictHelper.cs +++ b/Bootstrap.Client.DataAccess/DictHelper.cs @@ -82,5 +82,11 @@ namespace Bootstrap.Client.DataAccess /// /// public static string RetrieveIconFolderPath() => (RetrieveDicts().FirstOrDefault(d => d.Name == "头像路径" && d.Category == "头像地址" && d.Define == 0) ?? new BootstrapDict() { Code = "~/images/uploader/" }).Code; + + /// + /// 获取验证码图床 + /// + /// + public static string RetrieveImagesLibUrl() => (RetrieveDicts().FirstOrDefault(d => d.Name == "验证码图床" && d.Category == "系统设置" && d.Define == 0) ?? new BootstrapDict() { Code = "https://longbow-1258823021.cos.ap-shanghai.myqcloud.com/pic/280/150/" }).Code; } } diff --git a/Bootstrap.Client/Models/NavigatorBarModel.cs b/Bootstrap.Client/Models/NavigatorBarModel.cs index 671c38de..27889239 100644 --- a/Bootstrap.Client/Models/NavigatorBarModel.cs +++ b/Bootstrap.Client/Models/NavigatorBarModel.cs @@ -17,10 +17,17 @@ namespace Bootstrap.Client.Models public NavigatorBarModel(ControllerBase controller) : base(controller.User.Identity) { Navigations = MenuHelper.RetrieveAppMenus(UserName, $"~/{controller.ControllerContext.ActionDescriptor.ControllerName}/{controller.ControllerContext.ActionDescriptor.ActionName}"); + ImageLibUrl = DictHelper.RetrieveImagesLibUrl(); } + /// /// /// public IEnumerable Navigations { get; } + + /// + /// + /// + public string ImageLibUrl { get; set; } } } \ No newline at end of file diff --git a/Bootstrap.Client/Views/Home/About.cshtml b/Bootstrap.Client/Views/Home/About.cshtml index 2711273b..3808999e 100644 --- a/Bootstrap.Client/Views/Home/About.cshtml +++ b/Bootstrap.Client/Views/Home/About.cshtml @@ -1,4 +1,4 @@ -@{ - ViewData["Title"] = "测试网页"; -} -

测试网页

\ No newline at end of file +@{ + ViewData["Title"] = "关于"; +} +

我是关于网页

\ No newline at end of file diff --git a/Bootstrap.Client/Views/Home/Index.cshtml b/Bootstrap.Client/Views/Home/Index.cshtml index 6997f02b..45aa67eb 100644 --- a/Bootstrap.Client/Views/Home/Index.cshtml +++ b/Bootstrap.Client/Views/Home/Index.cshtml @@ -1,6 +1,20 @@ -@{ - ViewData["Title"] = "Home Page"; +@model NavigatorBarModel +@{ + ViewData["Title"] = "前台首页"; } -

这是测试系统首页,欢迎测试使用

-

点击右上角登录信息下拉菜单中的设置按钮进入后台管理

-

由于本系统为演示系统,内部对一些敏感操作进行了限制操作,如一些特殊用户不能删除。

\ No newline at end of file +@section css { + +} +@section javascript { + + +} +

这是开源后台管理框架前台系统首页,欢迎使用

+

点击右上角登录信息下拉菜单中的设置按钮进入 后台管理 或者 直接进入

+

由于本系统为演示系统,内部对一些敏感操作进行了限制操作,如一些特殊用户不能删除。

+

+ +

+
+
+

\ No newline at end of file diff --git a/Bootstrap.Client/wwwroot/css/theme.css b/Bootstrap.Client/wwwroot/css/theme.css index 05402d83..44bfd954 100644 --- a/Bootstrap.Client/wwwroot/css/theme.css +++ b/Bootstrap.Client/wwwroot/css/theme.css @@ -1,613 +1,612 @@ -html { - font-size: 16px; - -ms-overflow-style: auto; -} - -body { - color: #797979; - background: #f1f2f7; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -webkit-overflow-scrolling: touch; -} - -a, a:hover, a:focus { - text-decoration: none; - outline: none; -} - -body, .form-control, .dropdown-menu, .btn:not(.btn-lg):not(.btn-xs), .input-group-text, .popover-header { - font-size: 0.875rem; -} - - .form-control:focus { - border-color: #66afe9; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6); - } - -.container-fluid { - padding-top: 15px; -} - -.sidebar-open aside { - transform: translate(0); -} - -.sidebar-open footer, .sidebar-open .container-fluid { - display: none; -} - -.sidebar { - padding: 20px 0; - background: inherit; -} - - .sidebar .nav-item { - margin: 5px 10px; - } - - .sidebar .nav-item .nav-link:hover, .sidebar .nav-item .nav-link:focus { - background: #35404d; - color: #fff; - } - - .sidebar .nav-item.active > .nav-link { - color: #FF6C60; - } - - .sidebar .nav-link { - color: #aeb2b7; - padding: 15px 10px; - transition: all .3s linear; - display: flex; - align-items: center; - } - - .sidebar .nav-link .dcjq-icon { - height: 17px; - width: 17px; - background: url(../images/nav-expand.png) no-repeat; - margin-left: auto; - } - - .sidebar .nav-link i { - width: 22px; - } - - .sidebar .nav-link.active, .sidebar .nav-link.active + .sub, - .sidebar .sub .sub .nav-item .nav-link:hover, .sidebar .sub .sub .nav-item .nav-link:focus { - background: #35404D; - } - - .sidebar .nav-link.active .dcjq-icon { - background-position: bottom; - } - - .sidebar .sub .nav-item.dcjq-parent-li { - margin-left: 0; - margin-right: 0; - } - - .sidebar .sub .nav-item.dcjq-parent-li .nav-link { - padding-left: 20px; - } - - .sidebar .sub .dcjq-parent-li .nav-link.active, .sidebar .sub .dcjq-parent-li .nav-link.active + .sub, - .sidebar .sub .nav-item .nav-link:hover, .sidebar .sub .nav-item .nav-link:focus { - background: #3a4756; - } - -.sidebar-toggle-box { - display: block; - font-size: 1.25rem; - color: #eee; - flex: 1 1 auto; - padding: 12px 0; - transition: color .3s linear; -} - - .sidebar-toggle-box:hover { - color: #fff; - } - - .sidebar-toggle-box span { - display: none; - } - -aside { - transition: transform .4s ease-in-out; - transform: translate(-100%); - position: absolute; - top: 92px; - bottom: 0; - left: 0; - right: 0; - z-index: 4; - background: #2a3542; -} - -.breadcrumb { - border-top: solid 1px #ddd; - background-color: transparent; - border-radius: 0; - padding: 8px 10px; - margin-bottom: 0; -} - - .breadcrumb i { - padding-right: 6px; - } - -footer { - background: #5b6e84; - color: #fff; - padding: 10px 4px; - height: 40px; - left: 0; - bottom: 0; - right: 0; - z-index: 100; - display: flex; -} - - footer > span { - flex: 1 1 auto; - text-align: center; - margin-left: 4px; - display: inline-block; - } - -.go-top { - background: rgba(255,255,255,.5); - width: 20px; - height: 20px; - border-radius: 50%; - -webkit-border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - margin-right: 4px; -} - - .go-top:hover { - background-color: white; - } - - .go-top i { - color: #2A3542; - } - -.bs-bars { - display: none; -} - -.btn-fill { - width: 100%; -} - -.toolbar { - position: relative; - margin-top: 10px; - margin-bottom: 10px; - line-height: 34px; -} - - .toolbar .dropdown-menu a { - padding: 0 14px; - display: table-cell; - color: #504d4d; - } - - .toolbar .dropdown-menu a:not(:first-child) { - border-left: solid 1px #aeb2b7; - } - - .toolbar .dropdown-menu a:hover { - color: #235e90; - } - -.input-group .btn:focus, .btn-group .btn:focus, .page-link:focus { - box-shadow: none; -} - -.input-group .btn:not(.btn-secondary):not(.btn-primary):not(.btn-warning):not(.btn-info):not(.btn-danger) { - background-color: #e9ecef; - border-color: #ced4da; -} - -.nav-link { - transition: color .25s linear; -} - -.close { - transition: all .25s linear; -} - - .close:focus { - outline: none; - } - -.dropdown-select + .dropdown-menu a:hover { - background: #007AC0; - color: #fff; -} - -.dropdown-menu { - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.176); - padding: 0; -} - - .dropdown-menu a { - transition: all .25s linear; - padding: 6px 20px; - display: block; - } - -.dropdown-divider { - margin: 0.125rem 0; -} - -.dropdown-item.active, .dropdown-item:active { - color: rgba(0, 0, 0, 0.9); - background-color: rgba(0, 0, 0, 0.05); -} - -.dropdown-item i + span { - margin-left: 6px; -} - -.tooltip-inner { - max-width: 768px; -} - -.is-invalid .tooltip-inner { - background-color: #dc3545; -} - -.is-invalid .arrow:before { - border-top-color: #dc3545; - border-bottom-color: #dc3545; -} - -.radioGroup, .checkGroup, [radioGroup="true"], [checkGroup="true"] { - border: solid 1px transparent; - border-radius: 4px; - padding: 1px 4px; -} - - .radioGroup.is-invalid, .checkGroup.is-invalid, [radioGroup="true"].is-invalid, [checkGroup="true"].is-invalid { - border: solid 1px #dc3545; - } - - .radioGroup.is-invalid .form-check-input ~ .form-check-label, - .checkGroup.is-invalid .form-check-input ~ .form-check-label, - [radioGroup="true"].is-invalid .form-check-input ~ .form-check-label, - [checkGroup="true"].is-invalid .form-check-input ~ .form-check-label { - color: #dc3545; - } - - .radioGroup.is-valid .form-check-input ~ .form-check-label, - [radioGroup="true"].is-valid .form-check-input ~ .form-check-label { - color: #28a745; - } - - .checkGroup.is-valid .form-check-input ~ .form-check-label, - [checkGroup="true"].is-valid .form-check-input ~ .form-check-label { - color: unset; - } - - -input.is-invalid { - background-repeat: no-repeat; - background-image: url('../images/error.png'); - background-position: right 8px center; -} - -input.is-valid { - background-repeat: no-repeat; - background-image: url('../images/success.png'); - background-position: right 8px center; -} - -input.pending { - background-repeat: no-repeat; - background-image: url(../images/loading-sm.gif); - background-position: right 8px center; -} - -.form-inline .form-group { - margin-bottom: 15px; -} - - .form-inline .form-group .control-label { - margin-right: 10px; - width: 90px; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - justify-content: flex-start; - } - -.form-inline .form-row, .form-inline .row { - flex: 1 1 auto; -} - -.modal-header { - background-color: #f5f5f5; - padding: 10px 15px; -} - -.modal-body, .modal-footer, .popover-body { - background-color: #fff; -} - -.modal-title { - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.modal-body, .card-body { - padding: 15px 15px 0 15px; -} - -.modal-footer .btn span:last-child { - display: inline; -} - -.card { - margin-bottom: 15px; -} - - .card .card-header a { - color: #797979; - } - - .card .card-header a[data-toggle="collapse"] { - display: block; - } - - .card .card-header a[data-toggle="collapse"] i { - transition: all .25s linear; - } - - .card .card-header a[data-toggle="collapse"].collapsed i { - transform: rotate(-90deg); - } - - .card .card-header i + span { - margin-left: 6px; - } - -.btn i.fa + span { - margin-left: 4px; - display: none; -} - -.form-check-label { - cursor: pointer; -} - -.form-check-input + span, .form-check-input + label { - max-width: 98px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - display: inline-block; -} - -.bootstrap-table .table thead > tr > th { - border-top: none; -} - -.fixed-table-toolbar .bs-bars, .fixed-table-toolbar .search, .fixed-table-toolbar .columns { - line-height: normal; -} - -.table-condensed > thead > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > tbody > tr > td { - padding: 2px 0; -} - -.fixed-table-loading { - padding: 8px; -} - -.bootstrap-table { - margin-bottom: 15px; -} - - .bootstrap-table .fixed-table-body .table { - border-bottom: none; - } - - .bootstrap-table .fixed-table-body .table td > a { - display: block; - } - - .bootstrap-table .fixed-table-toolbar .columns-right .btn-group .dropdown-menu { - padding-top: 8px; - } - - .bootstrap-table .fixed-table-container .card-view .value { - word-break: break-all; - } - - .bootstrap-table .fixed-table-container .card-view .value > a, .bootstrap-table .fixed-table-container .card-view .value > div:not(.form-check) { - min-width: 60px; - display: inline-block; - } - - .bootstrap-table .card-view:not(:last-child) { - margin-bottom: 6px; - } - -.card-body:not(.nobar) .bootstrap-table { - margin-top: -10px; -} - -.card-header a.fa { - transition: color .3s linear; -} - - .card-header a.fa:hover { - color: #333; - } - -.fixed-table-toolbar .dropdown-menu { - min-width: unset; -} - -.popover { - max-width: 320px; - padding: 0; -} - -.popover-content { - max-height: 240px; - overflow-y: auto; - overflow-x: hidden; -} - -.file-drop-zone.clickable:hover { - border: 1px dashed #999; -} - -[data-toggle="LgbValidate"] input[type="text"], [data-toggle="LgbValidate"] input[type="password"] { - padding-right: 30px; -} - -.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { - border-color: #dc3545; - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(220, 53, 69,.6); -} - -.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { - border-color: #28a745; - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(40, 167, 69,.6); -} - -.arrow-primary { - border-color: transparent transparent #007bff; -} - -.arrow-success { - border-color: transparent transparent #28a745; -} - -.arrow-info { - border-color: transparent transparent #17a2b8; -} - -.arrow-warning { - border-color: transparent transparent #ffc107; -} - -.arrow-danger { - border-color: transparent transparent #dc3545; -} - -.show .shadow-primary, .shadow-primary:hover { - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(0, 123, 255, 0.5); - border-color: #007bff !important; -} - -.show .shadow-success, .shadow-success:hover { - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(40, 167, 69, 0.5); - border-color: #28a745 !important; -} - -.show .shadow-info, .shadow-info:hover { - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(23, 162, 184, 0.5); - border-color: #17a2b8 !important; -} - -.show .shadow-warning, .shadow-warning:hover { - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(255, 193, 7, 0.5); - border-color: #ffc107 !important; -} - -.show .shadow-danger, .shadow-danger:hover { - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(220, 53, 69, 0.5); - border: 1px solid #dc3545 !important; -} - -.show .shadow-default, .shadow-default:hover { - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6); - border: 1px solid #337ab7; -} - -.mCSB_scrollTools { - width: 10px; -} - -.control-label > a { - margin-left: 6px; - color: #5bc0de; -} - -.date > input { - cursor: pointer; -} - -.datetimepicker { - padding: 4px; -} - - .datetimepicker table tr td span { - white-space: nowrap; - } - -.form-inline .form-group-dropdown { - display: flex; - flex-wrap: nowrap; -} - - .form-inline .form-group-dropdown .control-label { - padding: 6px 0; - margin: 0 10px 0 0; - } - - .form-inline .form-group-dropdown .dropdown, .form-inline .form-group-dropdown .dropup { - display: inline-block; - } - -.toggle .btn-default, .toggle.btn-default { - text-shadow: 0 1px 0 #fff; - background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); - background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); - background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - border-color: #ccc; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); -} - - .toggle .btn-default.active { - background-image: none; - background-color: #e0e0e0; - border-color: #dbdbdb; - } - - .toggle .btn-default.active:hover { - color: #333; - background-color: #d4d4d4; - border-color: #8c8c8c; - } - -.toggle.btn .toggle-handle { - display: inline-block; - margin-left: 0; -} - -.sweet-alert h2 { - margin-top: 32px; - margin-bottom: 16px; - font-size: 1.5rem; -} - -.sweet-alert .sa-button-container .btn-lg { - padding: 0.375rem 1.5rem; - font-size: 1rem; -} +html { + font-size: 16px; + -ms-overflow-style: auto; +} + +body { + color: #797979; + background: #f1f2f7; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -webkit-overflow-scrolling: touch; +} + +a, a:hover, a:focus { + text-decoration: none; + outline: none; +} + +body, .form-control, .dropdown-menu, .btn:not(.btn-lg):not(.btn-xs), .input-group-text, .popover-header { + font-size: 0.875rem; +} + + .form-control:focus { + border-color: #66afe9; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6); + } + +.container-fluid { + padding-top: 15px; +} + +.sidebar-open aside { + transform: translate(0); +} + +.sidebar-open footer, .sidebar-open .container-fluid { + display: none; +} + +.sidebar { + padding: 20px 0; + background: inherit; +} + + .sidebar .nav-item { + margin: 5px 10px; + } + + .sidebar .nav-item .nav-link:hover, .sidebar .nav-item .nav-link:focus { + background: #35404d; + color: #fff; + } + + .sidebar .nav-item.active > .nav-link { + color: #FF6C60; + } + + .sidebar .nav-link { + color: #aeb2b7; + padding: 15px 10px; + transition: all .3s linear; + display: flex; + align-items: center; + } + + .sidebar .nav-link .dcjq-icon { + height: 17px; + width: 17px; + background: url(../images/nav-expand.png) no-repeat; + margin-left: auto; + } + + .sidebar .nav-link i { + width: 22px; + } + + .sidebar .nav-link.active, .sidebar .nav-link.active + .sub, + .sidebar .sub .sub .nav-item .nav-link:hover, .sidebar .sub .sub .nav-item .nav-link:focus { + background: #35404D; + } + + .sidebar .nav-link.active .dcjq-icon { + background-position: bottom; + } + + .sidebar .sub .nav-item.dcjq-parent-li { + margin-left: 0; + margin-right: 0; + } + + .sidebar .sub .nav-item.dcjq-parent-li .nav-link { + padding-left: 20px; + } + + .sidebar .sub .dcjq-parent-li .nav-link.active, .sidebar .sub .dcjq-parent-li .nav-link.active + .sub, + .sidebar .sub .nav-item .nav-link:hover, .sidebar .sub .nav-item .nav-link:focus { + background: #3a4756; + } + +.sidebar-toggle-box { + display: block; + font-size: 1.25rem; + color: #eee; + flex: 1 1 auto; + padding: 12px 0; + transition: color .3s linear; +} + + .sidebar-toggle-box:hover { + color: #fff; + } + + .sidebar-toggle-box span { + display: none; + } + +aside { + transition: transform .4s ease-in-out; + transform: translate(-100%); + position: absolute; + top: 92px; + bottom: 0; + left: 0; + right: 0; + z-index: 4; + background: #2a3542; +} + +.breadcrumb { + border-top: solid 1px #ddd; + background-color: transparent; + border-radius: 0; + padding: 8px 10px; + margin-bottom: 0; +} + + .breadcrumb i { + padding-right: 6px; + } + +footer { + background: #5b6e84; + color: #fff; + padding: 10px 4px; + height: 40px; + left: 0; + bottom: 0; + right: 0; + z-index: 100; + display: flex; +} + + footer > span { + flex: 1 1 auto; + text-align: center; + margin-left: 4px; + display: inline-block; + } + +.go-top { + background: rgba(255,255,255,.5); + width: 20px; + height: 20px; + border-radius: 50%; + -webkit-border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-right: 4px; +} + + .go-top:hover { + background-color: white; + } + + .go-top i { + color: #2A3542; + } + +.bs-bars { + display: none; +} + +.btn-fill { + width: 100%; +} + +.toolbar { + position: relative; + margin-top: 10px; + margin-bottom: 10px; + line-height: 34px; +} + + .toolbar .dropdown-menu a { + padding: 0 14px; + display: table-cell; + color: #504d4d; + } + + .toolbar .dropdown-menu a:not(:first-child) { + border-left: solid 1px #aeb2b7; + } + + .toolbar .dropdown-menu a:hover { + color: #235e90; + } + +.input-group .btn:focus, .btn-group .btn:focus, .page-link:focus { + box-shadow: none; +} + +.input-group .btn:not(.btn-secondary):not(.btn-primary):not(.btn-warning):not(.btn-info):not(.btn-danger) { + background-color: #e9ecef; + border-color: #ced4da; +} + +.nav-link { + transition: color .25s linear; +} + +.close { + transition: all .25s linear; +} + + .close:focus { + outline: none; + } + +.dropdown-select + .dropdown-menu a:hover { + background: #007AC0; + color: #fff; +} + +.dropdown-menu { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.176); + padding: 0; +} + + .dropdown-menu a { + transition: all .25s linear; + padding: 6px 20px; + display: block; + } + +.dropdown-divider { + margin: 0.125rem 0; +} + +.dropdown-item.active, .dropdown-item:active { + color: rgba(0, 0, 0, 0.9); + background-color: rgba(0, 0, 0, 0.05); +} + +.dropdown-item i + span { + margin-left: 6px; +} + +.tooltip-inner { + max-width: 768px; +} + +.is-invalid .tooltip-inner { + background-color: #dc3545; +} + +.is-invalid .arrow:before { + border-top-color: #dc3545; + border-bottom-color: #dc3545; +} + +.radioGroup, .checkGroup, [radioGroup="true"], [checkGroup="true"] { + border: solid 1px transparent; + border-radius: 4px; + padding: 1px 4px; +} + + .radioGroup.is-invalid, .checkGroup.is-invalid, [radioGroup="true"].is-invalid, [checkGroup="true"].is-invalid { + border: solid 1px #dc3545; + } + + .radioGroup.is-invalid .form-check-input ~ .form-check-label, + .checkGroup.is-invalid .form-check-input ~ .form-check-label, + [radioGroup="true"].is-invalid .form-check-input ~ .form-check-label, + [checkGroup="true"].is-invalid .form-check-input ~ .form-check-label { + color: #dc3545; + } + + .radioGroup.is-valid .form-check-input ~ .form-check-label, + [radioGroup="true"].is-valid .form-check-input ~ .form-check-label { + color: #28a745; + } + + .checkGroup.is-valid .form-check-input ~ .form-check-label, + [checkGroup="true"].is-valid .form-check-input ~ .form-check-label { + color: unset; + } + + +input.is-invalid { + background-repeat: no-repeat; + background-image: url('../images/error.png'); + background-position: right 8px center; +} + +input.is-valid { + background-repeat: no-repeat; + background-image: url('../images/success.png'); + background-position: right 8px center; +} + +input.pending { + background-repeat: no-repeat; + background-image: url(../images/loading-sm.gif); + background-position: right 8px center; +} + +.form-inline .form-group { + margin-bottom: 15px; +} + + .form-inline .form-group .control-label { + margin-right: 10px; + width: 90px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + justify-content: flex-start; + } + +.form-inline .form-row, .form-inline .row { + flex: 1 1 auto; +} + +.modal-header { + background-color: #f5f5f5; + padding: 10px 15px; +} + +.modal-body, .modal-footer, .popover-body { + background-color: #fff; +} + +.modal-title { + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.modal-body, .card-body { + padding: 15px 15px 0 15px; +} + +.modal-footer .btn span:last-child { + display: inline; +} + +.card { + margin-bottom: 15px; +} + + .card .card-header a { + color: #797979; + } + + .card .card-header a[data-toggle="collapse"] { + display: block; + } + + .card .card-header a[data-toggle="collapse"] i { + transition: all .25s linear; + } + + .card .card-header a[data-toggle="collapse"].collapsed i { + transform: rotate(-90deg); + } + + .card .card-header i + span { + margin-left: 6px; + } + +.btn i.fa + span { + margin-left: 4px; +} + +.form-check-label { + cursor: pointer; +} + +.form-check-input + span, .form-check-input + label { + max-width: 98px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + display: inline-block; +} + +.bootstrap-table .table thead > tr > th { + border-top: none; +} + +.fixed-table-toolbar .bs-bars, .fixed-table-toolbar .search, .fixed-table-toolbar .columns { + line-height: normal; +} + +.table-condensed > thead > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > tbody > tr > td { + padding: 2px 0; +} + +.fixed-table-loading { + padding: 8px; +} + +.bootstrap-table { + margin-bottom: 15px; +} + + .bootstrap-table .fixed-table-body .table { + border-bottom: none; + } + + .bootstrap-table .fixed-table-body .table td > a { + display: block; + } + + .bootstrap-table .fixed-table-toolbar .columns-right .btn-group .dropdown-menu { + padding-top: 8px; + } + + .bootstrap-table .fixed-table-container .card-view .value { + word-break: break-all; + } + + .bootstrap-table .fixed-table-container .card-view .value > a, .bootstrap-table .fixed-table-container .card-view .value > div:not(.form-check) { + min-width: 60px; + display: inline-block; + } + + .bootstrap-table .card-view:not(:last-child) { + margin-bottom: 6px; + } + +.card-body:not(.nobar) .bootstrap-table { + margin-top: -10px; +} + +.card-header a.fa { + transition: color .3s linear; +} + + .card-header a.fa:hover { + color: #333; + } + +.fixed-table-toolbar .dropdown-menu { + min-width: unset; +} + +.popover { + max-width: 320px; + padding: 0; +} + +.popover-content { + max-height: 240px; + overflow-y: auto; + overflow-x: hidden; +} + +.file-drop-zone.clickable:hover { + border: 1px dashed #999; +} + +[data-toggle="LgbValidate"] input[type="text"], [data-toggle="LgbValidate"] input[type="password"] { + padding-right: 30px; +} + +.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { + border-color: #dc3545; + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(220, 53, 69,.6); +} + +.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { + border-color: #28a745; + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(40, 167, 69,.6); +} + +.arrow-primary { + border-color: transparent transparent #007bff; +} + +.arrow-success { + border-color: transparent transparent #28a745; +} + +.arrow-info { + border-color: transparent transparent #17a2b8; +} + +.arrow-warning { + border-color: transparent transparent #ffc107; +} + +.arrow-danger { + border-color: transparent transparent #dc3545; +} + +.show .shadow-primary, .shadow-primary:hover { + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(0, 123, 255, 0.5); + border-color: #007bff !important; +} + +.show .shadow-success, .shadow-success:hover { + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(40, 167, 69, 0.5); + border-color: #28a745 !important; +} + +.show .shadow-info, .shadow-info:hover { + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(23, 162, 184, 0.5); + border-color: #17a2b8 !important; +} + +.show .shadow-warning, .shadow-warning:hover { + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(255, 193, 7, 0.5); + border-color: #ffc107 !important; +} + +.show .shadow-danger, .shadow-danger:hover { + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(220, 53, 69, 0.5); + border: 1px solid #dc3545 !important; +} + +.show .shadow-default, .shadow-default:hover { + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6); + border: 1px solid #337ab7; +} + +.mCSB_scrollTools { + width: 10px; +} + +.control-label > a { + margin-left: 6px; + color: #5bc0de; +} + +.date > input { + cursor: pointer; +} + +.datetimepicker { + padding: 4px; +} + + .datetimepicker table tr td span { + white-space: nowrap; + } + +.form-inline .form-group-dropdown { + display: flex; + flex-wrap: nowrap; +} + + .form-inline .form-group-dropdown .control-label { + padding: 6px 0; + margin: 0 10px 0 0; + } + + .form-inline .form-group-dropdown .dropdown, .form-inline .form-group-dropdown .dropup { + display: inline-block; + } + +.toggle .btn-default, .toggle.btn-default { + text-shadow: 0 1px 0 #fff; + background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); + background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + border-color: #ccc; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); +} + + .toggle .btn-default.active { + background-image: none; + background-color: #e0e0e0; + border-color: #dbdbdb; + } + + .toggle .btn-default.active:hover { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c; + } + +.toggle.btn .toggle-handle { + display: inline-block; + margin-left: 0; +} + +.sweet-alert h2 { + margin-top: 32px; + margin-bottom: 16px; + font-size: 1.5rem; +} + +.sweet-alert .sa-button-container .btn-lg { + padding: 0.375rem 1.5rem; + font-size: 1rem; +} diff --git a/Bootstrap.Client/wwwroot/js/index.js b/Bootstrap.Client/wwwroot/js/index.js new file mode 100644 index 00000000..a0048af4 --- /dev/null +++ b/Bootstrap.Client/wwwroot/js/index.js @@ -0,0 +1,26 @@ +$(function () { + var $captcha = $('#captcha'); + + $captcha.sliderCaptcha({ + localImages: function () { + return '../../lib/captcha/images/Pic' + Math.round(Math.random() * 4) + '.jpg'; + }, + setSrc: function () { + return $captcha.attr('data-imageLibUrl') + 'Pic' + Math.round(Math.random() * 136) + '.jpg'; + }, + onSuccess: function () { + var that = this; + setTimeout(() => { + that.parent().removeClass('d-inline-block'); + that.sliderCaptcha('reset'); + $('.userinfo .dropdown-menu a:first')[0].click(); + }, 1000); + } + }); + + $('#btnCaptcha').on('click', function () { + $('#captcha').parent().addClass('d-inline-block'); + }); + + $.footer(); +}); \ No newline at end of file diff --git a/Bootstrap.Client/wwwroot/lib/captcha/images/Pic0.jpg b/Bootstrap.Client/wwwroot/lib/captcha/images/Pic0.jpg new file mode 100644 index 00000000..ccfbedd1 Binary files /dev/null and b/Bootstrap.Client/wwwroot/lib/captcha/images/Pic0.jpg differ diff --git a/Bootstrap.Client/wwwroot/lib/captcha/images/Pic1.jpg b/Bootstrap.Client/wwwroot/lib/captcha/images/Pic1.jpg new file mode 100644 index 00000000..689cc20c Binary files /dev/null and b/Bootstrap.Client/wwwroot/lib/captcha/images/Pic1.jpg differ diff --git a/Bootstrap.Client/wwwroot/lib/captcha/images/Pic2.jpg b/Bootstrap.Client/wwwroot/lib/captcha/images/Pic2.jpg new file mode 100644 index 00000000..6ec84255 Binary files /dev/null and b/Bootstrap.Client/wwwroot/lib/captcha/images/Pic2.jpg differ diff --git a/Bootstrap.Client/wwwroot/lib/captcha/images/Pic3.jpg b/Bootstrap.Client/wwwroot/lib/captcha/images/Pic3.jpg new file mode 100644 index 00000000..d1d1553d Binary files /dev/null and b/Bootstrap.Client/wwwroot/lib/captcha/images/Pic3.jpg differ diff --git a/Bootstrap.Client/wwwroot/lib/captcha/images/Pic4.jpg b/Bootstrap.Client/wwwroot/lib/captcha/images/Pic4.jpg new file mode 100644 index 00000000..d718c2bb Binary files /dev/null and b/Bootstrap.Client/wwwroot/lib/captcha/images/Pic4.jpg differ diff --git a/Bootstrap.Client/wwwroot/lib/captcha/longbow.slidercaptcha.js b/Bootstrap.Client/wwwroot/lib/captcha/longbow.slidercaptcha.js new file mode 100644 index 00000000..800c4380 --- /dev/null +++ b/Bootstrap.Client/wwwroot/lib/captcha/longbow.slidercaptcha.js @@ -0,0 +1,293 @@ +(function ($) { + 'use strict'; + + var SliderCaptcha = function (element, options) { + this.$element = $(element); + this.options = $.extend({}, SliderCaptcha.DEFAULTS, options); + this.$element.css({ 'position': 'relative', 'width': this.options.width + 'px', 'margin': '0 auto' }); + this.init(); + }; + + SliderCaptcha.VERSION = '1.0'; + SliderCaptcha.Author = 'argo@163.com'; + SliderCaptcha.DEFAULTS = { + width: 280, // canvas宽度 + height: 155, // canvas高度 + PI: Math.PI, + sliderL: 42, // 滑块边长 + sliderR: 9, // 滑块半径 + offset: 5, // 容错偏差 + loadingText: '正在加载中...', + failedText: '再试一次', + barText: '向右滑动填充拼图', + repeatIcon: 'fa fa-repeat', + maxLoadCount: 3, + localImages: function () { + return 'images/Pic' + Math.round(Math.random() * 4) + '.jpg'; + } + }; + + function Plugin(option) { + return this.each(function () { + var $this = $(this); + var data = $this.data('lgb.SliderCaptcha'); + var options = typeof option === 'object' && option; + + if (!data && /init|reset|verify/.test(option)) return; + if (!data) $this.data('lgb.SliderCaptcha', data = new SliderCaptcha(this, options)); + if (typeof option === 'string') data[option](); + }); + } + + $.fn.sliderCaptcha = Plugin; + $.fn.sliderCaptcha.Constructor = SliderCaptcha; + + var _proto = SliderCaptcha.prototype; + _proto.init = function () { + this.initDOM(); + this.initImg(); + this.bindEvents(); + }; + + _proto.initDOM = function () { + var createElement = function (tagName, className) { + var elment = document.createElement(tagName); + elment.className = className; + return elment; + }; + + var createCanvas = function (width, height) { + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + return canvas; + }; + + var canvas = createCanvas(this.options.width - 2, this.options.height) // 画布 + var block = canvas.cloneNode(true) // 滑块 + var sliderContainer = createElement('div', 'sliderContainer'); + var refreshIcon = createElement('i', 'refreshIcon ' + this.options.repeatIcon); + var sliderMask = createElement('div', 'sliderMask'); + var sliderbg = createElement('div', 'sliderbg'); + var slider = createElement('div', 'slider'); + var sliderIcon = createElement('i', 'fa fa-arrow-right sliderIcon'); + var text = createElement('span', 'sliderText'); + + block.className = 'block' + text.innerHTML = this.options.barText; + + var el = this.$element; + el.append($(canvas)); + el.append($(refreshIcon)); + el.append($(block)); + slider.appendChild(sliderIcon); + sliderMask.appendChild(slider); + sliderContainer.appendChild(sliderbg); + sliderContainer.appendChild(sliderMask); + sliderContainer.appendChild(text); + el.append($(sliderContainer)); + + Object.assign(this, { + canvas, + block, + sliderContainer: $(sliderContainer), + refreshIcon, + slider, + sliderMask, + sliderIcon, + text: $(text), + canvasCtx: canvas.getContext('2d'), + blockCtx: block.getContext('2d') + }) + }; + + _proto.initImg = function () { + var that = this; + var isIE = window.navigator.userAgent.indexOf('Trident') > -1; + var L = this.options.sliderL + this.options.sliderR * 2 + 3; // 滑块实际边长 + var drawImg = function (ctx, operation) { + var l = that.options.sliderL; + var r = that.options.sliderR; + var PI = that.options.PI; + var x = that.x; + var y = that.y; + ctx.beginPath() + ctx.moveTo(x, y) + ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI) + ctx.lineTo(x + l, y) + ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI) + ctx.lineTo(x + l, y + l) + ctx.lineTo(x, y + l) + ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true) + ctx.lineTo(x, y) + ctx.lineWidth = 2 + ctx.fillStyle = 'rgba(255, 255, 255, 0.7)' + ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)' + ctx.stroke() + ctx[operation]() + ctx.globalCompositeOperation = isIE ? 'xor' : 'overlay' + } + + var getRandomNumberByRange = function (start, end) { + return Math.round(Math.random() * (end - start) + start); + }; + var img = new Image(); + img.crossOrigin = "Anonymous"; + var loadCount = 0; + img.onload = function () { + // 随机创建滑块的位置 + that.x = getRandomNumberByRange(L + 10, that.options.width - (L + 10)); + that.y = getRandomNumberByRange(10 + that.options.sliderR * 2, that.options.height - (L + 10)); + drawImg(that.canvasCtx, 'fill'); + drawImg(that.blockCtx, 'clip'); + + that.canvasCtx.drawImage(img, 0, 0, that.options.width - 2, that.options.height); + that.blockCtx.drawImage(img, 0, 0, that.options.width - 2, that.options.height); + var y = that.y - that.options.sliderR * 2 - 1; + var ImageData = that.blockCtx.getImageData(that.x - 3, y, L, L); + that.block.width = L; + that.blockCtx.putImageData(ImageData, 0, y); + that.text.text(that.text.attr('data-text')); + }; + img.onerror = function () { + loadCount++; + if (window.location.protocol === 'file:') { + loadCount = that.options.maxLoadCount; + console.error("can't load pic resource file from File protocal. Please try http or https"); + } + if (loadCount >= that.options.maxLoadCount) { + that.text.text('加载失败').addClass('text-danger'); + return; + } + img.src = that.options.localImages(); + }; + img.setSrc = function () { + var src = ''; + loadCount = 0; + that.text.removeClass('text-danger'); + if ($.isFunction(that.options.setSrc)) src = that.options.setSrc(); + if (!src || src === '') src = 'https://picsum.photos/' + that.options.width + '/' + that.options.height + '/?image=' + Math.round(Math.random() * 20); + if (isIE) { // IE浏览器无法通过img.crossOrigin跨域,使用ajax获取图片blob然后转为dataURL显示 + var xhr = new XMLHttpRequest() + xhr.onloadend = function (e) { + var file = new FileReader(); // FileReader仅支持IE10+ + file.readAsDataURL(e.target.response); + file.onloadend = function (e) { + img.src = e.target.result; + } + } + xhr.open('GET', src); + xhr.responseType = 'blob'; + xhr.send(); + } else img.src = src; + }; + img.setSrc(); + this.text.attr('data-text', this.options.barText); + this.text.text(this.options.loadingText); + this.img = img + }; + + _proto.clean = function () { + this.canvasCtx.clearRect(0, 0, this.options.width, this.options.height); + this.blockCtx.clearRect(0, 0, this.options.width, this.options.height); + this.block.width = this.options.width; + }; + + _proto.bindEvents = function () { + var that = this; + this.$element.on('selectstart', function () { + return false; + }); + + $(this.refreshIcon).on('click', function () { + that.text.text(that.options.barText); + that.reset(); + if ($.isFunction(that.options.onRefresh)) that.options.onRefresh.call(that.$element); + }); + + var originX, originY, trail = [], + isMouseDown = false + + var handleDragStart = function (e) { + if (that.text.hasClass('text-danger')) return; + originX = e.clientX || e.touches[0].clientX; + originY = e.clientY || e.touches[0].clientY; + isMouseDown = true; + }; + + var handleDragMove = function (e) { + if (!isMouseDown) return false; + var eventX = e.clientX || e.touches[0].clientX; + var eventY = e.clientY || e.touches[0].clientY; + var moveX = eventX - originX; + var moveY = eventY - originY; + if (moveX < 0 || moveX + 40 > that.options.width) return false; + that.slider.style.left = (moveX - 1) + 'px'; + var blockLeft = (that.options.width - 40 - 20) / (that.options.width - 40) * moveX; + that.block.style.left = blockLeft + 'px'; + + that.sliderContainer.addClass('sliderContainer_active'); + that.sliderMask.style.width = (moveX + 4) + 'px'; + trail.push(moveY); + }; + + var handleDragEnd = function (e) { + if (!isMouseDown) return false + isMouseDown = false + var eventX = e.clientX || e.changedTouches[0].clientX + if (eventX == originX) return false + that.sliderContainer.removeClass('sliderContainer_active'); + that.trail = trail + var { + spliced, + verified + } = that.verify() + if (spliced && verified) { + that.sliderContainer.addClass('sliderContainer_success'); + if ($.isFunction(that.options.onSuccess)) that.options.onSuccess.call(that.$element); + } else { + that.sliderContainer.addClass('sliderContainer_fail'); + if ($.isFunction(that.options.onFail)) that.options.onFail.call(that.$element); + setTimeout(() => { + that.text.text(that.options.failedText); + that.reset(); + }, 1000); + } + }; + + this.slider.addEventListener('mousedown', handleDragStart); + this.slider.addEventListener('touchstart', handleDragStart); + document.addEventListener('mousemove', handleDragMove); + document.addEventListener('touchmove', handleDragMove); + document.addEventListener('mouseup', handleDragEnd); + document.addEventListener('touchend', handleDragEnd); + + document.addEventListener('mousedown', function () { return false; }); + document.addEventListener('touchstart', function () { return false; }); + }; + + _proto.verify = function () { + var sum = function (x, y) { return x + y; }; + var square = function (x) { return x * x; }; + var arr = this.trail // 拖动时y轴的移动距离 + var average = arr.reduce(sum) / arr.length; + var deviations = arr.map(function (x) { return x - average; }); + var stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length); + var left = parseInt(this.block.style.left); + return { + spliced: Math.abs(left - this.x) < this.options.offset, + verified: stddev !== 0, // 简单验证下拖动轨迹,为零时表示Y轴上下没有波动,可能非人为操作 + } + }; + + _proto.reset = function () { + this.sliderContainer.removeClass('sliderContainer_fail sliderContainer_success'); + this.slider.style.left = 0 + this.block.style.left = 0 + this.sliderMask.style.width = 0 + this.clean() + this.text.attr('data-text', this.text.text()); + this.text.text(this.options.loadingText); + this.img.setSrc(); + }; +})(jQuery); \ No newline at end of file diff --git a/Bootstrap.Client/wwwroot/lib/captcha/slidercaptcha.css b/Bootstrap.Client/wwwroot/lib/captcha/slidercaptcha.css new file mode 100644 index 00000000..ac97699d --- /dev/null +++ b/Bootstrap.Client/wwwroot/lib/captcha/slidercaptcha.css @@ -0,0 +1,131 @@ +body { + overflow-x: hidden; +} + +.block { + position: absolute; + left: 0; + top: 0; +} + +.sliderContainer { + position: relative; + text-align: center; + line-height: 40px; + background: #f7f9fa; + color: #45494c; + border-radius: 2px; +} + +.sliderbg { + position: absolute; + left: 0; + right: 0; + top: 0; + background-color: #f7f9fa; + height: 40px; + border-radius: 2px; + border: 1px solid #e6e8eb; +} + +.sliderContainer_active .slider { + top: -1px; + border: 1px solid #1991FA; +} + +.sliderContainer_active .sliderMask { + border-width: 1px 0 1px 1px; +} + +.sliderContainer_success .slider { + top: -1px; + border: 1px solid #52CCBA; + background-color: #52CCBA !important; +} + +.sliderContainer_success .sliderMask { + border: 1px solid #52CCBA; + border-width: 1px 0 1px 1px; + background-color: #D2F4EF; +} + +.sliderContainer_success .sliderIcon:before { + content: "\f00c"; +} + +.sliderContainer_fail .slider { + top: -1px; + border: 1px solid #f57a7a; + background-color: #f57a7a !important; +} + +.sliderContainer_fail .sliderMask { + border: 1px solid #f57a7a; + background-color: #fce1e1; + border-width: 1px 0 1px 1px; +} + +.sliderContainer_fail .sliderIcon:before { + content: "\f00d"; +} +.sliderContainer_active .sliderText, .sliderContainer_success .sliderText, .sliderContainer_fail .sliderText { + display: none; +} + +.sliderMask { + position: absolute; + left: 0; + top: 0; + height: 40px; + border: 0 solid #1991FA; + background: #D1E9FE; + border-radius: 2px; +} + +.slider { + position: absolute; + top: 0; + left: 0; + width: 40px; + height: 40px; + background: #fff; + box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); + cursor: pointer; + transition: background .2s linear; + border-radius: 2px; + display: flex; + align-items: center; + justify-content: center; +} + +.slider:hover { + background: #1991FA; +} + +.slider:hover .sliderIcon { + background-position: 0 -13px; +} + +.sliderText { + position: relative; +} + +.sliderIcon { + +} + +.refreshIcon { + position: absolute; + right: 0; + top: 0; + cursor: pointer; + margin: 6px; + color: rgba(0,0,0,.25); + font-size: 1rem; + z-index: 5; + transition: color .3s linear; +} + + .refreshIcon:hover { + color: #6c757d; + } diff --git a/Bootstrap.DataAccess.MongoDB/Bootstrap.DataAccess.MongoDB.csproj b/Bootstrap.DataAccess.MongoDB/Bootstrap.DataAccess.MongoDB.csproj index 5da20f34..6560d353 100644 --- a/Bootstrap.DataAccess.MongoDB/Bootstrap.DataAccess.MongoDB.csproj +++ b/Bootstrap.DataAccess.MongoDB/Bootstrap.DataAccess.MongoDB.csproj @@ -11,7 +11,7 @@ - + diff --git a/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj b/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj index 7a9fb04e..8f0ca2ee 100644 --- a/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj +++ b/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj @@ -1,23 +1,23 @@ - - - - netstandard2.0 - true - ..\Keys\Longbow.Utility.snk - - - - - - - - - - - - - - - - - + + + + netstandard2.0 + true + ..\Keys\Longbow.Utility.snk + + + + + + + + + + + + + + + + + diff --git a/Bootstrap.DataAccess/Dict.cs b/Bootstrap.DataAccess/Dict.cs index ae20f704..46908bec 100644 --- a/Bootstrap.DataAccess/Dict.cs +++ b/Bootstrap.DataAccess/Dict.cs @@ -1,180 +1,186 @@ -using Bootstrap.Security; -using Bootstrap.Security.DataAccess; -using Longbow; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Bootstrap.DataAccess -{ - /// - /// - /// - public class Dict : BootstrapDict - { - /// - /// 删除字典中的数据 - /// - /// 需要删除的IDs - /// - public virtual bool Delete(IEnumerable value) - { - if (!value.Any()) return true; - var ids = string.Join(",", value); - string sql = $"where ID in ({ids})"; - DbManager.Create().Delete(sql); - return true; - } - - /// - /// 保存新建/更新的字典信息 - /// - /// - /// - public virtual bool Save(BootstrapDict dict) - { - if (dict.Category.Length > 50) dict.Category = dict.Category.Substring(0, 50); - if (dict.Name.Length > 50) dict.Name = dict.Name.Substring(0, 50); - if (dict.Code.Length > 50) dict.Code = dict.Code.Substring(0, 50); - - DbManager.Create().Save(dict); - return true; - } - - /// - /// 保存网站个性化设置 - /// - /// - /// - public virtual bool SaveSettings(BootstrapDict dict) - { - DbManager.Create().Update("set Code = @Code where Category = @Category and Name = @Name", dict); - return true; - } - - /// - /// 获取字典分类名称 - /// - /// - public virtual IEnumerable RetrieveCategories() => DictHelper.RetrieveDicts().OrderBy(d => d.Category).Select(d => d.Category).Distinct(); - - /// - /// - /// - /// - public virtual string RetrieveWebTitle() => (DictHelper.RetrieveDicts().FirstOrDefault(d => d.Name == "网站标题" && d.Category == "网站设置" && d.Define == 0) ?? new BootstrapDict() { Code = "后台管理系统" }).Code; - - /// - /// - /// - /// - public virtual string RetrieveWebFooter() => (DictHelper.RetrieveDicts().FirstOrDefault(d => d.Name == "网站页脚" && d.Category == "网站设置" && d.Define == 0) ?? new BootstrapDict() { Code = "2016 © 通用后台管理系统" }).Code; - - /// - /// 获得系统中配置的可以使用的网站样式 - /// - /// - public virtual IEnumerable RetrieveThemes() => DictHelper.RetrieveDicts().Where(d => d.Category == "网站样式"); - - /// - /// 获得网站设置中的当前样式 - /// - /// - public virtual string RetrieveActiveTheme() - { - var theme = DictHelper.RetrieveDicts().FirstOrDefault(d => d.Name == "使用样式" && d.Category == "当前样式" && d.Define == 0); - return theme == null ? string.Empty : (theme.Code.Equals("site.css", StringComparison.OrdinalIgnoreCase) ? string.Empty : theme.Code); - } - - /// - /// 获取头像路径 - /// - /// - public virtual string RetrieveIconFolderPath() => (DictHelper.RetrieveDicts().FirstOrDefault(d => d.Name == "头像路径" && d.Category == "头像地址" && d.Define == 0) ?? new BootstrapDict { Code = "~/images/uploader/" }).Code; - - /// - /// 获得默认的前台首页地址,默认为~/Home/Index - /// - /// - /// - public virtual string RetrieveHomeUrl(string appCode) - { - // https://gitee.com/LongbowEnterprise/dashboard/issues?id=IS0WK - var url = "~/Home/Index"; - var dicts = DictHelper.RetrieveDicts(); - if (appCode != "0") - { - var appUrl = dicts.FirstOrDefault(d => d.Name.Equals(appCode, StringComparison.OrdinalIgnoreCase) && d.Category == "应用首页" && d.Define == 0)?.Code; - if (!string.IsNullOrEmpty(appUrl)) - { - url = appUrl; - return url; - } - } - var defaultUrl = dicts.FirstOrDefault(d => d.Name == "前台首页" && d.Category == "网站设置" && d.Define == 0)?.Code; - if (!string.IsNullOrEmpty(defaultUrl)) url = defaultUrl; - return url; - } - - /// - /// - /// - /// - public virtual IEnumerable> RetrieveApps() => DictHelper.RetrieveDicts().Where(d => d.Category == "应用程序" && d.Define == 0).Select(d => new KeyValuePair(d.Code, d.Name)).OrderBy(d => d.Key); - - /// - /// 通过数据库获得所有字典表配置信息,缓存Key=DictHelper-RetrieveDicts - /// - /// - public virtual IEnumerable RetrieveDicts() => DbHelper.RetrieveDicts(); - - /// - /// 程序异常时长 默认1月 - /// - /// - public int RetrieveExceptionsLogPeriod() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "程序异常保留时长" && d.Define == 0)?.Code, 1); - - /// - /// 操作日志时长 默认12月 - /// - /// - public int RetrieveLogsPeriod() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "操作日志保留时长" && d.Define == 0)?.Code, 12); - - /// - /// 登录日志时长 默认12月 - /// - /// - public int RetrieveLoginLogsPeriod() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "登录日志保留时长" && d.Define == 0)?.Code, 12); - - /// - /// Cookie保存时长 默认7天 - /// - /// - public int RetrieveCookieExpiresPeriod() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "Cookie保留时长" && d.Define == 0)?.Code, 7); - - /// - /// 获得 IP地理位置 - /// - /// - public string RetrieveLocaleIPSvr() => DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "IP地理位置接口" && d.Define == 0)?.Code; - - /// - /// 获得 项目是否获取登录地点 默认为false - /// - /// 服务提供名称 - /// - public string RetrieveLocaleIPSvrUrl(string ipSvr) => DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == ipSvr && d.Define == 0)?.Code; - - /// - /// 获得 访问日志保留时长 默认为1个月 - /// - /// - public int RetrieveAccessLogPeriod() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "访问日志保留时长" && d.Define == 0)?.Code, 1); - - /// - /// 获得 是否为演示系统 默认为 false 不是演示系统 - /// - /// - public bool RetrieveSystemModel() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "演示系统" && d.Define == 0)?.Code, "0") == "1"; - } -} +using Bootstrap.Security; +using Bootstrap.Security.DataAccess; +using Longbow; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Bootstrap.DataAccess +{ + /// + /// + /// + public class Dict : BootstrapDict + { + /// + /// 删除字典中的数据 + /// + /// 需要删除的IDs + /// + public virtual bool Delete(IEnumerable value) + { + if (!value.Any()) return true; + var ids = string.Join(",", value); + string sql = $"where ID in ({ids})"; + DbManager.Create().Delete(sql); + return true; + } + + /// + /// 保存新建/更新的字典信息 + /// + /// + /// + public virtual bool Save(BootstrapDict dict) + { + if (dict.Category.Length > 50) dict.Category = dict.Category.Substring(0, 50); + if (dict.Name.Length > 50) dict.Name = dict.Name.Substring(0, 50); + if (dict.Code.Length > 50) dict.Code = dict.Code.Substring(0, 50); + + DbManager.Create().Save(dict); + return true; + } + + /// + /// 保存网站个性化设置 + /// + /// + /// + public virtual bool SaveSettings(BootstrapDict dict) + { + DbManager.Create().Update("set Code = @Code where Category = @Category and Name = @Name", dict); + return true; + } + + /// + /// 获取字典分类名称 + /// + /// + public virtual IEnumerable RetrieveCategories() => DictHelper.RetrieveDicts().OrderBy(d => d.Category).Select(d => d.Category).Distinct(); + + /// + /// + /// + /// + public virtual string RetrieveWebTitle() => (DictHelper.RetrieveDicts().FirstOrDefault(d => d.Name == "网站标题" && d.Category == "网站设置" && d.Define == 0) ?? new BootstrapDict() { Code = "后台管理系统" }).Code; + + /// + /// + /// + /// + public virtual string RetrieveWebFooter() => (DictHelper.RetrieveDicts().FirstOrDefault(d => d.Name == "网站页脚" && d.Category == "网站设置" && d.Define == 0) ?? new BootstrapDict() { Code = "2016 © 通用后台管理系统" }).Code; + + /// + /// 获得系统中配置的可以使用的网站样式 + /// + /// + public virtual IEnumerable RetrieveThemes() => DictHelper.RetrieveDicts().Where(d => d.Category == "网站样式"); + + /// + /// 获得网站设置中的当前样式 + /// + /// + public virtual string RetrieveActiveTheme() + { + var theme = DictHelper.RetrieveDicts().FirstOrDefault(d => d.Name == "使用样式" && d.Category == "当前样式" && d.Define == 0); + return theme == null ? string.Empty : (theme.Code.Equals("site.css", StringComparison.OrdinalIgnoreCase) ? string.Empty : theme.Code); + } + + /// + /// 获取头像路径 + /// + /// + public virtual string RetrieveIconFolderPath() => (DictHelper.RetrieveDicts().FirstOrDefault(d => d.Name == "头像路径" && d.Category == "头像地址" && d.Define == 0) ?? new BootstrapDict { Code = "~/images/uploader/" }).Code; + + /// + /// 获得默认的前台首页地址,默认为~/Home/Index + /// + /// + /// + public virtual string RetrieveHomeUrl(string appCode) + { + // https://gitee.com/LongbowEnterprise/dashboard/issues?id=IS0WK + var url = "~/Home/Index"; + var dicts = DictHelper.RetrieveDicts(); + if (appCode != "0") + { + var appUrl = dicts.FirstOrDefault(d => d.Name.Equals(appCode, StringComparison.OrdinalIgnoreCase) && d.Category == "应用首页" && d.Define == 0)?.Code; + if (!string.IsNullOrEmpty(appUrl)) + { + url = appUrl; + return url; + } + } + var defaultUrl = dicts.FirstOrDefault(d => d.Name == "前台首页" && d.Category == "网站设置" && d.Define == 0)?.Code; + if (!string.IsNullOrEmpty(defaultUrl)) url = defaultUrl; + return url; + } + + /// + /// + /// + /// + public virtual IEnumerable> RetrieveApps() => DictHelper.RetrieveDicts().Where(d => d.Category == "应用程序" && d.Define == 0).Select(d => new KeyValuePair(d.Code, d.Name)).OrderBy(d => d.Key); + + /// + /// 通过数据库获得所有字典表配置信息,缓存Key=DictHelper-RetrieveDicts + /// + /// + public virtual IEnumerable RetrieveDicts() => DbHelper.RetrieveDicts(); + + /// + /// 程序异常时长 默认1月 + /// + /// + public int RetrieveExceptionsLogPeriod() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "程序异常保留时长" && d.Define == 0)?.Code, 1); + + /// + /// 操作日志时长 默认12月 + /// + /// + public int RetrieveLogsPeriod() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "操作日志保留时长" && d.Define == 0)?.Code, 12); + + /// + /// 登录日志时长 默认12月 + /// + /// + public int RetrieveLoginLogsPeriod() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "登录日志保留时长" && d.Define == 0)?.Code, 12); + + /// + /// Cookie保存时长 默认7天 + /// + /// + public int RetrieveCookieExpiresPeriod() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "Cookie保留时长" && d.Define == 0)?.Code, 7); + + /// + /// 获得 IP地理位置 + /// + /// + public string RetrieveLocaleIPSvr() => DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "IP地理位置接口" && d.Define == 0)?.Code; + + /// + /// 获得 项目是否获取登录地点 默认为false + /// + /// 服务提供名称 + /// + public string RetrieveLocaleIPSvrUrl(string ipSvr) => DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == ipSvr && d.Define == 0)?.Code; + + /// + /// 获得 访问日志保留时长 默认为1个月 + /// + /// + public int RetrieveAccessLogPeriod() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "访问日志保留时长" && d.Define == 0)?.Code, 1); + + /// + /// 获得 是否为演示系统 默认为 false 不是演示系统 + /// + /// + public bool RetrieveSystemModel() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "演示系统" && d.Define == 0)?.Code, "0") == "1"; + + /// + /// 获得 验证码图床地址 + /// + /// + public string RetrieveImagesLibUrl() => LgbConvert.ReadValue(DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "系统设置" && d.Name == "验证码图床" && d.Define == 0)?.Code, "https://longbow-1258823021.cos.ap-shanghai.myqcloud.com/pic/280/150/"); + } +} diff --git a/Bootstrap.DataAccess/Helper/DictHelper.cs b/Bootstrap.DataAccess/Helper/DictHelper.cs index d68fabaa..0f704c63 100644 --- a/Bootstrap.DataAccess/Helper/DictHelper.cs +++ b/Bootstrap.DataAccess/Helper/DictHelper.cs @@ -1,6 +1,8 @@ using Bootstrap.Security; using Longbow.Cache; using Longbow.Data; +using Longbow.Web; +using System; using System.Collections.Generic; using System.Linq; @@ -75,6 +77,21 @@ namespace Bootstrap.DataAccess return ret; } + /// + /// + /// + /// + public static void ConfigIPLocator(IPLocatorOption op) + { + var name = RetrieveLocaleIPSvr(); + if (!string.IsNullOrEmpty(name) && !name.Equals("None", StringComparison.OrdinalIgnoreCase)) + { + var url = RetrieveLocaleIPSvrUrl(name); + op.Locator = string.IsNullOrEmpty(url) ? null : DefaultIPLocatorProvider.CreateLocator(name); + op.Url = string.IsNullOrEmpty(url) ? string.Empty : $"{url}{op.IP}"; + } + } + /// /// 保存网站个性化设置 /// @@ -183,5 +200,11 @@ namespace Bootstrap.DataAccess /// /// public static bool RetrieveSystemModel() => DbContextManager.Create().RetrieveSystemModel(); + + /// + /// 获得验证码图床地址 + /// + /// + public static string RetrieveImagesLibUrl() => DbContextManager.Create().RetrieveImagesLibUrl(); } } diff --git a/Bootstrap.DataAccess/Helper/TraceHelper.cs b/Bootstrap.DataAccess/Helper/TraceHelper.cs index d4c31808..d85bec1c 100644 --- a/Bootstrap.DataAccess/Helper/TraceHelper.cs +++ b/Bootstrap.DataAccess/Helper/TraceHelper.cs @@ -1,5 +1,7 @@ using Longbow.Data; +using Longbow.Web; using Longbow.Web.Mvc; +using Microsoft.AspNetCore.Http; using PetaPoco; using System; @@ -11,8 +13,27 @@ namespace Bootstrap.DataAccess /// /// 保存访问历史记录 /// - /// - public static void Save(Trace p) => DbContextManager.Create().Save(p); + /// + /// + public static void Save(HttpContext context, OnlineUser v) + { + if (context.User.Identity.IsAuthenticated) + { + var user = UserHelper.RetrieveUserByUserName(context.User.Identity.Name); + v.UserName = user.UserName; + v.DisplayName = user.DisplayName; + DbContextManager.Create().Save(new Trace + { + Ip = v.Ip, + RequestUrl = v.RequestUrl, + LogTime = v.LastAccessTime, + City = v.Location, + Browser = v.Browser, + OS = v.OS, + UserName = v.UserName + }); + } + } /// /// 获得指定IP历史访问记录 diff --git a/DatabaseScripts/InitData.sql b/DatabaseScripts/InitData.sql index 836f9c2d..cf6e47d4 100644 --- a/DatabaseScripts/InitData.sql +++ b/DatabaseScripts/InitData.sql @@ -44,6 +44,7 @@ INSERT [dbo].[Dicts] ([Category], [Name], [Code], [Define]) VALUES (N'系统设 INSERT [dbo].[Dicts] ([Category], [Name], [Code], [Define]) VALUES (N'系统设置', N'BaiDuIPSvr', 'http://api.map.baidu.com/location/ip?ak=6lvVPMDlm2gjLpU0aiqPsHXi2OiwGQRj&ip=', 0) INSERT [dbo].[Dicts] ([Category], [Name], [Code], [Define]) VALUES (N'系统设置', N'JuheIPSvr', 'http://apis.juhe.cn/ip/ipNew?key=f57102d1b9fadd3f4a1c29072d0c0206&ip=', 0) INSERT [dbo].[Dicts] ([Category], [Name], [Code], [Define]) VALUES (N'系统设置', N'演示系统', '0', 0) +INSERT [dbo].[Dicts] ([Category], [Name], [Code], [Define]) VALUES (N'系统设置', N'验证码图床', 'https://longbow-1258823021.cos.ap-shanghai.myqcloud.com/pic/280/150/', 0) DELETE FROM Navigations Where Category = N'0' INSERT [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, N'后台管理', 10, N'fa fa-gear', N'~/Admin/Index', N'0') diff --git a/DatabaseScripts/MongoDB/BootstrapAdmin.Dicts.json b/DatabaseScripts/MongoDB/BootstrapAdmin.Dicts.json index 83a3db13..9cf9fbf8 100644 --- a/DatabaseScripts/MongoDB/BootstrapAdmin.Dicts.json +++ b/DatabaseScripts/MongoDB/BootstrapAdmin.Dicts.json @@ -341,5 +341,12 @@ "Name": "演示系统", "Code": "0", "Define": NumberInt(0) + }, + { + "_id": ObjectId("5bd6c73d5fa31256f77e4a49"), + "Category": "系统设置", + "Name": "验证码图床", + "Code": "https://longbow-1258823021.cos.ap-shanghai.myqcloud.com/pic/280/150/", + "Define": NumberInt(0) } ] \ No newline at end of file diff --git a/DatabaseScripts/MySQL/initData.sql b/DatabaseScripts/MySQL/initData.sql index e4ba462a..348e6142 100644 --- a/DatabaseScripts/MySQL/initData.sql +++ b/DatabaseScripts/MySQL/initData.sql @@ -41,6 +41,7 @@ INSERT INTO Dicts (Category, Name, Code, Define) VALUES ('系统设置', 'IP地 INSERT INTO Dicts (Category, Name, Code, Define) VALUES ('系统设置', 'BaiDuIPSvr', 'http://api.map.baidu.com/location/ip?ak=6lvVPMDlm2gjLpU0aiqPsHXi2OiwGQRj&ip=', 0); INSERT INTO Dicts (Category, Name, Code, Define) VALUES ('系统设置', 'JuheIPSvr', 'http://apis.juhe.cn/ip/ipNew?key=f57102d1b9fadd3f4a1c29072d0c0206&ip=', 0); INSERT INTO Dicts (Category, Name, Code, Define) VALUES ('系统设置', '演示系统', '0', 0); +INSERT INTO Dicts (Category, Name, Code, Define) VALUES ('系统设置', '验证码图床', 'https://longbow-1258823021.cos.ap-shanghai.myqcloud.com/pic/280/150/', 0); DELETE FROM Navigations Where Category = '0'; INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (0, '后台管理', 10, 'fa fa-gear', '~/Admin/Index', '0'); diff --git a/DatabaseScripts/Postgresql/initData.sql b/DatabaseScripts/Postgresql/initData.sql index e82d2ca7..0489a044 100644 --- a/DatabaseScripts/Postgresql/initData.sql +++ b/DatabaseScripts/Postgresql/initData.sql @@ -40,6 +40,7 @@ INSERT INTO Dicts (Category, Name, Code, Define) VALUES ('系统设置', 'IP地 INSERT INTO Dicts (Category, Name, Code, Define) VALUES ('系统设置', 'BaiDuIPSvr', 'http://api.map.baidu.com/location/ip?ak=6lvVPMDlm2gjLpU0aiqPsHXi2OiwGQRj&ip=', 0); INSERT INTO Dicts (Category, Name, Code, Define) VALUES ('系统设置', 'JuheIPSvr', 'http://apis.juhe.cn/ip/ipNew?key=f57102d1b9fadd3f4a1c29072d0c0206&ip=', 0); INSERT INTO Dicts (Category, Name, Code, Define) VALUES ('系统设置', '演示系统', '0', 0); +INSERT INTO Dicts (Category, Name, Code, Define) VALUES ('系统设置', '验证码图床', 'https://longbow-1258823021.cos.ap-shanghai.myqcloud.com/pic/280/150/', 0); DELETE FROM Navigations Where Category = '0'; INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '后台管理', 10, 'fa fa-gear', '~/Admin/Index', '0'); diff --git a/DatabaseScripts/SQLite/InitData.sql b/DatabaseScripts/SQLite/InitData.sql index d05ec895..bc3d0596 100644 --- a/DatabaseScripts/SQLite/InitData.sql +++ b/DatabaseScripts/SQLite/InitData.sql @@ -39,6 +39,7 @@ INSERT INTO [Dicts] ([Category], [Name], [Code], [Define]) VALUES ('系统设置 INSERT INTO [Dicts] ([Category], [Name], [Code], [Define]) VALUES ('系统设置', 'BaiDuIPSvr', 'http://api.map.baidu.com/location/ip?ak=6lvVPMDlm2gjLpU0aiqPsHXi2OiwGQRj&ip=', 0); INSERT INTO [Dicts] ([Category], [Name], [Code], [Define]) VALUES ('系统设置', 'JuheIPSvr', 'http://apis.juhe.cn/ip/ipNew?key=f57102d1b9fadd3f4a1c29072d0c0206&ip=', 0); INSERT INTO [Dicts] ([Category], [Name], [Code], [Define]) VALUES ('系统设置', '演示系统', '0', 0); +INSERT INTO [Dicts] ([Category], [Name], [Code], [Define]) VALUES ('系统设置', '验证码图床', 'https://longbow-1258823021.cos.ap-shanghai.myqcloud.com/pic/280/150/', 0); DELETE FROM Navigations Where Category = '0'; INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, '后台管理', 10, 'fa fa-gear', '~/Admin/Index', '0'); @@ -136,6 +137,8 @@ INSERT into [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category INSERT into [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], [Application]) VALUES (0, '测试页面', 20, 'fa fa-fa', '#', '1', 2); INSERT into [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], [Application]) VALUES (last_insert_rowid(), '关于', 10, 'fa fa-fa', '~/Home/About', '1', 2); +INSERT into [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], [Application]) VALUES (0, '返回码云', 20, 'fa fa-fa', 'https://gitee.com/LongbowEnterprise/BootstrapAdmin', '1', 2); + -- 菜单授权 DELETE FROM NavigationRole Where NavigationID in (Select ID From Navigations Where [Application] = 2); INSERT INTO NavigationRole SELECT NULL, ID, 2 FROM Navigations Where [Application] = 2; diff --git a/UnitTest/Bootstrap.Admin/Api/OnlineTest.cs b/UnitTest/Bootstrap.Admin/Api/OnlineTest.cs index 7398c049..72982baf 100644 --- a/UnitTest/Bootstrap.Admin/Api/OnlineTest.cs +++ b/UnitTest/Bootstrap.Admin/Api/OnlineTest.cs @@ -1,32 +1,33 @@ -using System; -using System.Collections.Generic; -using Xunit; - -namespace Bootstrap.Admin.Api -{ - public class OnlineTest : ControllerTest - { - public OnlineTest(BAWebHost factory) : base(factory, "api/OnlineUsers") { } - - [Fact] - public async void Get_Ok() - { - var users = await Client.GetAsJsonAsync>(); - Assert.Single(users); - } - - [Fact] - public async void GetById_Ok() - { - var urls = await Client.GetAsJsonAsync>>("UnitTest"); - Assert.Empty(urls); +using Longbow.Web; +using System; +using System.Collections.Generic; +using Xunit; + +namespace Bootstrap.Admin.Api +{ + public class OnlineTest : ControllerTest + { + public OnlineTest(BAWebHost factory) : base(factory, "api/OnlineUsers") { } + + [Fact] + public async void Get_Ok() + { + var users = await Client.GetAsJsonAsync>(); + Assert.Single(users); } - [Fact] - public async void Put_Ok() - { - var ret = await Client.PutAsJsonAsync(""); - Assert.False(ret); + [Fact] + public async void GetById_Ok() + { + var urls = await Client.GetAsJsonAsync>>("UnitTest"); + Assert.Empty(urls); } - } -} + + [Fact] + public async void Put_Ok() + { + var ret = await Client.PutAsJsonAsync(""); + Assert.False(ret); + } + } +} diff --git a/UnitTest/Bootstrap.Admin/Api/UsersTest.cs b/UnitTest/Bootstrap.Admin/Api/UsersTest.cs index 57606f61..da8b9dc0 100644 --- a/UnitTest/Bootstrap.Admin/Api/UsersTest.cs +++ b/UnitTest/Bootstrap.Admin/Api/UsersTest.cs @@ -1,12 +1,10 @@ using Bootstrap.DataAccess; -using Longbow.Data; using Longbow.Web.Mvc; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using Xunit; -using DbManager = Longbow.Data.DbManager; namespace Bootstrap.Admin.Api { diff --git a/UnitTest/UnitTest.csproj b/UnitTest/UnitTest.csproj index e3f5367d..dd5e3b10 100644 --- a/UnitTest/UnitTest.csproj +++ b/UnitTest/UnitTest.csproj @@ -9,10 +9,10 @@ - - + + - + all