大事件后台管理开发-前端网页

html+css+js+layui

1. 项目前期的准备工作

1.1. 初始化项目结构

  1. 项目文件夹命名为 code,在该目录下新建 login.htmlindex.html 页面
  2. code目录下放入 assetshome文件夹
  3. assets 中新建 lib文件,放入 layuijquery的开发包

1.2. 使用GitHub管理大事件的项目

  1. code 目录中运行 git init 命令

  2. code 目录中运行 git add . 命令

  3. code 目录下运行 git commit -m "init project" 命令

  4. 新建 Github 仓库 web_bigevent

  5. 将本地仓库和Github仓库建立关联关系

    git remote add origin https://[token]@github.com/[username]/web_bigevent.git

  6. 将本地仓库的代码推送到Github仓库中,运行git push -u origin master 命令

  7. 运行 git checkout -b login 命令,创建并切换到 login 分支

  8. 查看当前所处分支,运行 git branch 命令

1.3. 安装插件

Expressv0.0.5

Hosts current workspace with Express web server in Visual Studio Code

快捷键 shift+command+p 打开命令面板

选择 express: Hosts Current Workspace and Open in Browser

2. 登录注册

2.1. 绘制login页面的基本结构

样式来源 :https://layuion.com/docs/

  1. 编写 HTML 结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>大事件后台管理系统</title>
<!-- 导入layui样式 -->
<link rel="stylesheet" href="./assets/lib/layui/css/layui.css">
<!-- 创建自定义样式 -->
<link rel="stylesheet" href="./assets/css/login.css">
</head>
<body>
<!-- 头部logo区域 -->
<div class="layui-main">
<!-- 背景图片 -->
<img src="./assets/images/logo.png" alt="">
</div>

<!-- 登录注册区域 -->
<div class="loginAndRegBox">
<!-- 标题 -->
<div class="title-box"></div>
<!-- 登录页面 -->
<div class="login-box"></div>
<!-- 注册页面 -->
<div class="reg-box"></div>
</div>
</body>
</html>
  1. 美化样式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
html,
body{
margin: 0;
padding: 0;
height: 100%;
width: 100%;
background: url(../images/login_bg.jpg) no-repeat center / cover;
}

.loginAndRegBox{
width: 400px;
height: 310px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
background-color: #fff;
}

.title-box{
width: 100%;
height: 60px;
background: url(../images/login_title.png) no-repeat center;
}

2.2. 实现登录和注册的按需切换

  1. 编写html结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- 登录注册区域 -->
    <div class="loginAndRegBox">
    <!-- 标题 -->
    <div class="title-box"></div>
    <!-- 登录页面 -->
    <div class="login-box">
    <a href="javascript:;">去注册</a>
    </div>
    <!-- 注册页面 -->
    <div class="reg-box">
    <a href="javascript:;">去登录</a>
    </div>
    </div>
  2. 引入jquery

    1
    <script src="./assets/lib/jquery.js"></script>
  3. 创建 asset/js/login.js ,并引入到 login.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $(".login-box a").on("click",()=>{
    $(".login-box").hide();
    $(".reg-box").show();
    })

    $(".reg-box a").on("click",()=>{
    $(".reg-box").hide();
    $(".login-box").show();
    })

2.3. 绘制登录表单的基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 登录页面 -->
<div class="login-box">
<form class="layui-form" action="">
<div class="layui-form-item">
<input type="text" name="username" required lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-item">
<input type="password" name="password" required lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-item">
<button class="layui-btn layui-btn-normal layui-btn-fluid" lay-submit lay-filter="formDemo">登录</button>
</div>
</form>
<a href="javascript:;">去注册</a>
</div>

2.4. 美化登录表单的样式

1
2
3
4
5
6
7
8
9
10
11
12
13
.login-box,
.reg-box{
padding: 0 30px;
}

.login-box a,
.reg-box a{
float: right;
}

.reg-box{
display: none;
}

2.5. 绘制文本框前面的小图标

  1. 在用户名的文本框之前,添加如下的标签结构:

    1
    <i class="layui-icon layui-icon-username"></i>
  2. 在密码框之前,添加如下的标签结构:

    1
    <i class="layui-icon layui-icon-password"></i>
  3. 美化样式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    .loginAndRegBox .layui-form-item{
    position: relative;
    }

    .loginAndRegBox .layui-form-item i{
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    }
    .loginAndRegBox .layui-form-item .layui-input{
    padding-left: 32px;
    }

2.6. 快速绘制注册的表单

将登录的表单复制一份,并修改为注册的表单即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 注册页面 -->
<div class="reg-box">
<form class="layui-form" action="">
<div class="layui-form-item">
<i class="layui-icon layui-icon-username"></i>
<input type="text" name="username" required lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-item">
<i class="layui-icon layui-icon-password"></i>
<input type="password" name="password" required lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-item">
<i class="layui-icon layui-icon-password"></i>
<input type="password" name="password" required lay-verify="required" placeholder="再次确认密码" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-item">
<button class="layui-btn layui-btn-fluid layui-btn-normal" lay-submit lay-filter="formDemo">注册</button>
</div>
</form>
<a href="javascript:;">去登录</a>
</div>

2.7. 实现登录表单的验证

  1. 导入 layui 的 js 文件

    1
    2
    <!-- 导入layui.js -->
    <script src="./assets/lib/layui/layui.js"></script>
  2. 为需要验证的表单项添加 lay-verify 属性,同时指定具体的校验规则即可

    https://layuion.com/docs/modules/form.html#verify

2.8. 自定义校验规则

  1. 从 layui 中获取 form 对象:

    1
    var form = layui.form
  2. 通过 form.verify() 函数自定义校验规则:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    form.verify({
    // 自定义了一个叫做 pwd 校验规则
    pwd: [/^[\S]{6,12}$/, '密码必须6到12位,且不能出现空格'],
    // 校验两次密码是否一致的规则
    repwd: function(value) {
    // 通过形参拿到的是确认密码框中的内容
    // 还需要拿到密码框中的内容
    // 然后进行一次等于的判断
    // 如果判断失败,则return一个提示消息即可
    var pwd = $('.reg-box [name=password]').val()
    if (pwd !== value) {
    return '两次密码不一致!'
    }
    }
    })
  3. 按需为表单项添加校验规则:

    1
    2
    3
    4
    <input type="password" name="password" required  lay-verify="required|pwd" placeholder="请输入密码" autocomplete="off" class="layui-input">


    <input type="password" name="repassword" required lay-verify="required|pwd|repwd" placeholder="再次确认密码" autocomplete="off" class="layui-input" />

2.9. 发起注册用户的Ajax请求

  1. 为注册表单添加Id:

    1
    2
    <!-- 注册的表单 -->
    <form class="layui-form" id="form_reg"></form>
  2. 监听提交事件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     var layer = layui.layer;
    // 监听注册表单的提交事件
    $("#form-reg").on("submit",(e)=>{
    // 1. 阻止默认的提交行为
    e.preventDefault()
    // 2. 发起Ajax的POST请求
    const username = $("#form-reg [name=username]").val();
    // console.log(username);
    const password = $("#form-reg [name=password]").val();

    $.post("http://127.0.0.1:3007/api/register",{
    username:username,
    password:password
    },(result)=>{
    // console.log(result);
    if(result.status==1){
    return layer.msg(result.message);
    }else{
    layer.msg('注册成功,请登录!');
    // 模拟人的点击行为
    $(".reg-box a").click();
    }
    });
    })

2.10. 使用layer提示消息

  1. 导入 layer:

    1
    var layer = layui.layer
  2. 调用 layer.msg() 提示消息:

    1
    layer.msg('注册成功,请登录!')

2.11. 发起登录的Ajax请求

  1. 为登录表单添加id:

    1
    <form class="layui-form" id="form_login"></form>
  2. 监听提交事件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 监听注册表单的登录事件
    $("#form-login").on("submit",(e)=>{
    // 1. 阻止默认的提交行为
    e.preventDefault();
    // console.log($(this).serialize());
    // 2. 发起Ajax请求
    $.ajax({
    url: "http://127.0.0.1:3007/api/login",
    method: 'POST',
    // 快速获取表单中的数据
    data: {
    username: $("#form-login [name=username]").val(),
    password: $("#form-login [name=password]").val()
    },
    success: function(res) {
    if(res.status==1){
    return layer.msg(res.message);
    }
    layer.msg("登录成功!");
    // 存储token
    localStorage.setItem("token",res.token);
    // 跳转至主页
    var url = location.href;
    url = url.substring(0,url.lastIndexOf("/"));
    location.href=url+"/index.html";
    }
    })

    });

2.12. 在ajaxPrefilter中统一拼接请求的根路径

  1. /assets/js 目录中新建 baseAPI.js

  2. 编写如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    // 注意:每次调用 $.get() 或 $.post() 或 $.ajax() 的时候,
    // 会先调用 ajaxPrefilter 这个函数
    // 在这个函数中,可以拿到我们给Ajax提供的配置对象
    $.ajaxPrefilter(function(options) {
    // 在发起真正的 Ajax 请求之前,统一拼接请求的根路径
    options.url = 'http://ajax.frontend.itheima.net' + options.url
    })

  3. 修改登录,注册中ajax提交的路由

    1
    2
    3
    4
    5
    // 监听注册表单的提交事件
    $.post("/api/register",...);

    // 监听登录表单的提交事件
    $.post("/api/login",...);

2.13. 提交login分支的代码到GitHub

  1. 运行 git add . 命令
  2. 运行 git commit -m "完成了登录和注册的功能" 命令
  3. 运行 git push -u origin login 命令
  4. 运行 git checkout master 命令
  5. 运行 git merge login 命令
  6. 运行 git push 命令
  7. 运行 git checkout -b index 命令

3. 后台主页

3.1. 快速实现后台主页的布局效果

从 layUI 官方文档中粘贴布局的主要代码,并修改如下:

https://layuion.com/docs/element/layout.html#admin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>大事件后台管理系统-首页</title>
<!-- 导入layui样式 -->
<link rel="stylesheet" href="./assets/lib/layui/css/layui.css">
</head>
<body>
<div class="layui-layout layui-layout-admin">
<div class="layui-header">
<div class="layui-logo layui-hide-xs layui-bg-black">
<img src="./assets/images/logo.png" alt="">
</div>
<!-- 头部区域(可配合layui 已有的水平导航) -->
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item layui-hide layui-show-md-inline-block">
<a href="javascript:;">
<img src="./assets/images/sample.jpg" class="layui-nav-img">
个人中心
</a>
<dl class="layui-nav-child">
<dd><a href="">基本资料</a></dd>
<dd><a href="">更换头像</a></dd>
<dd><a href="">重置密码</a></dd>
</dl>
</li>
<li class="layui-nav-item" lay-header-event="menuRight" lay-unselect>
<a href="javascript:;">退出 </a>
</li>
</ul>
</div>

<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<!-- 左侧导航区域(可配合layui已有的垂直导航) -->
<ul class="layui-nav layui-nav-tree" lay-filter="test">
<li class="layui-nav-item"><a href="javascript:;">首页</a></li>
<li class="layui-nav-item">
<a class="" href="javascript:;">文章管理</a>
<dl class="layui-nav-child">
<dd><a href="javascript:;">文章类别</a></dd>
<dd><a href="javascript:;">文章列表</a></dd>
<dd><a href="javascript:;">发布文章</a></dd>
</dl>
</li>
<li class="layui-nav-item">
<a href="javascript:;">个人中心</a>
<dl class="layui-nav-child">
<dd><a href="javascript:;">基本资料</a></dd>
<dd><a href="javascript:;">更换头像</a></dd>
<dd><a href="javascript:;">重置密码</a></dd>
</dl>
</li>
</ul>
</div>
</div>

<div class="layui-body">
<!-- 内容主体区域 -->
<div style="padding: 15px;">内容主体区域。记得修改 layui.css 和 js 的路径</div>
</div>

<div class="layui-footer">
<!-- 底部固定区域 -->
© 大事件后台管理系统
</div>
</div>
<!-- 导入layui.js -->
<script src="./assets/lib/layui/layui.js"></script>
</body>
</html>

3.2. 导入自定义样式

  1. 导入css文件

    1
    2
    3
    4
    <head>
    <!-- 导入自定义样式 -->
    <link rel="stylesheet" href="./assets/css/index.css">
    </head>
  2. 添加样式

    1
    2
    3
    .layui-footer{
    text-align: center;
    }

3.3. 使用lay-shrink实现左侧菜单互斥效果

1
2
3
4
5
6
7
8
<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<!-- 左侧导航区域(可配合layui已有的垂直导航) -->
<ul class="layui-nav layui-nav-tree" lay-shrink="all">
<!-- 省略其他代码 -->
</ul>
</div>
</div>

3.4. 为菜单项添加图标

  1. 导入第三方的图标库:

    1
    2
    <!-- 导入第三方图标库 -->
    <link rel="stylesheet" href="/assets/fonts/iconfont.css" />
  2. 修改左侧菜单的结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    <!-- 左侧导航区域(可配合layui已有的垂直导航) -->
    <ul class="layui-nav layui-nav-tree" lay-shrink="all" lay-filter="test">
    <li class="layui-nav-item layui-this">
    <!--
    href属性用来指定打开哪个网页
    target属性用来指定在哪里打开网页
    target=fm 表示在name为fm的iframe中打开指定网页
    -->
    <a href="./home/dashboard.html" target="fm">
    <span class="iconfont icon-home"></span>
    首页
    </a>
    </li>
    <li class="layui-nav-item">
    <a class="" href="javascript:;">
    <span class="iconfont icon-16"></span>文章管理
    </a>
    <dl class="layui-nav-child">
    <dd>
    <a href="javascript:;">
    <i class="layui-icon layui-icon-app"></i>
    文章类别
    </a>
    </dd>
    <dd>
    <a href="javascript:;">
    <i class="layui-icon layui-icon-list"></i>
    文章列表
    </a>
    </dd>
    <dd>
    <a href="javascript:;">
    <i class="layui-icon layui-icon-edit"></i>
    发布文章
    </a>
    </dd>
    </dl>
    </li>
    <li class="layui-nav-item">
    <a href="javascript:;">
    <span class="iconfont icon-user"></span>个人中心
    </a>
    <dl class="layui-nav-child">
    <dd>
    <a href="javascript:;">
    <i class="layui-icon layui-icon-form"></i>
    基本资料
    </a>
    </dd>
    <dd>
    <a href="javascript:;">
    <i class="layui-icon layui-icon-picture"></i>
    更换头像
    </a>
    </dd>
    <dd>
    <a href="javascript:;">
    <i class="layui-icon layui-icon-key"></i>
    重置密码
    </a>
    </dd>
    </dl>
    </li>
    </ul>
  3. 美化样式

    1
    2
    3
    4
    .iconfont,
    .layui-nav-child .layui-icon{
    margin-right: 10px;
    }

3.5. 使用iframe标签在内容主体区域显示网页内容

  1. 在页面主体的 div 中添加 iframe

    1
    2
    3
    4
     <div class="layui-body">
    <!-- 内容主体区域 -->
    <iframe src="./home/dashboard.html" name="fm" frameborder="0"></iframe>
    </div>
  2. 首页链接添加hreftarget属性:

    1
    2
    3
    4
    5
    6
    7
    8
    <li class="layui-nav-item layui-this">
    <!--
    href属性用来指定打开哪个网页
    target属性用来指定在哪里打开网页
    target=fm 表示在name为fm的iframe中打开指定网页
    -->
    <a href="./home/dashboard.html" target="fm">首页</a>
    </li>
  3. 美化

    1
    2
    3
    4
    5
    6
    7
    8
    iframe{
    width: 100%;
    height: 100%;
    }

    .layui-body{
    overflow: hidden;
    }

3.6. 解决小问题

  1. 首页 对应的导航 Item 项添加 layui-this 属性:

    1
    2
    3
    4
    <li class="layui-nav-item layui-this">
    <a href="/home/dashboard.html" target="fm">
    <span class="iconfont icon-home"></span>首页</a>
    </li>
  2. 强制清除 <a> 链接的 CSS3 动画:

    1
    2
    3
    a {
    transition: none !important;
    }

3.7. 渲染图片头像和文字头像

  1. 修改头部区域的的头像结构如下:

    1
    2
    3
    4
    5
    <a href="javascript:;" class="userinfo">
    <img src="./assets/images/sample.jpg" class="layui-nav-img">
    <span class="text-avatar">A</span>
    个人中心
    </a>
  2. 在左侧导航区域的 ul 之前添加如下头像结构:

    1
    2
    3
    4
    5
    <div class="userinfo">
    <img src="./assets/images/sample.jpg" class="layui-nav-img">
    <span class="text-avatar">A</span>
    <span>欢迎 ***</span>
    </div>
  3. 添加样式美化 UI 结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    .text-avatar{
    display: inline-block;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background-color: rgb(65 151 136);
    font-size: 20px;
    text-align: center;
    line-height: 30px;
    margin-right: 10px;
    position: relative;
    top: 4px;
    }

    .userinfo{
    height: 60px;
    line-height: 60px;
    text-align: center;
    }
    .layui-side-scroll .userinfo{
    border-bottom: 1px solid #282b33;
    }

    .userinfo img,
    .userinfo span{
    display: none;
    user-select: none;
    }

3.8. 获取用户的基本信息

  1. 导入需要的脚本:

    1
    2
    3
    4
    5
    6
    <!-- 导入jquery -->
    <script src="./assets/lib/jquery.js"></script>
    <!-- 导入自己封装的 baseAPI -->
    <script src="./assets/js/baseAPI.js"></script>
    <!-- 导入自定义js -->
    <script src="./assets/js/index.js"></script>
  2. 定义 getUserInfo 函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    var layer = layui.layer;
    getUserInfo();

    function getUserInfo(){
    $.ajax({
    method: "GET",
    url: "/my/userinfo",
    // headers 就是请求头配置对象
    headers: {
    Authorization: localStorage.getItem("token") || ''
    },
    success: (result)=>{
    // console.log(result);
    if(result.status==1){
    return layer.msg(result.message);
    }else{
    // TODO:获取头像信息
    renderAvatar(result.data);
    }
    }
    })
    }

3.9. 渲染用户头像

定义 renderAvatar 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 获取头像信息 
function renderAvatar(user){
// {"id": 4,"username": "admin","nickname": null, "email": null,"user_pic": null}
// 用户头像
const avatar = user.user_pic;
// 用户姓名
const name = user.nickname || user.username;
if(avatar){
// 渲染图片
$(".text-avatar").hide().siblings("img").attr("src",avatar).show();
}else{
// 渲染文字
// 只取姓名的第一个字符
const first = name[0].toUpperCase();
$(".text-avatar").html(first).css("display","inline-block").siblings("img").hide();
}
$(".text-avatar").siblings("span").show().html("欢迎 "+name);
}

3.10. 统一为有权限的接口设置headers请求头

在 baseAPI的 ajaxPrefilter 中添加如下代码:

1
2
3
4
5
6
//  统一为有权限的接口,设置 headers 请求头
if(options.url.indexOf("/my")!=-1){
options.headers = {
Authorization: localStorage.getItem("token") || ''
}
}

3.11. 实现退出功能

  1. 修改退出的<a> 链接如下:

    1
    2
    3
    4
    <a href="javascript:;" id="btnLogout">
    <span class="iconfont icon-tuichu"></span>
    退出
    </a>
  2. 实现退出功能:

    采用layui的提示框

    https://layuion.com/docs/modules/layer.html#layer.confirm

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 退出按钮
    $("#btnLogout").on("click",()=>{
    console.log($(this));
    layer.confirm('确定退出登录?', {icon: 3, title:'提示'},
    function(index){
    //do something
    // 删除本地token
    localStorage.removeItem("token");

    // 跳转至登录页
    // 跳转至主页
    var url = location.href;
    url = url.substring(0,url.lastIndexOf("/"));
    location.href=url+"/login.html";

    layer.close(index);
    });
    });

3.12. 控制用户的访问权限

在调用有权限接口的时候,指定complete回调函数

jquery的ajax函数中请求成功会回调 success,失败会回调 error,但无论成功与否都会回调 complete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 不论成功还是失败,最终都会调用 complete 回调函数
complete: function(res){
// console.log('执行了 complete 回调:')
// console.log(res)
// 可以使用 res.responseJSON 拿到服务器响应回来的数据
const resJson = res.responseJSON;
// console.log(resJson);
if(resJson.status==1 && resJson.message=='身份认证失败!'){
// layer.msg(resJson.message);
// 清空token
localStorage.removeItem("token");
// 跳转至登录页
var url = location.href;
url = url.substring(0,url.lastIndexOf("/"));
location.href=url+"/login.html";

}
}

3.13. 优化权限控制的代码

将权限控制的代码,从每个请求中,抽离到 ajaxPrefilter 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 全局统一挂载 complete 回调函数
// 不论成功还是失败,最终都会调用 complete 回调函数
options.complete = function(res){
// console.log('执行了 complete 回调:')
// console.log(res)
// 可以使用 res.responseJSON 拿到服务器响应回来的数据
const resJson = res.responseJSON;
// console.log(resJson);
if(resJson.status==1 && resJson.message=='身份认证失败!'){
// layer.msg(resJson.message);
// 清空token
localStorage.removeItem("token");
// 跳转至登录页
var url = location.href;
url = url.substring(0,url.lastIndexOf("/"));
location.href=url+"/login.html";
}
}

3.14. 提交index分支的代码到GitHub

  1. 运行 git add . 命令
  2. 运行 git commit -m "完成了主页功能的开发" 命令
  3. 运行 git push -u origin index命令
  4. 运行 git checkout master 命令
  5. 运行 git merge index 命令
  6. 运行 git push 命令
  7. 运行 git checkout -b user 命令

4. 用户管理

4.1. 基本资料

4.1.1. 创建基本资料对应的页面

  1. 在根目录下新建 /user/user_info.html 并初始化如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>大事件后台管理系统-基本资料</title>
    <!-- 导入layui样式 -->
    <link rel="stylesheet" href="../assets/lib/layui/css/layui.css">
    <!-- 导入自定义样式 -->
    <link rel="stylesheet" href="../assets/css/user/userInfo.css">
    </head>
    <body>
    用户资料
    </body>
    </html>
  2. 新建 /assets/css/user/userInfo.css 并初始化如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    html,
    body{
    margin: 0;
    padding: 0;
    }

    body{
    background-color: #f2f3f5;
    padding: 15px;
    }
  3. 修改 index.html基本资料 对应 <a>

    1
    2
    3
    4
    <a href="./user/userInfo.html" target="fm">
    <i class="layui-icon layui-icon-form"></i>
    基本资料
    </a>

4.1.2. 绘制基本资料对应的表单

  1. 编写如下的表单结构:

    采用layui的卡片面板绘制表单

    https://layuion.com/docs/element/panel.html#card

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>大事件后台管理系统-基本资料</title>
    <!-- 导入layui样式 -->
    <link rel="stylesheet" href="../assets/lib/layui/css/layui.css">
    <!-- 导入自定义样式 -->
    <link rel="stylesheet" href="../assets/css/user/userInfo.css">
    </head>
    <body>
    <div class="layui-card">
    <div class="layui-card-header">修改用户信息</div>
    <div class="layui-card-body">
    <form class="layui-form" action="">
    <div class="layui-form-item">
    <label class="layui-form-label">登录名称</label>
    <div class="layui-input-block">
    <input type="text" name="username" required lay-verify="required" placeholder="请输入登录名称" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <label class="layui-form-label">用户昵称</label>
    <div class="layui-input-block">
    <input type="text" name="nickname" required lay-verify="required" placeholder="请输入用户昵称" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <label class="layui-form-label">用户邮箱</label>
    <div class="layui-input-block">
    <input type="text" name="email" required lay-verify="required|email" placeholder="请输入用户邮箱" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <div class="layui-input-block">
    <button class="layui-btn" lay-submit lay-filter="formDemo">提交修改</button>
    <button type="reset" class="layui-btn layui-btn-primary">重置</button>
    </div>
    </div>
    </form>
    </div>
    </div>
    </body>
    </html>
  2. 在页面底部导入如下的脚本:

    1
    2
    3
    4
    5
    6
    <!-- 导入layui.js -->
    <script src="../assets/lib/layui/layui.js"></script>
    <!-- 导入jquery.js -->
    <script src="../assets/lib/jquery.js"></script>
    <!-- 导入自定义js -->
    <script src="../assets/js/user/userInfo.js"></script>
  3. user_info.js 中编写如下的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var form = layui.form;

    form.verify({
    nickname: function(value){
    if(value.length>6){
    return '昵称长度必须在 1 ~ 6 个字符之间!';
    }
    }
    })

4.1.3. 获取用户的基本信息

  1. 导入 baseAPI

    1
    <script src="/assets/js/baseAPI.js"></script>
  2. user_info.js 中定义并调用 initUserInfo 函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    initUserInfo();
    // 初始化用户信息
    function initUserInfo(){
    $.ajax({
    method: "GET",
    url: "/my/userinfo",
    success: (result)=>{
    if(result.status==1){
    return layer.msg(result.message);
    }else{
    // TODO:填充到表单中
    }
    }
    })
    }

4.1.4. 使用form.val方法快速为表单赋值

  1. 为表单指定 lay-filter 属性:

    form.val('filter', object);用于给指定表单集合的元素赋值和取值

    https://layuion.com/docs/modules/form.html#val

    1
    <form class="layui-form" lay-filter="formUserInfo"></form>
  2. 调用 form.val() 方法为表单赋值:

    1
    form.val('formUserInfo', res.data);
  3. 使用隐藏域保存用户的 id 值:

    1
    2
    3
    4
    5
    6
    7
    <!-- form 表单区域 -->
    <form class="layui-form" lay-filter="formUserInfo">
    <!-- 这是隐藏域 -->
    <input type="hidden" name="id" value="" />

    <!-- 省略其他代码 -->
    </form>

4.1.5. 实现表单的重置效果

阻止表单的默认重置行为,再重新获取用户信息即可

1
2
3
4
5
6
7
// 重置按钮
$("#btnReset").on("click",function(e){
// 阻止表单的默认重置行为
e.preventDefault();
initUserInfo();

});

4.1.6. 发起请求更新用户的信息

阻止表单的默认提交行为,并发起数据请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  // 监听表单的提交事件
$(".layui-form").on("submit",function(e){
// 阻止表单的默认重置行为
e.preventDefault();

$.ajax({
method: "POST",
url: '/my/userinfo',
data: {
id: $(".layui-form [name=id]").val(),
username: $(".layui-form [name=username]").val(),
nickname: $(".layui-form [name=nickname]").val(),
email: $(".layui-form [name=email]").val()
},
success: function(res){
if(res.status==1){
return layer.msg(res.message);
}
layer.msg(res.message);
// 调用父页面中的方法,重新渲染用户的头像和用户的信息
parent.window.getUserInfo();
}
});

});

4.2. 重置密码

4.2.1. 渲染重置密码的页面结构

  1. /user/userPwd.html 页面中编写如下的结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>大事件后台管理系统-重置密码</title>
    <!-- 导入layui样式 -->
    <link rel="stylesheet" href="../assets/lib/layui/css/layui.css">
    <!-- 导入自定义样式 -->
    <link rel="stylesheet" href="../assets/css/user/userPwd.css">
    </head>
    <body>
    <div class="layui-card">
    <div class="layui-card-header">修改密码</div>
    <div class="layui-card-body">
    <form class="layui-form" action="" lay-filter="formUserInfo">
    <!-- 隐藏用户id -->
    <input type="hidden" name="id" value="">
    <div class="layui-form-item">
    <label class="layui-form-label">原密码</label>
    <div class="layui-input-block">
    <input type="password" name="oldPwd" required lay-verify="required" placeholder="请输入原密码" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <label class="layui-form-label">新密码</label>
    <div class="layui-input-block">
    <input type="password" name="newPwd" required lay-verify="required" placeholder="请输入新密码" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <label class="layui-form-label">确认密码</label>
    <div class="layui-input-block">
    <input type="password" name="rePwd" required lay-verify="required" placeholder="请确认新密码" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <div class="layui-input-block">
    <button class="layui-btn" lay-submit lay-filter="formDemo">修改密码</button>
    <button type="reset" class="layui-btn layui-btn-primary" id="btnReset">重置</button>
    </div>
    </div>
    </form>
    </div>
    </div>

    </body>
    </html>
  2. /assets/css/user/userPwd.css 中编写如下的样式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    html,
    body{
    margin: 0;
    padding: 0;
    width: 100%;
    }

    body{
    padding: 15px;
    background-color: #f2f3f5;
    }

    .layui-card{
    width: 50%;
    }

4.2.2. 为密码框定义校验规则

  1. 在 body 结束标签之前导入如下的 script 标签:

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- 导入layui.js -->
    <script src="../assets/lib/layui/layui.js"></script>
    <!-- 导入jquery.js -->
    <script src="../assets/lib/jquery.js"></script>
    <!-- 导入baseAPI.js -->
    <script src="../assets/js/baseAPI.js"></script>
    <!-- 导入自定义js -->
    <script src="../assets/js/user/userInfo.js"></script>
  2. userPwd.js 中定义如下的三个校验规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var form = layui.form;
    var layer = layui.layer;

    form.verify({
    pwd: [/^[\S]{6,12}$/,"密码必须6到12位,且不能出现空格"],
    samePwd: function(val){
    if(val== $(".layui-form [name=oldPwd").val()){
    return '新旧密码不能相同!';
    }
    },
    rePwd: function(val){
    if(val!= $(".layui-form [name=newPwd").val()){
    return '两次密码不一致!';
    }
    }
    })
  3. 为密码框分别添加对应的校验规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <form class="layui-form" action="" lay-filter="formUserInfo">
    <!-- 隐藏用户id -->
    <input type="hidden" name="id" value="">
    <div class="layui-form-item">
    <label class="layui-form-label">原密码</label>
    <div class="layui-input-block">
    <input type="password" name="oldPwd" required lay-verify="required|pwd" placeholder="请输入原密码" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <label class="layui-form-label">新密码</label>
    <div class="layui-input-block">
    <input type="password" name="newPwd" required lay-verify="required|pwd|samePwd" placeholder="请输入新密码" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <label class="layui-form-label">确认密码</label>
    <div class="layui-input-block">
    <input type="password" name="rePwd" required lay-verify="required|pwd|rePwd" placeholder="请确认新密码" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <div class="layui-input-block">
    <button class="layui-btn" lay-submit lay-filter="formDemo">修改密码</button>
    <button type="reset" class="layui-btn layui-btn-primary" id="btnReset">重置</button>
    </div>
    </div>
    </form>

4.2.3. 发起请求实现重置密码的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 提交密码
$(".layui-form").on("submit",function(e){
e.preventDefault();

$.ajax({
method: "POST",
url: "/my/updatepwd",
data: {
oldPwd: $(".layui-form [name=oldPwd").val(),
newPwd: $(".layui-form [name=newPwd").val()
},
success: function(res){
if(res.status==1){
return layer.msg(res.message);
}
layer.msg(res.message);
// 重置表单
$(".layui-form")[0].reset();
}
})
})

4.3. 更换头像

4.3.1. 初步渲染更换头像页面的结构

  1. 创建 /user/userAvatar.html 页面:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>大事件后台管理系统-更换头像</title>
    <!-- 导入layui.css -->
    <link rel="stylesheet" href="../assets/lib/layui/css/layui.css">
    <!-- 导入自定义css -->
    <link rel="stylesheet" href="../assets/css/user/userAvatar.css">
    </head>
    <body>
    <div class="layui-card">
    <div class="layui-card-header">更换头像</div>
    <div class="layui-card-body">

    </div>
    </div>
    </body>
    </html>
  2. assets/css/user/userAvatar.css 中美化基本样式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    html,
    body {
    margin: 0;
    padding: 0;
    }

    body {
    padding: 15px;
    background-color: #f2f3f5;
    }
  3. 修改 index.html 中对应链接的属性:

    1
    2
    3
    4
    <a href="./user/userAvatar.html" target="fm">
    <i class="layui-icon layui-icon-picture"></i>
    更换头像
    </a>

4.3.2. cropper 基本用法

文档 https://github.com/fengyuanchen/jquery-cropper

官网 https://fengyuanchen.github.io/jquery-cropper/

  1. <head> 中导入 cropper.css 样式表:

    1
    <link rel="stylesheet" href="/assets/lib/cropper/cropper.css" />
  2. <body> 的结束标签之前,按顺序导入如下的 js 脚本:

    1
    2
    3
    <script src="/assets/lib/jquery.js"></script>
    <script src="/assets/lib/cropper/Cropper.js"></script>
    <script src="/assets/lib/cropper/jquery-cropper.js"></script>
  3. 在卡片的 layui-card-body 主体区域中,定义如下的 HTML 结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <!-- 第一行的图片裁剪和预览区域 -->
    <div class="row1">
    <!-- 图片裁剪区域 -->
    <div class="cropper-box">
    <!-- 这个 img 标签很重要,将来会把它初始化为裁剪区域 -->
    <img id="image" src="/assets/images/sample.jpg" />
    </div>
    <!-- 图片的预览区域 -->
    <div class="preview-box">
    <div>
    <!-- 宽高为 100px 的预览区域 -->
    <div class="img-preview w100"></div>
    <p class="size">100 x 100</p>
    </div>
    <div>
    <!-- 宽高为 50px 的预览区域 -->
    <div class="img-preview w50"></div>
    <p class="size">50 x 50</p>
    </div>
    </div>
    </div>

    <!-- 第二行的按钮区域 -->
    <div class="row2">
    <button type="button" class="layui-btn">上传</button>
    <button type="button" class="layui-btn layui-btn-danger">确定</button>
    </div>
  4. 美化的样式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    /* 设置卡片主体区域的宽度 */
    .layui-card {
    width: 50%;
    }

    .row1{
    overflow: hidden;
    }

    /* 设置裁剪区域的样式 */
    .cropper-box {
    float: left;
    width: 350px;
    height: 350px;
    background-color: cyan;
    overflow: hidden;
    }

    /* 设置 preview-box 区域的的样式 */
    .preview-box {
    float: left;
    margin-left: 10%;
    height: 350px;
    }

    /* 设置第一个预览区域的样式 */
    .w100 {
    width: 100px;
    height: 100px;
    background-color: gray;
    }

    /* 设置第二个预览区域的样式 */
    .w50 {
    width: 50px;
    height: 50px;
    background-color: gray;
    margin-top: 50px;
    margin-left: 25%;
    }

    /* 设置预览区域下方文本的样式 */
    .size {
    font-size: 12px;
    color: gray;
    text-align: center;
    }


    /* 设置 img-preview 区域的样式 */
    .img-preview {
    overflow: hidden;
    border-radius: 50%;
    }


    /* 设置按钮行的样式 */
    .row2 {
    margin-top: 20px;
    margin-left: 70%;
    }
  5. 实现基本裁剪效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 1.获取裁剪区域的 DOM 元素
    var img = $("#image");

    // 2.配置选项
    const options = {
    // 纵横比
    aspectRatio: 1,
    // 指定预览区域
    preview: '.img-preview'
    }

    // 3.创建裁剪区域
    img.cropper(options);

4.3.3. 实现裁剪区域图片的替换

  1. userAvatar.html 中添加上传文件的按钮

    1
    2
    3
    4
    5
    6
    <!-- 第二行的按钮区域 -->
    <div class="row2">
    <input type="file" name="" id="file">
    <button type="button" class="layui-btn" id="btnFile">上传</button>
    <button type="button" class="layui-btn layui-btn-danger" id="btnUpload">确定</button>
    </div>
  2. 隐藏按钮。在 userAvatar.css 中添加样式

    1
    2
    3
    #file{
    display: none;
    }
  3. 上传按钮模拟点击文件上传。在 userAvatar.js 中添加

    1
    2
    3
    4
    5
    6
    7
    // 上传按钮
    $("#btnFile").on("click",function(e){
    e.preventDefault();
    // 模拟点击上传文件
    $("#file").click();

    });
  4. 为文件选择框绑定 change 事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$("#file").on("change",function(e){
var filelist = e.target.files;
// console.log(filelist);
if(filelist.length==0){
return layer.msg("请选择照片!");
}
// 1. 拿到用户选择的文件
var file = filelist[0];
// 2. 将文件,转化为路径
var imgURL = URL.createObjectURL(file)
// 3. 重新初始化裁剪区域
img
.cropper('destroy') // 销毁旧的裁剪区域
.attr('src', imgURL) // 重新设置图片路径
.cropper(options); // 重新初始化裁剪区域
});

4.3.4. 将裁剪后的头像上传到服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 确定按钮
$("#btnUpload").on("click",function(e){
e.preventDefault();

// 1. 要拿到用户裁剪之后的头像
var dataURL = img
.cropper('getCroppedCanvas', {
// 创建一个 Canvas 画布
width: 100,
height: 100
})
// 将 Canvas 画布上的内容,转化为 base64 格式的字符串
.toDataURL('image/png');
// console.log(dataURL);

// 2. 发起ajax请求
$.ajax({
method: 'POST',
url: '/my/update/avatar',
data: {
avatar: dataURL
},
success: function(res) {
if (res.status !== 0) {
return layer.msg(res.message);
}
layer.msg(res.message);
// 更新首页用户头像
parent.window.getUserInfo();
}
})
})

4.3.5. base64

https://www.css-js.com/tools/base64.html

DataURI 允许在HTML文档中嵌入小文件,可以使用 img 标签或 CSS 嵌入转换后的 Base64 编码,减少 HTTP 请求,加快小图像的加载时间。

经过Base64 编码后的文件体积一般比源文件大 30% 左右。

4.3.6. 设置头部区域的快捷方式

打开 index.html,在头部 个人中心 中添加id属性如下

1
2
3
4
5
<dl class="layui-nav-child" id="top-user">
<dd><a href="javacript:;">基本资料</a></dd>
<dd><a href="javacript:;">更换头像</a></dd>
<dd><a href="javacript:;">重置密码</a></dd>
</dl>

对应的侧边栏用户管理,添加 id="nav-user"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<li class="layui-nav-item">
<a href="javascript:;">
<span class="iconfont icon-user"></span>个人中心
</a>
<dl class="layui-nav-child" id="nav-user">
<dd>
<a href="./user/userInfo.html" target="fm">
<i class="layui-icon layui-icon-form"></i>
基本资料
</a>
</dd>
<dd>
<a href="./user/userAvatar.html" target="fm">
<i class="layui-icon layui-icon-picture"></i>
更换头像
</a>
</dd>
<dd>
<a href="./user/userPwd.html" target="fm">
<i class="layui-icon layui-icon-key"></i>
重置密码
</a>
</dd>
</dl>
</li>

让头部个人中心点击事件直接触发侧边栏的点击事件

1
2
3
4
5
6
7
8
9
// 顶部个人中心下拉菜单
$("#top-user").on("click","dd",function(e){
e.preventDefault();
// console.log($("#nav-user dd").length);
var dd = $("#nav-user dd").eq($(this).index());
dd.children("a").children("i").click();
dd.parent().parent().addClass("layui-nav-itemed")
.siblings().removeClass("layui-nav-itemed");
});

4.4. 提交user分支的代码到GitHub

  1. 运行 git add . 命令
  2. 运行 git commit -m "完成了用户管理功能的开发" 命令
  3. 运行 git push -u origin user命令
  4. 运行 git checkout master 命令
  5. 运行 git merge user命令
  6. 运行 git push 命令
  7. 运行 git checkout -b article 命令

5. 文章管理

5.1. 文章类别

5.1.1. 创建并显示文章分类

  1. 创建 /article/art_cate.html 页面,并初始化如下的UI结构:

    采用layui表格布局

    https://layuion.com/docs/element/table.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>大事件后台管理系统-文章分类</title>
    <link rel="stylesheet" href="../assets/lib/layui/css/layui.css">
    <!-- 导入自定义样式 -->
    <link rel="stylesheet" href="../assets/css/article/art_cate.css">
    </head>
    <body>
    <div class="layui-card">
    <div class="layui-card-header">
    <span>文章类别管理</span>
    <button type="button" class="layui-btn layui-btn-normal">添加类别</button>
    </div>
    <div class="layui-card-body">
    <table class="layui-table">
    <colgroup>
    <!-- 定义列宽度,不加数字的为自适应 -->
    <col>
    <col>
    <col width="200">
    </colgroup>
    <thead>
    <tr>
    <th>分类名称</th>
    <th>分类别名</th>
    <th>操作</th>
    </tr>
    </thead>
    <tbody>
    <tr>
    <td>贤心</td>
    <td>2016-11-29</td>
    <td>
    <button class="layui-btn">编辑</button>
    <button class="layui-btn layui-btn-danger">删除</button>
    </td>
    </tr>
    </tbody>
    </table>
    </div>
    </div>
    </body>
    </html>
  2. 定义 /assets/css/article/art_cate.css 美化样式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    html,
    body {
    margin: 0;
    padding: 0;
    }

    body {
    padding: 15px;
    background-color: #f2f3f5;
    }

    .layui-card-header span{
    float: left;
    }

    .layui-card-header button{
    float: right;
    margin-top: 10px;
    }
  3. 修改 index.html 中对应的 <a> 链接:

    1
    2
    3
    4
    <a href="./aticle/art_cate.html" target="fm">
    <i class="layui-icon layui-icon-app"></i>
    文章类别
    </a>

5.1.2. 获取并使用模板引擎渲染表格的数据

  1. /article/art_cate.html 页面底部导入模板引擎:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!-- 导入layui.js -->
    <script src="../assets/lib/layui/layui.js"></script>
    <!-- 导入jquery -->
    <script src="../assets/lib/jquery.js"></script>
    <!-- 导入自己封装的 baseAPI -->
    <script src="../assets/js/baseAPI.js"></script>
    <!-- 导入模板引擎 -->
    <script src="../assets/lib/template-web.js"></script>
    <!-- 导入自定义js -->
    <script src="../assets/js/article/art_cate.js"></script>
  2. /article/art_cate.html 页面中定义模板:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- 表格数据的模板 -->
    <script type="text/html" id="cate_table">
    {{each data}}
    <tr>
    <td>{{$value.name}}</td>
    <td>{{$value.alias}}</td>
    <td>
    <button type="button" class="layui-btn layui-btn-xs">编辑</button>
    <button type="button" class="layui-btn layui-btn-danger layui-btn-xs">删除</button>
    </td>
    </tr>
    {{/each}}
    </script>
  3. 发起请求获取数据。在 assets/js/article/art_cate.js 中添加:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    getAricleCate();

    function getAricleCate(){

    $.ajax({
    method: 'GET',
    url: '/my/article/cates',
    success: function(res) {
    // console.log(res);
    var htmlStr = template('cate_table', res)
    $('tbody').html(htmlStr);
    }
    })
    }

5.1.3. 使用layer.open实现弹出层效果

layui的弹出层:https://layuion.com/docs/modules/layer.html

  1. assets/js/article/art_cate.js 中导入 layer

    1
    var layer = layui.layer
  2. /article/art_cate.html 页面中为按钮添加 id 属性:

    1
    <button type="button" class="layui-btn layui-btn-normal layui-btn-sm" id="btnAddCate">添加类别</button>
  3. 在按钮的点击事件中,通过 layer.open() 展示弹出层:

    1
    2
    3
    4
    5
    6
    7
    // 为添加类别按钮绑定点击事件
    $('#btnAddCate').on('click', function() {
    layer.open({
    title: '在线调试'
    ,content: '配置各种参数,试试效果'
    });
    })

5.1.4. 在弹出层中渲染form表单结构

  1. 在页面中定义如下的 script 标签:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <!-- 添加类别表单 -->
    <script type="text/html" id="dialog-add">
    <form class="layui-form" id="form-add">
    <div class="layui-form-item">
    <label class="layui-form-label">分类名称</label>
    <div class="layui-input-block">
    <input type="text" name="name" required lay-verify="required" placeholder="请输入分类名称" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <label class="layui-form-label">分类别名</label>
    <div class="layui-input-block">
    <input type="text" name="alias" required lay-verify="required|alias" placeholder="请输入分类别名" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <div class="layui-input-block">
    <button class="layui-btn" lay-submit lay-filter="formDemo">确认添加</button>
    <button type="reset" class="layui-btn layui-btn-primary">重置</button>
    </div>
    </div>
    </form>
    </script>
  2. 通过 content 属性指定内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 添加类别按钮
    $("#btnAddCates").on("click",function(e){
    e.preventDefault();

    layer.open({
    // 0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层)
    type: 1,
    // area - 宽高
    area:['500px','250px'],
    title: '添加文章分类',
    // content不仅可以传入普通的html内容,还可以指定DOM
    content: $("#dialog-add").html()
    });
    })
  3. 为别名定义校验规则:

    1
    2
    3
    4
    5
    // 文章分类规则
    var form = layui.form;
    form.verify({
    alias: [/^[a-zA-Z0-9]$/,"别名必须是字母或数字!"]
    });

5.1.5. 实现添加文章分类的功能

  1. 预先定义弹出层的索引

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 定义弹出层的索引
    var indexAdd = null;
    // 添加类别按钮
    $("#btnAddCates").on("click",function(e){
    e.preventDefault();

    indexAdd = layer.open({
    // 0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层)
    type: 1,
    // area - 宽高
    area:['500px','250px'],
    title: '添加文章分类',
    // content不仅可以传入普通的html内容,还可以指定DOM
    content: $("#dialog-add").html()
    });
    })
  2. 发起ajax请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 通过代理的形式,为 form-add 表单绑定 submit 事件
    $("body").on("submit","#form-add",function(e){
    e.preventDefault();

    $.ajax({
    method: 'POST',
    url: '/my/article/addcates',
    data: {
    name: $("#form-add [name=name]").val(),
    alias: $("#form-add [name=alias]").val()
    },
    success: function(res) {
    if (res.status !== 0) {
    return layer.msg(res.message);
    }
    // 刷新表格
    getAricleCate();
    layer.msg(res.message);
    // 根据索引,关闭对应的弹出层
    layer.close(indexAdd);
    }
    });
    });

5.1.6. 点击编辑按钮展示修改文章分类的弹出层

  1. 为编辑按钮添加 btn-edit 类名如下:

    1
    <button type="button" class="layui-btn layui-btn-xs btn-edit">编辑</button>
  2. 定义 修改分类 的弹出层:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <!-- 修改表单 -->
    <script type="text/html" id="dialog-edit">
    <form class="layui-form" id="form-edit" lay-filter="form-edit">
    <!-- 隐藏域,保存 id 的值 -->
    <input type="hidden" name="id">
    <div class="layui-form-item">
    <label class="layui-form-label">分类名称</label>
    <div class="layui-input-block">
    <input type="text" name="name" required lay-verify="required" placeholder="请输入分类名称" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <label class="layui-form-label">分类别名</label>
    <div class="layui-input-block">
    <input type="text" name="alias" required lay-verify="required|alias" placeholder="请输入分类别名" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <div class="layui-input-block">
    <button class="layui-btn" lay-submit lay-filter="formDemo">确认修改</button>
    </div>
    </div>
    </form>
    </script>
  3. 通过 代理 的形式,为 btn-edit 按钮绑定点击事件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 修改弹出层
    // 定义弹出层的索引
    var indexEdit= null;
    $("tbody").on("click",".btn-edit",function(e){
    e.preventDefault();

    indexEdit = layer.open({
    // 0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层)
    type: 1,
    // area - 宽高
    area:['500px','250px'],
    title: '修改文章分类',
    // content不仅可以传入普通的html内容,还可以指定DOM
    content: $("#dialog-edit").html()
    });
    })

5.1.7. 为修改文章分类的弹出层填充表单数据

  1. 为编辑按钮绑定 data-id 自定义属性:

    1
    <button type="button" class="layui-btn layui-btn-xs btn-edit" data-id="{{$value.id}}">编辑</button>
  2. 在展示弹出层之后,根据 id 的值发起请求获取文章分类的数据,并填充到表单中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 修改弹出层
    // 定义弹出层的索引
    var indexEdit= null;
    $("tbody").on("click",".btn-edit",function(e){
    e.preventDefault();

    indexEdit = layer.open({
    // 0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层)
    type: 1,
    // area - 宽高
    area:['500px','250px'],
    title: '修改文章分类',
    // content不仅可以传入普通的html内容,还可以指定DOM
    content: $("#dialog-edit").html()
    });

    // 填充表单
    const id = $(this).data("id");
    // console.log(id);
    $.ajax({
    method: 'GET',
    url: '/my/article/cates/' + id,
    success: function(res) {
    // console.log(res);
    // 与form的lay-filter相联系
    form.val('form-edit', res.data);
    }
    })
    });

5.1.8. 更新文章分类的数据

通过代理的形式,为修改分类的表单绑定 submit 事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 通过代理的形式,为 form-edit 表单绑定 submit 事件
$("body").on("submit","#form-edit",function(e){
e.preventDefault();

$.ajax({
method: 'POST',
url: '/my/article/updatecate',
data: {
id: $("#form-edit [name=id]").val(),
name: $("#form-edit [name=name]").val(),
alias: $("#form-edit [name=alias]").val()
},
success: function(res) {
if (res.status !== 0) {
return layer.msg(res.message);
}
// 刷新表格
getAricleCate();
layer.msg(res.message);
// 根据索引,关闭对应的弹出层
layer.close(indexEdit);
}
});
});

5.1.9. 删除文章分类

  1. 为删除按钮绑定 btn-delete 类名,并添加 data-id 自定义属性:

    1
    <button type="button" class="layui-btn layui-btn-danger layui-btn-xs btn-delete" data-id="{{$value.id}}">删除</button>
  2. 通过代理的形式,为删除按钮绑定点击事件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 删除按钮
    $("tbody").on("click",".btn-delete",function(e){
    e.preventDefault();
    // 填充表单
    const id = $(this).data("id");

    layer.confirm('确认删除?', { icon: 3, title: '提示' },
    function(index) {
    $.ajax({
    method: 'GET',
    url: '/my/article/deletecate/'+id,
    success: function(res) {
    if (res.status !== 0) {
    return layer.msg(res.message);
    }
    // 刷新表格
    getAricleCate();
    layer.msg(res.message);
    // 根据索引,关闭对应的弹出层
    layer.close(index);
    }
    });
    });
    });

5.2. 文章列表

5.2.1. 创建文章列表页面

  1. 新建 /article/art_list.html 页面结构如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>大事件后台管理系统-文章列表</title>
    <!-- layui样式 -->
    <link rel="stylesheet" href="../assets/lib/layui/css/layui.css">
    <!-- 导入自定义样式 -->
    <link rel="stylesheet" href="../assets/css/article/art_list.css">
    </head>
    <body>
    <div class="layui-card">
    <div class="layui-card-header">
    文章列表
    </div>
    <div class="layui-card-body">
    <!-- 筛选区域 -->
    <!-- 列表区域 -->
    <table class="layui-table">
    <colgroup>
    <!-- 定义列宽度,不加数字的为自适应 -->
    <col>
    <col width="150" />
    <col width="180" />
    <col width="150" />
    <col width="150" />
    </colgroup>
    <thead>
    <tr>
    <th>文章标题</th>
    <th>分类</th>
    <th>发表时间</th>
    <th>状态</th>
    <th>操作</th>
    </tr>
    </thead>
    <tbody>

    </tbody>
    </table>

    <!-- 分页区域 -->
    </div>
    </div>

    <!-- 导入layui.js -->
    <script src="../assets/lib/layui/layui.js"></script>
    <!-- 导入jquery -->
    <script src="../assets/lib/jquery.js"></script>
    <!-- 导入自己封装的 baseAPI -->
    <script src="../assets/js/baseAPI.js"></script>
    <!-- 导入模板引擎 -->
    <script src="../assets/lib/template-web.js"></script>
    <!-- 导入自定义js -->
    <script src="../assets/js/article/art_list.js"></script>
    </body>
    </html>
  2. 新建 /assets/css/article/art_list.css 样式表如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    html,
    body {
    margin: 0;
    padding: 0;
    }

    body {
    padding: 15px;
    background-color: #f2f3f5;
    }
  3. 新建 /assets/js/article/art_list.js 脚本文件

  4. 修改 index.html 中对应的 <a> 链接:

    1
    2
    3
    4
    <a href="./aticle/art_list.html" target="fm">
    <i class="layui-icon layui-icon-list"></i>
    文章列表
    </a>

5.2.2. 定义查询参数对象

/assets/js/article/art_list.js 定义一个查询的参数对象如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 定义一个查询的参数对象,将来请求数据的时候,
// 需要将请求参数对象提交到服务器
var obj = {
// 页码值,默认请求第一页的数据
pagenum: 1,
// 每页显示几条数据,默认每页显示2条
pagesize: 2
// // 文章分类的 Id
// ,cate_id: '',
// // 文章的发布状态
// state: ''
};

5.2.3. 请求文章列表数据并使用模板引擎渲染列表结构

  1. /assets/js/article/art_list.js 中定义获取文章列表数据的方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
initTable();
var layer = layui.layer;
function initTable(){
$.ajax({
method: 'GET',
url: '/my/article/list',
data: obj,
success: function(res) {
if (res.status !== 0) {
return layer.msg(res.message);
}
if(res.data.length==0){
layer.msg("暂无数据");
}
// 使用模板引擎渲染页面的数据
var htmlStr = template('article-table', res);
$('tbody').html(htmlStr);
}
})
}
  1. /article/art_list.html 定义列表数据的模板结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script type="text/html" id="article-table">
{{each data}}
<tr>
<td>{{$value.title}}</td>
<td>{{$value.cate_name}}</td>
<td>{{$value.pub_date}}</td>
<td>{{$value.state}}</td>
<td>
<button type="button" class="layui-btn layui-btn-xs">编辑</button>
<button type="button" class="layui-btn layui-btn-danger layui-btn-xs">删除</button>
</td>
</tr>
{{/each}}
</script>

5.2.4. 绘制筛选区域的UI结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 筛选区域 -->
<form class="layui-form" id="form-search">
<div class="layui-form-item layui-inline">
<select name="cate_id"></select>
</div>
<div class="layui-form-item layui-inline">
<select name="state">
<option value="">所有状态</option>
<option value="已发布">已发布</option>
<option value="草稿">草稿</option>
</select>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn" lay-submit lay-filter="formDemo">筛选</button>
</div>
</form>

5.2.5. 发起请求获取并渲染文章分类的下拉选择框

  1. 定义 initCate 函数请求文章分类的列表数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    var form = layui.form;
    initCate();
    // 筛选区域 - 所有文章分类
    function initCate(){
    $.ajax({
    method: 'GET',
    url: '/my/article/cates',
    success: function(res) {
    if (res.status !== 0) {
    return layer.msg(res.message);
    }
    // console.log(res);
    // 使用模板引擎渲染页面的数据
    var htmlStr = template('article-cate', res);
    $('.layui-form [name=cate_id]').html(htmlStr);
    // 通过 layui 重新渲染表单区域的UI结构
    form.render();
    }
    });

    }
  2. 定义分类可选项的模板结构:

    1
    2
    3
    4
    5
    6
    7
    <!-- 文章分类模板 -->
    <script type="text/html" id="article-cate">
    <option value="">所有分类</option>
    {{each data}}
    <option value="{{$value.id}}">{{$value.name}}</option>
    {{/each}}
    </script>

5.2.6. 实现筛选的功能

为筛选表单绑定 submit 事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 筛选表单提交事件
$(".layui-form").on("submit",function(e){
e.preventDefault();
// 获取表单中选中项的值
const id = $(".layui-form [name=cate_id]").val();
const state = $(".layui-form [name=state]").val();
// console.log(id,state);

// 为查询参数对象 q 中对应的属性赋值
if(id.length!=0){
obj.cate_id = Number(id);
}else{
delete obj.cate_id;
}
if(state.length!=0){
obj.state = state;
}else{
delete obj.state;
}
// console.log(obj);

// 根据最新的筛选条件,重新渲染表格的数据
initTable();
});

5.2.7. 定义渲染分页的 renderPage 方法

  1. 定义渲染分页的方法:

    1
    2
    3
    function renderPage(total) {
    console.log(total)
    }
  2. initTable 中调用 renderPage 方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function initTable(){
    $.ajax({
    method: 'GET',
    url: '/my/article/list',
    data: obj,
    success: function(res) {
    if (res.status !== 0) {
    return layer.msg(res.message);
    }
    // 使用模板引擎渲染页面的数据
    var htmlStr = template('article-table', res);
    $('tbody').html(htmlStr);

    // 调用渲染分页的方法
    renderPage(res.total);
    }
    })
    }

5.2.8. 调用 laypage.render 方法渲染分页的基本结构

https://layuion.com/docs/modules/laypage.html

  1. 在页面中定义分页的区域:

    1
    2
    <!-- 分页区域 -->
    <div id="pageBox"></div>
  2. 调用 laypage.render() 方法来渲染分页的结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 分页函数
    function renderPage(total) {
    // console.log(total);
    var laypage = layui.laypage;

    //执行一个laypage实例
    laypage.render({
    //注意,这里的 test1 是 ID,不用加 # 号
    elem: 'pageBox',
    //数据总数,从服务端得到
    count: total,
    // 每页显示几条数据
    limit: obj.pagesize,
    // 设置默认被选中的分页
    curr: obj.pagenum
    });
    }

5.2.9. 在jump回调函数中通过obj.curr获取到最新的页码值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 分页函数
function renderPage(total) {
// console.log(total);
var laypage = layui.laypage;

//执行一个laypage实例
laypage.render({
//注意,这里的 test1 是 ID,不用加 # 号
elem: 'pageBox',
//数据总数,从服务端得到
count: total,
// 每页显示几条数据
limit: obj.pagesize,
// 设置默认被选中的分页
curr: obj.pagenum,
// 分页发生切换的时候,触发 jump 回调
// 触发 jump 回调的方式有两种:
// 1. 点击页码的时候,会触发 jump 回调
// 2. 只要调用了 laypage.render() 方法,就会触发 jump 回调
jump: function(ob,first) {
// console.log(obj.curr);
// 可以通过 first 的值,来判断是通过哪种方式,触发的 jump 回调
// 如果 first 的值为 true,证明是方式2触发的
// 否则就是方式1触发的
// console.log(first);
// 把最新的页码值,赋值到 obj 这个查询参数对象中
obj.pagenum = ob.curr;
// console.log(obj);
if(!first){
initTable();
}
}
});
}

5.2.10. 自定义分页的功能项

添加 layoutlimits 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 分页函数
function renderPage(total) {
// console.log(total);
var laypage = layui.laypage;

//执行一个laypage实例
laypage.render({
//注意,这里的 test1 是 ID,不用加 # 号
elem: 'pageBox',
//数据总数,从服务端得到
count: total,
// 每页显示几条数据
limit: obj.pagesize,
// 设置默认被选中的分页
curr: obj.pagenum,
/**
* 自定义排版。可选值有:
* count(总条目输区域)、
* prev(上一页区域)、
* page(分页区域)、
* next(下一页区域)、
* limit(条目选项区域)、
* refresh(页面刷新区域。注意:layui 2.3.0 新增) 、
* skip(快捷跳页区域)
*/
layout: ['count', 'limit', 'prev', 'page', 'next', 'skip'],
// 每页条数的选择项。如果 layout 参数开启了 limit,则会出现每页条数的select选择框
limits: [3,5,10],
// 分页发生切换的时候,触发 jump 回调
// 触发 jump 回调的方式有两种:
// 1. 点击页码的时候,会触发 jump 回调
// 2. 只要调用了 laypage.render() 方法,就会触发 jump 回调
jump: function(ob,first) {
// console.log(obj.curr);
// 可以通过 first 的值,来判断是通过哪种方式,触发的 jump 回调
// 如果 first 的值为 true,证明是方式2触发的
// 否则就是方式1触发的
// console.log(first);
// 把最新的页码值,赋值到 obj 这个查询参数对象中
obj.pagenum = ob.curr;
// console.log(obj);
if(!first){
initTable();
}
}
});
}

5.2.11. 删除文章

  1. 为删除按钮绑定 btn-delete 类名和 data-id 自定义属性:

    1
    <button type="button" class="layui-btn layui-btn-danger layui-btn-xs btn-delete" data-id={{$value.id}}>删除</button>
  2. 通过代理的形式,为删除按钮绑定点击事件处理函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    // 删除按钮
    $("tbody").on("click",".btn-delete",function(e){
    e.preventDefault();

    // 获取删除按钮的个数
    var len = $('.btn-delete').length;

    const id = $(this).data("id");

    layer.confirm('确认删除?', { icon: 3, title: '提示' },
    function(index) {
    $.ajax({
    method: 'GET',
    url: '/my/article/delete/'+id,
    success: function(res) {
    if (res.status !== 0) {
    return layer.msg(res.message);
    }
    layer.msg(res.message);
    // 当数据删除完成后,需要判断当前这一页中,是否还有剩余的数据
    // 如果没有剩余的数据了,则让页码值 -1 之后,
    // 再重新调用 initTable 方法
    // 4
    if (len == 1) {
    // 如果 len 的值等于1,证明删除完毕之后,页面上就没有任何数据了
    // 页码值最小必须是 1
    obj.pagenum = obj.pagenum === 1 ? 1 : obj.pagenum - 1
    }
    initTable();
    }
    });
    layer.close(index);
    });

    });

5.3. 发布文章

5.3.1. 创建文章发布页面的基本结构

  1. 新建 /article/art_pub.html 页面结构如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>大事件后台管理系统-发布文章</title>
    <!-- layui样式 -->
    <link rel="stylesheet" href="../assets/lib/layui/css/layui.css">
    <!-- 导入自定义样式 -->
    <link rel="stylesheet" href="../assets/css/article/art_pub.css">
    </head>
    <body>
    <div class="layui-card">
    <div class="layui-card-header">
    发布文章
    </div>
    <div class="layui-card-body">
    <form class="layui-form" id="form-edit" lay-filter="form-edit">
    <div class="layui-form-item">
    <label class="layui-form-label">文章标题</label>
    <div class="layui-input-block">
    <input type="text" name="title" required lay-verify="required" placeholder="请输入文章标题" autocomplete="off" class="layui-input">
    </div>
    </div>
    <div class="layui-form-item">
    <label class="layui-form-label">文章类别</label>
    <div class="layui-input-block" style="width:15%;">
    <select name="cate_id" lay-verify="required"></select>
    </div>
    </div>
    <div class="layui-form-item">
    <label class="layui-form-label">文章内容</label>
    <!-- 为富文本编辑器外部的容器设置高度 -->
    <div class="layui-input-block" style="height: 400px;">
    <!-- 重要:将来这个 textarea 会被初始化为富文本编辑器 -->
    <textarea name="content" id="articleContent"></textarea>
    </div>
    </div>
    <div class="layui-form-item">
    <div class="layui-input-block">
    <button class="layui-btn" lay-submit>发布</button>
    <button class="layui-btn layui-btn-primary" lay-submit>存为草稿</button>
    </div>
    </div>
    </form>
    </div>
    </div>
    <!-- 导入layui.js -->
    <script src="../assets/lib/layui/layui.js"></script>
    <!-- 导入jquery -->
    <script src="../assets/lib/jquery.js"></script>
    <!-- 导入自己封装的 baseAPI -->
    <script src="../assets/js/baseAPI.js"></script>
    <!-- 导入模板引擎 -->
    <script src="../assets/lib/template-web.js"></script>
    <!-- 导入自定义js -->
    <script src="../assets/js/article/art_pud.js"></script>
    </body>
    </html>
  2. 新建 /assets/css/article/art_pub.css 样式文件如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    html,
    body{
    margin: 0;
    padding: 0;
    }

    body{
    margin: 0 15px;
    background-color: #f2f3f5;
    }
  3. 新建 /assets/js/article/art_pub.js 脚本文件

  4. 修改 index.html 中对应的 <a> 链接:

    1
    2
    3
    4
    <a href="./aticle/art_pub.html" target="fm">
    <i class="layui-icon layui-icon-edit"></i>
    发布文章
    </a>

5.3.2. 渲染文章类别对应的下拉选择框结构

  1. /article/art_pub.html中 定义模板结构

    1
    2
    3
    4
    5
    6
    7
    <!-- 下拉选框 -->
    <script type="text/html" id="article_cates">
    <option value=""></option>
    {{each data}}
    <option value={{$value.id}}>{{$value.name}}</option>
    {{/each}}
    </script>
  2. /assets/js/article/art_pub.js 中定义 initCate 方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    initCate();
    var layer = layui.layer;
    var form = layui.form;

    // 下拉文章分类列表
    function initCate(){
    $.ajax({
    method: "GET",
    url: "/my/article/cates",
    success: function(res){
    const htmlStr = template("article_cates",res);
    $("[name=cate_id]").html(htmlStr);
    form.render();
    }
    })
    }

5.3.3. 渲染富文本编辑器

TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器 http://tinymce.ax-z.cn/

  1. 导入富文本必须的 script 脚本:

    1
    2
    3
    <!-- 富文本 -->
    <script src="../assets/lib/tinymce/tinymce.min.js"></script>
    <script src="../assets/lib/tinymce/tinymce_setup.js"></script>
  2. 让TinyMCE关联页面的 textarea

    1
    2
    3
    4
    5
    6
    7
    function initEditor() {
    tinymce.init({
    //选择id为articleContent的标签作为编辑器
    selector: '#articleContent',
    // TODO: 其他事务
    })
    }
  3. /assets/js/article/art_pub.js 中调用 initEditor() 方法,初始化富文本编辑器:

    1
    initEditor();

5.3.4. 渲染封面裁剪区域

  1. <head> 中导入 cropper.css 样式表:

    1
    2
    <!-- 导入裁剪样式 -->
    <link rel="stylesheet" href="../assets/lib/cropper/cropper.css">
  2. <body> 的结束标签之前,按顺序导入如下的 js 脚本

    1
    2
    3
    <!-- 导入裁剪js -->
    <script src="../assets/lib/cropper/Cropper.js"></script>
    <script src="../assets/lib/cropper/jquery-cropper.js"></script>
  3. 在表单中,添加如下的表单行结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <div class="layui-form-item">
    <label class="layui-form-label">文章封面</label>
    <div class="layui-input-block" id="img-box">
    <!-- 第一行的图片裁剪和预览区域 -->
    <div class="row1">
    <!-- 图片裁剪区域 -->
    <div class="cropper-box">
    <!-- 这个 img 标签很重要,将来会把它初始化为裁剪区域 -->
    <img id="image" src="../assets/images/sample.jpg" />
    </div>
    <!-- 图片的预览区域 -->
    <div class="preview-box">
    <div>
    <!-- 宽高为 200px 的预览区域 -->
    <div class="img-preview w200"></div>
    <p class="size">200 x 100</p>
    </div>
    </div>
    </div>

    <!-- 第二行的按钮区域 -->
    <div class="row2">
    <!-- 选择封面按钮 -->
    <button type="button" class="layui-btn layui-btn-danger" id="btnFile">选择封面</button>
    </div>
    </div>
    </div>
  4. 美化的样式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    /* 设置裁剪区域的样式 */
    .cropper-box {
    float: left;
    width: 600px;
    height: 300px;
    background-color: cyan;
    overflow: hidden;
    }

    /* 设置 preview-box 区域的的样式 */
    .preview-box {
    float: right;
    }

    /* 设置第一个预览区域的样式 */
    .w200 {
    width: 200px;
    height: 100px;
    background-color: gray;
    }

    /* 设置预览区域下方文本的样式 */
    .size {
    font-size: 12px;
    color: gray;
    text-align: center;
    }


    /* 设置 img-preview 区域的样式 */
    .img-preview {
    overflow: hidden;
    }


    /* 设置按钮行的样式 */
    .row2 {
    position: absolute;
    bottom: 0;
    right: 0;
    }

    /* 上传文件按钮 */
    #file{
    display: none;
    }

    /* 裁剪图片div */
    #img-box{
    width: 70%;
    position: relative;
    margin-bottom: 20px;
    }
  5. /assets/js/article/art_pub.js 中实现基本裁剪效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 1. 初始化图片裁剪器
    var img = $("#image");
    // 2. 裁剪选项
    var options = {
    aspectRatio: 600 / 300,
    preview: '.img-preview'
    }
    // 3. 初始化裁剪区域
    img.cropper(options);

5.3.5. 点击选择封面按钮打开文件选择框

  1. 修改 UI 结构,为 选择封面 按钮添加 id,并且在按钮后面添加 文件选择框

    1
    2
    3
    4
    5
    6
    7
    <!-- 第二行的按钮区域 -->
    <div class="row2">
    <!-- 隐藏的文件选择框 -->
    <input type="file" name="" id="file" accept="image/png,image/jpeg,image/gif">
    <!-- 选择封面按钮 -->
    <button type="button" class="layui-btn layui-btn-danger" id="btnFile">选择封面</button>
    </div>
  2. 为选择封面的按钮,绑定点击事件处理函数:

    1
    2
    3
    4
    5
    $("#btnFile").on("click",function(e){
    e.preventDefault();

    $("#file").click();
    })

5.3.6. 将选择的图片设置到裁剪区域中

监听 filechange 事件,获取用户选择的文件列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 更换裁剪的图片
$("#file").on("change",function(e){
var fileList = e.target.files;
// console.log(fileList);
if(fileList.length==0){
return layer.msg("请上传文章封面!");
}
// 拿到用户选择的文件
var file = fileList[0];
// 根据选择的文件,创建一个对应的 URL 地址
var newImgURL = URL.createObjectURL(file);
img
// 销毁旧的裁剪区域
.cropper('destroy')
// 重新设置图片路径
.attr('src', newImgURL)
// 重新初始化裁剪区域
.cropper(options);

});

5.3.7. 分析发布文章的实现步骤

  1. 存为草稿发布 按钮添加 id 属性:

    1
    2
    <button class="layui-btn btn-pub" lay-submit>发布</button>
    <button class="layui-btn layui-btn-primary btn-draft" lay-submit>存为草稿</button>
  2. 确定文章的发布状态:

    1
    2
    3
    4
    5
    6
    7
    8
    // 文章状态保存
    var articleState;
    $(".btn-pub").on("click",function(e){
    articleState = "已发布";
    })
    $(".btn-draft").on("click",function(e){
    articleState = "草稿";
    })

5.3.8. 基于Form表单创建FormData对象

当文章内容无输入时,应该有判断

1
2
3
4
5
6
7
// 得到富文本内容
var content = tinyMCE.editors["articleContent"].getContent();
// console.log(content);
// 富文本中没有内容
if(content.length==0){
return layer.msg('文章内容不能为空', {icon: 5});
}

富文本中的数据同步到textarea

http://tinymce.ax-z.cn/advanced/some-example.php

1
tinyMCE.editors["articleContent"].save();

为表单绑定 submit 提交事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 发布文章form
$(".layui-form").on("submit",function(e){
// 1. 阻止表单的默认提交行为
e.preventDefault();

// 得到富文本内容
var content = tinyMCE.editors["articleContent"].getContent();
// console.log(content);
// 富文本中没有内容
if(content.length==0){
return layer.msg('文章内容不能为空', {icon: 5});
}
// console.log(articleState);
// 同步内容到textarea
tinyMCE.editors["articleContent"].save();
// 2. 基于 form 表单,快速创建一个 FormData 对象
var obj = new FormData($(this)[0]);
// 3. 将文章的发布状态,存到 obj 中
obj.append('state', articleState);
// console.log(obj);
// 4. 将封面裁剪过后的图片,输出为一个文件对象
// 先判断图片是否上传过
if(img.attr("src") == "../assets/images/sample.jpg"){
return layer.msg('请上传文章封面!', {icon: 5});
}
// 再输出为文件对象
img.cropper('getCroppedCanvas', {
// 创建一个 Canvas 画布
width: 600,
height: 300
})
.toBlob(function(blob) {
// 将 Canvas 画布上的内容,转化为文件对象
// 得到文件对象后,进行后续的操作
// console.log(blob);
// 5. 将文件对象,存储到 obj 中
obj.append('cover_img', blob);
// 6. 发起 ajax 数据请求
publishArticle(obj);
});

})

5.3.9. 发起Ajax请求实现发布文章的功能

  1. 定义一个发布文章的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    function publishArticle(obj) {
    $.ajax({
    method: 'POST',
    url: '/my/article/add',
    data: obj,
    // 注意:如果向服务器提交的是 FormData 格式的数据,
    // 必须添加以下两个配置项
    contentType: false,
    processData: false,
    success: function(res) {
    if (res.status == 1) {
    return layer.msg(res.message);
    }
    layer.msg(res.message);
    // 发布文章成功后,跳转到文章列表页面
    // 跳转至文章列表页
    var url = location.href;
    url = url.substring(0,url.lastIndexOf("/"));
    // console.log(url);
    location.href=url+'/art_list.html';
    // 点击左边框的文章列表按钮
    $("#artList",window.parent.document).click();
    }
    })
    }
  2. 其中的 $("#artList") 是侧边栏的文章列表的id

    index.html 中为文章列表添加 id

    1
    2
    3
    4
    <a href="./aticle/art_list.html" target="fm">
    <i class="layui-icon layui-icon-list" id="artList"></i>
    文章列表
    </a>

5.3.10. 编辑文章

  1. 定义 getArticle 函数,获取文章 art_pub.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 根据id获取文章
    function getArticle(id){
    $.ajax({
    method: "get",
    url: "/my/article/"+id,
    success: function(res){
    if(res.status==1){
    return layer.msg(res.message);
    }
    form.val("form-edit",res.data);
    img
    // 销毁旧的裁剪区域
    .cropper('destroy')
    // 重新设置图片路径
    .attr('src', "http://127.0.0.1:3007"+res.data.cover_img)
    // 重新初始化裁剪区域
    .cropper(options);
    }
    })
    }
  2. 通过路由判断页面采用发布还是更新 art_pub.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    addOrUpdate(location.href);

    // 判断为发布文章还是更新文章
    var flag;
    var id;
    // 当链接中带有id时说明是更新
    function addOrUpdate(url){
    // 路由中存在 ?id=1
    var index = url.indexOf("?id=");
    if(index!=-1){
    // 更新
    flag = false;
    // console.log(url.substring(index+4));
    // 获取路由中的id值
    id = Number(url.substring(index+4));
    getArticle(id);
    // console.log($("[name=content]").val());
    }
    else{
    // 发布
    flag = true;
    }
    }
  3. 修改表单的ajax请求,融合发布和更新 art_pub.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // 发布文章函数
    function publishArticle(obj,id) {
    var url = 'add';
    if(!flag){
    obj.append("id",id);
    url = 'edit';
    }
    $.ajax({
    method: 'POST',
    url: '/my/article/'+url,
    data: obj,
    // 注意:如果向服务器提交的是 FormData 格式的数据,
    // 必须添加以下两个配置项
    contentType: false,
    processData: false,
    success: function(res) {
    if (res.status == 1) {
    return layer.msg(res.message);
    }
    layer.msg(res.message);
    // 发布文章成功后,跳转到文章列表页面
    // 跳转至文章列表页
    var url = location.href;
    url = url.substring(0,url.lastIndexOf("/"));
    // console.log(url);
    location.href=url+'/art_list.html';
    // 点击左边框的文章列表按钮
    $("#artList",window.parent.document).click();
    }
    })
    }
  4. 修改art_list.html 中的编辑按钮

    1
    <button type="button" class="layui-btn layui-btn-xs btn-edit" data-id={{$value.id}}>编辑</button>
  5. 添加编辑按钮点击事件 art_list.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 编辑按钮
    $("tbody").on("click",".btn-edit",function(e){
    e.preventDefault();

    // 跳转至文章编辑页
    var url = location.href;
    url = url.substring(0,url.lastIndexOf("/"));
    // console.log(url);
    location.href=url+'/art_pub.html?id='+$(this).data("id");
    // 点击左边框的发布文章按钮
    $("#artPub",window.parent.document).parent().parent().addClass("layui-this");
    $("#artPub",window.parent.document).parent().parent().siblings().removeClass("layui-this");
    });
  6. 最后两行对应 index.html 中的 发布文章文章列表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <dd>                     
    <a href="./aticle/art_list.html" target="fm" >
    <i class="layui-icon layui-icon-list" id="artList"></i>
    文章列表
    </a>
    </dd>
    <dd >
    <a href="./aticle/art_pub.html" target="fm">
    <i class="layui-icon layui-icon-edit" id="artPub"></i>
    发布文章
    </a>
    </dd>

5.3.11. 将开发完成的项目代码推送到GitHub

  1. 运行 git add . 命令
  2. 运行 git commit -m "完成了文章功能的开发" 命令
  3. 运行 `git push -u origin article命令
  4. 运行 git checkout master 命令
  5. 运行 git merge article命令
  6. 运行 git push 命令
本文结束  感谢您的阅读