laravel实战-通用后台管理系统-登录/注销和主界面
1. 登录/注销
1.1 界面布局: 登录框水平垂直居中
- 使用定位的方式
- 创建登录框, 指定宽度和高度, 登录框容器为
<body>
的第一个元素, 这样登录框的原始位置就抵住浏览器左边和顶部. - 使用
position: absolute
定位, 相对自己原位置定位. 设置偏移登录框原位置的左边50%, 顶部50%; 因为登录框有宽度和高度, 所以使用margin-left: -登录框宽度的一半
和margin-top: -登录框高度的一半
, 把登录框再往回退.
- 创建登录框, 指定宽度和高度, 登录框容器为
代码:
1-登录页 <body>
元素代码片段:
<body style="background: #1E9FFF">
<!-- 1. login-box是<body>的第一个子元素, 它的原始位置是抵住浏览器窗口的左边和顶部 -->
<div class="login-box">
<div class="layui-form">
@CSRF
<div class="layui-form-item">
<span class="title">通用后台管理系统</span>
</div>
<div class="layui-form-item">
<label class="layui-form-label">用户名</label>
<div class="layui-input-inline">
<input type="text" class="layui-input" name="username">
</div>
</div>
<!-- 其他登录表单空间省略... -->
</div>
</div>
</body>
2-水平垂直居中的样式代码片段
/* 2. 设置登录框水平垂直居中 */
.login-box {
/* 相对登录框在文档流中的位置定位, 因为登录框是<body>元素的第一个子元素,
相当于相对浏览器的左边框和上边框定位 */
position: absolute;
width: 480px;
height: 400px;
/* 偏移浏览器窗口宽度的50% */
left: 50%;
/* 偏移浏览器窗口宽度的50% */
top: 50%;
/* 左外边距设置为"负值的登录框宽度的一半", 这样登录框会向左移动240px */
margin-left: -240px;
/* 上外边距设置为"负值的登录框高度的一半", 这样登录框会向上移动200px */
margin-top: -200px;
background: white;
padding: 10px;
border-radius: 5px;
box-shadow: 5px 5px 20px #444;
}
- 复习: 设置元素水平垂直居中的其他方式
- 使用
margin: 0 auto
, 把容器设为水平居中; 再为设置父容器的样式:display: table-cell; vertical-align: middle;
, 即把父容器的显示方式设置为表格的单元格, 就能使用vertical-align
属性设置垂直居中了.
- 使用
- 把父容器设置为
flex
布局, 再设置样式属性:justify-content: center;
实现水平居中; 设置样式属性:align-items: center
实现垂直居中(主轴为水平方向).
- 把父容器设置为
- 把父容器设置为
grid
布局, 设置样式属性:grid-template-columns: auto 登录框宽度 auto
和grid-template-rows: auto 登录框高度 auto
把父容器设置为九宫格布局, 其中第5个单元格的宽高跟登录框相同. 为登录框容器设置样式:grid-column-start: 2; grid-column-end: 3; grid-row-start: 2; grid-column-end: 3
把登录框填充到第5个单元格.
- 把父容器设置为
1.2 验证码实现
使用
composer
下载gregwar/captcha
验证码插件.在图片点击时发送请求获取验证码, 并把服务器返回的验证码图片数据赋给
img.src
属性.服务器生成验证码后, 把验证码在
session
中也保存一份, 用来跟用户输入的验证码比对.
代码:
1-显示验证码的 <img>
元素:
<img src="" alt="" style="height: 38px; width: 140px; cursor: pointer" onclick="getCaptcha()">
2-异步获取验证码图片数据的js方法
function getCaptcha() {
$.get('/admin/account/captcha?rand=' + Math.random(), null, function(data) {
// 把服务器返回的验证码图片数据设置给img的src属性.
$('img').attr('src', data);
});
}
3-生成验证码的控制器方法
// 生成二维码
public function getCaptcha() {
//生成验证码图片的Builder对象,配置相应属性(4位验证码)
$builder = new CaptchaBuilder(4);
//可以设置图片宽高及字体
$builder->build($width = 100, $height = 40, $font = null);
//获取验证码的内容
$phrase = $builder->getPhrase();
session_start();
//把内容存入session
$_SESSION['captcha'] = $phrase;
// 返回验证码到客户端
echo $builder->inline();
}
4-登录时验证验证码的代码片段
public function doLogin(Request $request) {
// 通过laravel封装的Request对象获取前端封装的数据,当获取不到某个参数值时,会返回null
// 使用$_POST, $_GET, $_REQUEST获取参数值有个缺点, 就是如果获取未定义的key值时,会报错;
$username = $request->username;
$password = $request->password;
// 用户输入的验证码
$vericode = $request->vericode;
// 判断验证码
session_start();
$captcha = $_SESSION['captcha'];
if(strtoupper($vericode) != strtoupper($captcha)) {
exit(json_encode(['status' => 1, 'message' => '验证码输入错误']));
}
// 后续业务处理代码省略...
}
5-路由(略)
1.3 登录认证和注销登录
使用laravel提供的门面类
\Illuminate\Support\Facades\Auth
提供attempt([用户字段名 => 用户字段值, 密码字段名 => 密码字段值])
的方法验证登录名和密码.执行登录验证前要开启session:
session_start()
; 登录成功返回json消息时, 要用return返回, 不能用exit()
, 因为后者会无法触发session写入用户信息.
- 给转到登录页面的路由起名字, 当session过期时, 自动跳转到登录界面. 路由名字要跟登录情况验证中间件
\App\Http\Middleware\Authenticate
中redirectTo()
方法指定的路由名称一致(默认为login
).
- 同样使用
Auth
门面类提供的logout()
方式实现用户注销.
登录功能代码:
1-登录按钮
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="formDemo" onclick="doLogin()">立即提交</button>
</div>
</div>
2-提交登录请求的js方法
function doLogin() {
var username = $.trim($('input[name="username"]').val());
var password = $.trim($('input[name="password"]').val());
var vericode = $.trim($('input[name="vericode"]').val());
// laravel token,不带上,laravel会报419错误
var _token = $('input[name="_token"]').val();
if (username == undefined || username == '') {
return layer.alert('请输入用户名', {
icon: 2
});
}
if (password == undefined || password == '') {
return layer.alert('请输入密码', {
icon: 2
});
}
if (vericode == undefined || vericode == '') {
return layer.alert('请输入验证码', {
icon: 2
});
}
var data = {
username: username,
password: password,
vericode: vericode,
_token: _token
};
// 提交登录请求
$.post('/admin/account/doLogin', data, function(data) { //alert(data);return;
var res = JSON.parse(data);
// 登陆失败
if (res.status != undefined && res.status == '1') {
// 提示错误
layer.alert(res.message, {
icon: 2
});
} else if (res.status == '0') { // 登陆成功
layer.msg(res.message, {
icon: 1
});
// 1秒后跳转到主页面
setTimeout(() => {
window.location.href = "/admin/home/index";
}, 1000);
}
});
}
3-后台处理登录验证的控制器方法
public function doLogin(Request $request) {
// 通过laravel封装的Request对象获取前端封装的数据,当获取不到某个参数值时,会返回null
// 使用$_POST, $_GET, $_REQUEST获取参数值有个缺点, 就是如果获取未定义的key值时,会报错;
$username = $request->username;
$password = $request->password;
$vericode = $request->vericode;
// 判断验证码
session_start();
$captcha = $_SESSION['captcha'];
if(strtoupper($vericode) != strtoupper($captcha)) {
exit(json_encode(['status' => 1, 'message' => '验证码输入错误']));
}
// 判空
if($username == null || $username == '') {
exit(json_encode(['status' => 1, 'message' => '用户名不能为空']));
}
if($password == null || $password == '') {
exit(json_encode(['status' => 1, 'message' => '用户名不能为空']));
}
// 查询数据库,校验用户名和密码
// laravel提供的一个用户名和密码的认证工具Auth::attempt--AES加密
if(Auth::attempt(['username' => $username, 'password' => $password])) {
// 注意,这里要用return返回,不要用exit,否则无法触发session写入用户信息的功能
return (json_encode(['status' => 0, 'message' => '登陆成功']));
} else {
exit(json_encode(['status' => 1, 'message' => '账号或密码错误']));
}
}
4-路由
// 后台登录(name()方法: 给路由起名字)
Route::get('/admin/account/login', 'admins\Account@login')->name('login');
注销登录功能代码:
1-执行注销登录的控制器方法
public function logout() {
Auth::logout();
return (json_encode(['status' => 0, 'message' => '登出成功!']));
}
2-注销链接, 路由(略)
2. 主界面
2.1 页面布局
左边栏导航, 整体浮动起来, 一级菜单用layui的手风琴折叠面板, 二级菜单采用layui的垂直/侧边导航的一级导航样式.
内容区容器也浮动起来, 内容区采用内嵌iframe加载功能页面的方式. 页面加载完成, 要重新设置iframe的高度 = 浏览器窗口高度 - 顶部导航高度(50px).
布局代码:
1-左边栏菜单布局
<!-- left menu -->
<div class="left-menu">
<!-- 在父容器加上lay-accordion属性, 就会开启手风琴模式, 打开一个子菜单, 其他菜单的子菜单自动合并 -->
<div class="layui-collapse" lay-accordion style="width: 200px; float:left" lay-accordion>
@foreach($menus as $menu1st)
<!-- 一级菜单 -->
<div class="layui-colla-item">
<h2 class="layui-colla-title">{{$menu1st->title}}</h2>
<!-- 加入.layui-show样式, 就会默认打开子菜单 -->
<div class="layui-colla-content">
<!-- 二级菜单列表 -->
<ul class="layui-nav layui-nav-tree" id="L_demoNav" lay-filter="test">
@foreach($menu1st->childs as $menu2st)
<li class="layui-nav-item">
<!-- controller和method属性分别记录该菜单项对应的控制器名和方法名 -->
<a href="javascript:;" onclick="firemenu(this)" controller="{{$menu2st->controller}}" method="{{$menu2st->action}}">{{$menu2st->title}}</a>
</li>
@endforeach
</ul>
</div>
</div>
@endforeach
</div>
</div>
2-内容区布局
<!-- main -->
<div class="main">
<!-- frameborder="0": 设置后iframe就没有边框了. 提升美观度 -->
<!-- onload: 加载页面完成后要重新设置iframe的高度 -->
<iframe src="/admin/home/welcome" frameborder="0" onload="resetIframeHeight(this)"></iframe>
</div>
3-内容区布局样式: 不给 .main
设置宽高, 使用定位的上下左右属性值, 来设置 .main
容器的大小和位置.
.main {
position: absolute;
/* 距离左边200px */
left: 200px;
/* 距离右边0px, 这样就可以撑满从左边200px到最右边的宽度 */
right: 0px;
top: 50px;
bottom: 0px;
}
.main iframe {
/* .main设置了left和right, 但是iframe并没有充满整个.main的宽度, 所以要设置width:100% */
width: 100%;
height: 100%;
}
4-页面加载完成后重新设置iframe的高度
function resetIframeHeight(iframe) {
// 设置iframe的高度 = 浏览器窗口高度 - 顶部导航高度(50px)
var main_height = document.documentElement.clientHeight - 50;
iframe.style.height = main_height;
}
2.2 用户菜单权限获取和菜单点击后渲染相应页面
查询
admin_menu
,admin_group
,admin
表获取用户菜单权限.在遍历渲染菜单项时, 给菜单项元素添加自定义属性
controller
和action
, 分别赋值为该菜单项对应的控制器名和方法名.- 可以扩展
admin_menu
表, 再增加module
属性和menu_param
属性, 前者记录控制器所属模块; 后者记录点击该菜单项, 默认发送给后台的参数.
- 可以扩展
在获取菜单项对应的页面的js方法中, 获取被点击菜单的
controller
和action
, 拼成获取对应页面的url.
代码:
1-左侧菜单渲染(见上一节)
2-获取用户菜单权限列表代码片段
public function index(Request $req) {
// 从request对象中取出登录用户的信息(在权限中间件中有设置)
$loginInfo = $req->loginInfo;
$loginRights = $loginInfo->rights;
// 查询出一级菜单列表(加上whereIn, 过滤出登录用户才有的菜单权限)
$data['menus'] = DB::table('admin_menu')->where('pid', 0)->where('ishidden', 0)->where('status', 0)
->whereIn('mid', $loginRights)
->orderBy('ord')
->get()->all();
// 查询二级菜单(加上whereIn, 过滤出登录用户才有的菜单权限)
foreach($data['menus'] as $key => $menu) {
$childs = DB::table('admin_menu')->where('pid', $menu->mid)->where('ishidden', 0)->where('status', 0)
->whereIn('mid', $loginRights)
->get()->all();
$menu->childs = $childs;
}
// 其他处理略...
return view('admins/home/index', $data);
}
3-点击后在iframe中渲染相应的页面
// 点击左边栏的菜单处理脚本
function firemenu(obj) {
var controller = $(obj).attr('controller');
var method = $(obj).attr('method');
// iframe中渲染链接到的页面
$('.main iframe').attr('src', '/admin/' + controller + '/' + method);
}
学习心得
设置项目水平垂直居中的方式有多种, 西门老师教的是相对元素在文档流中定位+
margin-left
的方式实现.使用laravel提供的
Auth
门面类, 可以很方便的实现用户登录验证和注销登录.让iframe完美的嵌入到父页面中的实现, 很有用, 写下来备查.