diff --git a/src/admin/Bootstrap.Admin/wwwroot/css/login-gitee.css b/src/admin/Bootstrap.Admin/wwwroot/css/login-gitee.css
new file mode 100644
index 00000000..60585bd6
--- /dev/null
+++ b/src/admin/Bootstrap.Admin/wwwroot/css/login-gitee.css
@@ -0,0 +1,197 @@
+body {
+ -webkit-font-smoothing: antialiased;
+}
+
+.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);
+}
+
+.login-wrap .rememberPwd i {
+ width: 13px;
+}
+
+.login-wrap .rememberPwd {
+ cursor: pointer;
+ margin-left: 2px;
+}
+
+.container-fluid {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 1000px;
+}
+
+.login-container {
+ min-height: 500px;
+ box-shadow: 0px 20px 80px 0px rgba(0,0,0,0.3);
+ display: flex;
+ width: 1000px;
+}
+
+ .login-container section {
+ width: 50%;
+ }
+
+ .login-container .login-sidebox {
+ position: relative;
+ background: -webkit-gradient(linear, left bottom, left top, from(#3a485a), to(#607089));
+ background: linear-gradient(0deg, #3a485a 0%, #607089 100%);
+ color: #fff;
+ }
+
+ .login-container .login-sidebox::before, .login-container .login-sidebox::after {
+ content: '';
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ }
+
+ .login-container .login-sidebox::before {
+ background: url(../images/left-1.png) no-repeat 0 0;
+ }
+
+ .login-container .login-sidebox::after {
+ background: url(../images/left-2.png) no-repeat right bottom;
+ }
+
+ .login-container .login-sidebox .login-sidebox-content {
+ padding: 80px 80px 48px;
+ position: relative;
+ z-index: 1;
+ }
+
+.login-sidebox-header {
+ margin-bottom: 40px;
+}
+
+.login-sidebox-logo {
+ display: flex;
+ align-items: center;
+ margin-bottom: 14px;
+}
+
+ .login-sidebox-logo img {
+ width: 48px;
+ height: auto;
+ border-radius: 50%;
+ margin-right: 14px;
+ }
+
+ .login-sidebox-logo span {
+ display: inline;
+ font-size: 1.5rem;
+ font-weight: 700;
+ }
+
+.login-sidebox-subtitle {
+ font-size: 20pt;
+ font-weight: normal;
+}
+
+.login-sidebox-footer {
+ margin-top: 40px;
+ border-top: solid 1px #ddd;
+ padding-top: 28px;
+ font-size: 0.875rem;
+ font-weight: 500;
+}
+
+ .login-sidebox-footer a {
+ cursor: pointer;
+ color: #fff;
+ }
+
+.login-form {
+ padding: 64px;
+ font-size: 0.875rem;
+}
+
+.login-form-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+ margin-bottom: 20px;
+}
+
+ .login-form-header h2 {
+ margin-bottom: 0;
+ font-size: 1.714rem;
+ }
+
+.login-other {
+ display: table;
+ text-align: center;
+ white-space: nowrap;
+ margin: 0.25rem 0 0.625rem 0;
+}
+
+ .login-other:before, .login-other:after {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaAAAAACCAYAAACuTHuKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OThBRDY4OUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OThBRDY4QUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5OEFENjg3Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5OEFENjg4Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+VU513gAAADVJREFUeNrs0DENACAQBDBIWLGBJQby/mUcJn5sJXQmOQMAAAAAAJqt+2prAAAAAACg2xdgANk6BEVuJgyMAAAAAElFTkSuQmCC");
+ content: '';
+ display: table-cell;
+ position: relative;
+ top: 50%;
+ width: 50%;
+ background-repeat: no-repeat;
+ }
+
+ .login-other:before {
+ background-position: right 1em top 50%;
+ }
+
+ .login-other:after {
+ background-position: left 1em top 50%;
+ }
+
+.login-list {
+ display: flex;
+ justify-content: space-between;
+}
+
+ .login-list .item {
+ width: 32px;
+ height: 32px;
+ }
+
+.btn-login {
+ color: #fff;
+ background: #fe7300;
+}
+
+ .btn-login:hover {
+ background: #f38d30;
+ color: #fff;
+ }
+
+.forget-password {
+ padding: 16px 0;
+}
+
+.login-footer {
+ position: absolute;
+ bottom: 40px;
+}
+
+ .login-footer .login-footer-body {
+ display: flex;
+ width: 600px;
+ justify-content: space-around;
+ }
+
+ .login-footer .login-footer-body li {
+ list-style: none;
+ }
+
+ .login-footer .login-footer-body li a {
+ color: #7e8392;
+ }
diff --git a/src/admin/Bootstrap.Admin/wwwroot/html/login1.html b/src/admin/Bootstrap.Admin/wwwroot/html/login1.html
new file mode 100644
index 00000000..b71c2663
--- /dev/null
+++ b/src/admin/Bootstrap.Admin/wwwroot/html/login1.html
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
健康检查
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 基于 RBAC 的 NetCore 后台管理框架,权限管理,前后台分离,支持多站点单点登录,兼容所有主流浏览器,内置微信、支付宝、QQ等多种登录方式,内置多种样式,可切换至 Blazor 多 Tabs 模式,权限控制细化到网页内任意元素(按钮、表格、文本框等等)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/admin/Bootstrap.Admin/wwwroot/images/left-1.png b/src/admin/Bootstrap.Admin/wwwroot/images/left-1.png
new file mode 100644
index 00000000..171999df
Binary files /dev/null and b/src/admin/Bootstrap.Admin/wwwroot/images/left-1.png differ
diff --git a/src/admin/Bootstrap.Admin/wwwroot/images/left-2.png b/src/admin/Bootstrap.Admin/wwwroot/images/left-2.png
new file mode 100644
index 00000000..42cc4d26
Binary files /dev/null and b/src/admin/Bootstrap.Admin/wwwroot/images/left-2.png differ
diff --git a/src/admin/Bootstrap.Admin/wwwroot/js/settings.js b/src/admin/Bootstrap.Admin/wwwroot/js/settings.js
index 16dd4b76..f6a9fbd0 100644
--- a/src/admin/Bootstrap.Admin/wwwroot/js/settings.js
+++ b/src/admin/Bootstrap.Admin/wwwroot/js/settings.js
@@ -238,6 +238,12 @@ $(function () {
}
});
break;
+ case 'saveLoginView':
+ var logView = $('#loginView').val();
+ $.bc({
+ url: Settings.url, data: [{ name: 'Login', code: logView }], title: '保存登录界面设置', method: "post"
+ });
+ break;
}
});
diff --git a/src/admin/Bootstrap.Admin/wwwroot/lib/longbow/longbow.common.js b/src/admin/Bootstrap.Admin/wwwroot/lib/longbow/longbow.common.js
index ceceeeca..8661857c 100644
--- a/src/admin/Bootstrap.Admin/wwwroot/lib/longbow/longbow.common.js
+++ b/src/admin/Bootstrap.Admin/wwwroot/lib/longbow/longbow.common.js
@@ -298,6 +298,7 @@
$.fn.extend({
autoCenter: function (options) {
+ if (this.length === 0) return;
options = $.extend({ top: 0 }, options);
var that = this;
var defaultVal = parseFloat(that.css('marginTop').replace('px', ''));
diff --git a/src/admin/Bootstrap.DataAccess/Dict.cs b/src/admin/Bootstrap.DataAccess/Dict.cs
index c8f836a3..290c731a 100644
--- a/src/admin/Bootstrap.DataAccess/Dict.cs
+++ b/src/admin/Bootstrap.DataAccess/Dict.cs
@@ -325,5 +325,17 @@ namespace Bootstrap.DataAccess
///
///
public bool RetrieveHealth() => (DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "网站设置" && d.Name == "健康检查" && d.Define == 0)?.Code ?? "0") == "1";
+
+ ///
+ /// 获得字典表登录界面数据
+ ///
+ ///
+ public IEnumerable
RetrieveLogins() => DictHelper.RetrieveDicts().Where(d => d.Category == "系统首页" && d.Define == 1);
+
+ ///
+ /// 获得使用中的登录视图名称
+ ///
+ ///
+ public string? RetrieveLoginView() => DictHelper.RetrieveDicts().FirstOrDefault(d => d.Category == "网站设置" && d.Name == "登录界面" && d.Define == 1)?.Code;
}
}
diff --git a/src/admin/Bootstrap.DataAccess/Helper/DictHelper.cs b/src/admin/Bootstrap.DataAccess/Helper/DictHelper.cs
index 1ce3c93d..b77b21b5 100644
--- a/src/admin/Bootstrap.DataAccess/Helper/DictHelper.cs
+++ b/src/admin/Bootstrap.DataAccess/Helper/DictHelper.cs
@@ -165,7 +165,8 @@ namespace Bootstrap.DataAccess
["CookiePeriod"] = "Cookie保留时长",
["IPCachePeriod"] = "IP请求缓存时长",
["AppPath"] = "后台地址",
- ["EnableHealth"] = "健康检查"
+ ["EnableHealth"] = "健康检查",
+ ["Login"] = "登录界面"
};
var ret = SaveSettings(items.Where(i => cache.Any(c => c.Key == i.Name)).Select(i => new BootstrapDict()
{
@@ -391,6 +392,18 @@ namespace Bootstrap.DataAccess
///
public static bool RetrieveHealth() => DbContextManager.Create()?.RetrieveHealth() ?? true;
+ ///
+ /// 获得登录界面数据
+ ///
+ ///
+ public static IEnumerable RetrieveLogins() => DbContextManager.Create()?.RetrieveLogins() ?? new BootstrapDict[0];
+
+ ///
+ /// 获得使用中的登录视图名称
+ ///
+ ///
+ public static string RetrieveLoginView() => DbContextManager.Create()?.RetrieveLoginView() ?? "Login";
+
///
/// 保存前台应用配置信息
///
diff --git a/test/UnitTest/Bootstrap.Admin/Controllers/LoginTest.cs b/test/UnitTest/Bootstrap.Admin/Controllers/LoginTest.cs
index d39bbb3a..404e1726 100644
--- a/test/UnitTest/Bootstrap.Admin/Controllers/LoginTest.cs
+++ b/test/UnitTest/Bootstrap.Admin/Controllers/LoginTest.cs
@@ -29,7 +29,7 @@ namespace Bootstrap.Admin.Controllers
var content = await r.Content.ReadAsStringAsync();
Assert.Contains("登 录", content);
- r = await client.GetAsync("/Account/Login");
+ r = await client.GetAsync("/Account/Login?AppId=BA&View=Login1");
var view = await r.Content.ReadAsStringAsync();
var tokenTag = "