AdminLTE
一个基于 bootstrap 的轻量级后台模板,这个前端界面个人感觉很清爽,对于一个大后端的我来说,可以减少较多的时间去承担前端的工作但又必须去独立去完成一个后台系统开发的任务,并且,文档还算比较齐全,对着demo可以完成一个基本的前端框架搭建了。大家如有更为好看的又方便后端上手的前端框架,也可以在留言区分享一下呗。
AdminLTE 文档
在线中文Demo:http://adminlte.la998.com/
在线中文文档:http://adminlte.la998.com/documentation/index.html
Github:https://github.com/almasaeed2010/AdminLTE/releases
AdminLTE 布局
AdminLTE依赖于两个主要框架:JQ和Bootstrap,其他插件可以按需增加。
从文档可以知道,使用AdminLTE主要有四个部分:
- 包装
.wrapper
。包裹整个网站的div。 - 主标题
.main-header
。包含徽标和导航栏。 - 边栏
.sidebar-wrapper
。包含用户面板和侧边栏菜单。 - 内容
.content-wrapper
。包含页眉和内容。
在文档中,可以找到下载地址,本文示例是使用最新的版本V2.4.5。
Asp.Net Core Razor
新建项目Asp.net Core Web应用程序,默认就是Razor Pages,然后添加相应的模块,如图:本文使用的SDK版本为:dotNet Core 2.1。
First
在Asp.Net Core项目中,引用AdminLTE,在wwwroot仅添加如图三个文件夹即可:
- bower_components 基本组件。
- dist adminlte的主要文件。
- plugins 其他插件。
Second
在_Layout.cshtml文件中添加引入相关文件:
<!-- 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来修改吧。
Third
开始使用AdminLTE,这里直接贴图吧,图上有注释和代码折叠比较直观点,重要的地方在放代码。
最后就可以运行项目来预览一下效果了:
都使用到bootstrap,必须得看看移动端的效果,还不错吧。
坑2:需要注意的是,点击这个小图标可以实现左侧边栏收缩展开的功能,当只有侧边栏可以正常收缩展开但Logo无动于衷的时候,你可能是少了【sidebar-mini】样式和【logo-mini】logo小图的引用
添加一个登陆
登录界面写得比较简约,我比较喜欢这种风格。前端写得不多,所以还得前端的女票指导一二,不然就是后端的设计的界面了,你懂的。
在Pages文件夹中,添加一个Razor界面,并撸好界面代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
@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方法,
- OnGet
- OnPost
- OnGetAsync
- OnPostAsync
如果是需要自定义的,举个栗子,定义为: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() { } } }
踩坑
一、Ajax Post请求, 400 Bad Request
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
二、DataTables参数实例加说明
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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": ": 以降序排列此列" } } });
Last
附上这些天来的成果,发现,我并不适合写前端,太丑了,哈哈。
最后,Show me the code。
Banana
Demo中会使用到这两个个人封装的组件:
Banana.Uow是基于Dapper封装的工作单元和仓储;
Banana.Utility是常用的工具类,有Redis,加解密,拼音等等;
欢迎大家在Issues中提出意见,大家共同进步。