一個基於 bootstrap 的輕量級後臺模板,這個前端界面我的感受很清爽,對於一個大後端的我來講,能夠減小較多的時間去承擔前端的工做但又必須去獨立去完成一個後臺系統開發的任務,而且,文檔還算比較齊全,對着demo能夠完成一個基本的前端框架搭建了。你們若有更爲好看的又方便後端上手的前端框架,也能夠在留言區分享一下唄。javascript
在線中文Demo:http://adminlte.la998.com/css
在線中文文檔:http://adminlte.la998.com/documentation/index.htmlhtml
Github:https://github.com/almasaeed2010/AdminLTE/releases前端
AdminLTE依賴於兩個主要框架:JQ和Bootstrap,其餘插件能夠按需增長。html5
從文檔能夠知道,使用AdminLTE主要有四個部分:java
.wrapper
。包裹整個網站的div。.main-header
。包含徽標和導航欄。.sidebar-wrapper
。包含用戶面板和側邊欄菜單。.content-wrapper
。包含頁眉和內容。在文檔中,能夠找到下載地址,本文示例是使用最新的版本V2.4.5。jquery
新建項目Asp.net Core Web應用程序,默認就是Razor Pages,而後添加相應的模塊,如圖:本文使用的SDK版本爲:dotNet Core 2.1。git
在Asp.Net Core項目中,引用AdminLTE,在wwwroot僅添加如圖三個文件夾便可: github
在_Layout.cshtml文件中添加引入相關文件:web
<!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <!-- Bootstrap 3.3.7 --> <link rel="stylesheet" href="~/adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css"> <!-- Font Awesome --> <link rel="stylesheet" href="~/adminlte/bower_components/font-awesome/css/font-awesome.min.css"> <!-- Ionicons --> <link href="~/adminlte/bower_components/Ionicons/css/ionicons.min.css" rel="stylesheet" /> <!-- Theme style --> <link rel="stylesheet" href="~/adminlte/dist/css/AdminLTE.min.css"> <!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. --> <link rel="stylesheet" href="~/adminlte/dist/css/skins/_all-skins.min.css"> <!-- Pace style --> <link href="~/adminlte/plugins/pace/pace.min.css" rel="stylesheet" /> <link href="~/css/common.css" rel="stylesheet" /> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> <!-- Google Font --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
在body中,添加js:
<!-- jQuery 3 --> <script src="~/adminlte/bower_components/jquery/dist/jquery.min.js"></script> <!-- jQuery UI 1.11.4 --> <script src="~/adminlte/bower_components/jquery-ui/jquery-ui.min.js"></script> <!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip --> <script> $.widget.bridge('uibutton', $.ui.button); </script> <!-- Bootstrap 3.3.7 --> <script src="~/adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> <!-- Slimscroll --> <script src="~/adminlte/bower_components/jquery-slimscroll/jquery.slimscroll.min.js"></script> <!-- FastClick --> <script src="~/adminlte/bower_components/fastclick/lib/fastclick.js"></script> <!-- AdminLTE App --> <script src="~/adminlte/dist/js/adminlte.min.js"></script> <!-- Skin --> <script type="text/javascript" src="~/adminlte/dist/js/sidebarskins.js" charset="gbk"></script>
sidebarskins.js是本人漢化的側邊欄皮膚
坑1:通常狀況,發現某些功能運行不起來的都是引用不正確致使的,這個要耐心對照好Demo來檢查,或者直接用Demo來修改吧。
開始使用AdminLTE,這裏直接貼圖吧,圖上有註釋和代碼摺疊比較直觀點,重要的地方在放代碼。
最後就能夠運行項目來預覽一下效果了:
都使用到bootstrap,必須得看看移動端的效果,還不錯吧。
坑2:須要注意的是,點擊這個小圖標能夠實現左側邊欄收縮展開的功能,當只有側邊欄能夠正常收縮展開但Logo無動於衷的時候,你多是少了【sidebar-mini】樣式和【logo-mini】logo小圖的引用
登陸界面寫得比較簡約,我比較喜歡這種風格。前端寫得很少,因此還得前端的女票指導一二,否則就是後端的設計的界面了,你懂的。
在Pages文件夾中,添加一個Razor界面,並擼好界面代碼:
@page @model AdminLTE.Net.Web.Pages.LoginModel @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>登陸 - AdminLTE.Net.Web</title> <meta name="developer" content="EminemJK"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" rel="stylesheet" /> <link href="~/adminlte/dist/css/AdminLTE.min.css" rel="stylesheet" /> <link href="~/css/login.css" rel="stylesheet" /> </head> <body> <div> <div class="row"> <div class="loginHeader"> <img class="logo-img" src="~/images/banana_logo.ico" /> <h1 class="logo-name">Banana</h1> <div class="clearfix"></div> </div> </div> <div class="row login-bg"> <div class="loginInBox"> @if (!string.IsNullOrEmpty(Model.Message)) { <p class="login-box-msg" style="color:red">@Model.Message</p> } else { <p class="login-box-msg">Sign in to start your session</p> } <form method="post"> <div class="form-group has-feedback"> <input type="text" class="form-control" asp-for="Login.UserName"> <span class="glyphicon glyphicon-user form-control-feedback"></span> </div> <div class="form-group has-feedback"> <input type="password" class="form-control" asp-for="Login.Password"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <button type="submit" class="btn btn-primary btn-block btn-flat login-btn">Sign In</button> </form> </div> </div> </div> <footer class="footer"> <strong>Copyright © 2018 <a href="http://www.cnblogs.com/EminemJK/">EminemJK</a>.</strong> All rights reserved. </footer> </body> </html>
在Startup中引入Authentication身份驗證:
services.AddAuthentication(CookieService.AuthenticationScheme) .AddCookie(CookieService.AuthenticationScheme, o => { o.LoginPath = new PathString("/Login"); });
Configure方法內調用
app.UseAuthentication();
在Login.cshtml.cs中增長一個OnPostAsync的方法:
[HttpPost] public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { Message = ModelState.Root.Errors[0].ErrorMessage; } else { var user = userService.Login(Login.UserName, Login.Password); if (user != null) { VUserModel model = new VUserModel() { Id = user.Id, UserName = user.UserName, Time = DateTime.Now }; var identity = new ClaimsIdentity(CookieService.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.Sid, CookieService.GetDesEncrypt(model))); await HttpContext.SignInAsync(CookieService.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties() { //記住我 IsPersistent = true, //過時時間 ExpiresUtc = DateTimeOffset.Now.Add(TimeSpan.FromMinutes(30)) }); return RedirectToPage("./Index"); } Message = "登陸失敗,用戶名密碼不正確。"; } return Page(); }
userService和CookieService都是在業務層定義的,gayhub會在文章末尾。
在.Net Core Razor中,xx.cshtml.cs中默認觸發的是Get和Post方法,
若是是須要自定義的,舉個栗子,定義爲:OnPostLoginAsync,而後在Form表單提交的【按鈕】增長asp-page-handler="Login",詳細的推薦你們閱讀這篇文章:ASP.NET Core - Razor頁面之Handlers處理方法。
接着,而後再Index和須要身份驗證的地方都加上Authorize特性便可:
namespace AdminLTE.Net.Web.Pages { [Authorize(AuthenticationSchemes = CookieService.AuthenticationScheme)] public class IndexModel : BasePageModel { public void OnGet() { } } }
function uploadfile() { var file = $("#input-userimg")[0].files[0]; var data = new FormData(); data.append('file', file); $.ajax({ url: "/Account/UserList?handler=Upload", type: 'POST', data: data, contentType: false, processData: false, success: function (returndata) { $("#user-img").attr('src', returndata.path); }, error: function (a, b, c) { alert('上傳失敗') } }); };
折騰許久,緣由是Razor被設計爲能夠自動防止跨站請求僞造(CSRF / XSRF)攻擊。你沒必要編寫任何其餘代碼。Razor頁面中自動包含防僞令牌生成和驗證。這裏請求失敗,是由於POST沒有提交AntiForgeryToken。
解決方法:
1.增長"XSRF-TOKEN"標識到框架中
//增長了"XSRF-TOKEN"標識,值爲表單自動生成的防僞標記 services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
2.頁面*.cshtml頭部加上
@Html.AntiForgeryToken()
3.ajax引入
function uploadfile() { var file = $("#input-userimg")[0].files[0]; var data = new FormData(); data.append('file', file); $.ajax({ url: "/Account/UserList?handler=Upload", type: 'POST', data: data, contentType: false, processData: false, beforeSend: function (xhr) { xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val()); }, success: function (returndata) { $("#user-img").attr('src', returndata.path); }, error: function (a, b, c) { alert('上傳失敗') } }); };
而後既能夠正常訪問Handler
var table = $('#userListTable').DataTable({ "processing": true, "serverSide": true, "ajax": function (data, callback, settings) { //data的參數請參考: https://segmentfault.com/a/1190000004478726 var param = {}; param.draw = data.draw; param.pageNum = (data.start / data.length) + 1; param.pageSize = data.length; param.sex = $('#select-sex option:selected').val(); param.phone = $('#input-phone').val(); param.name = $('#input-name').val(); $.ajax({ type: "GET", data: param, url: "/Account/UserList?handler=UserPage", dataType: "json", success: function (data) { //成功後回調自動渲染 callback(data); } }); }, 'columns': [ { 'data': 'id' }, { 'data': 'name' }, { 'data': 'userName' }, { 'data': 'sexString' }, { 'data': 'phone' }, { 'data': 'createTime' }, { 'data': 'enableString', 'render': function (data, type, row) { if (row.enable == 1) return '<span style="color:#19be6b" >' + row.enableString + '</span>'; else return '<span style="color:#ed3f14" >' + row.enableString + '</span>'; } }, { 'data': null, 'render': function (data, type, row) { return '<a id="btn-edit" class="btn btn-success btn-xs" title="編輯" onClick=btn_edit(' + row.id + ')><i class="fa fa-edit"></i>編輯</a> ' + '<a id="btn-edit" class="btn btn-danger btn-xs" title="刪除" onClick=btn_edit(' + row.id + ')><i class="fa fa-trash " title="刪除" style="cursor:pointer"></i>刪除</a>'; } }, ], //datatable設置參數 http://www.datatables.club/reference/option/ 'paging': true, //啓用分頁 'lengthChange': true, //設置每頁數量 'searching': false, 'ordering': false, 'info': true, 'autoWidth': false, //設置中文 'language': { "sProcessing": "玩命加載中...", "sLengthMenu": "每頁顯示顯示 _MENU_", "sZeroRecords": "沒有匹配結果", "sInfo": "顯示第 _START_ 至 _END_ 項結果,共 _TOTAL_ 項", "sInfoEmpty": "顯示第 0 至 0 項結果,共 0 項", "sInfoFiltered": "(由 _MAX_ 項結果過濾)", "sInfoPostFix": "", "sSearch": "搜索:", "sUrl": "", "sEmptyTable": "表中數據爲空", "sLoadingRecords": "玩命加載中...", "sInfoThousands": ",", "oPaginate": { "sFirst": "首頁", "sPrevious": "上頁", "sNext": "下頁", "sLast": "末頁" }, "oAria": { "sSortAscending": ": 以升序排列此列", "sSortDescending": ": 以降序排列此列" } } });
附上這些天來的成果,發現,我並不適合寫前端,太醜了,哈哈。
最後,Show me the code。
Demo中會使用到這兩個我的封裝的組件:
Banana.Uow是基於Dapper封裝的工做單元和倉儲;
Banana.Utility是經常使用的工具類,有Redis,加解密,拼音等等;
歡迎你們在Issues中提出意見,你們共同進步。
本文已獨家受權給腳本之家(ID:jb51net)公衆號發佈