# 演示地址和接口文档
案例接口文档:https://docs.apipost.cn/view/8675137ccc2b3ac0#3361314
线上演示地址:http://www.itcbc.com:8888/login.html
# 安装vscode的live server插件
# 安装

# 作用
- 模拟真实环境(服务器环境)打开页面。
- 修改页面后,浏览器会自动刷新。
# 使用
安装后,编辑器打开html文件,点击右键菜单的 “Open with Live Server”,就会在浏览器预览页面了。
使用 Live Server 插件打开HTML之后,浏览器地址栏一般都会显示 127.0.0.1:5500/xxx.html
# 必须使用live server的硬性条件
大事件中,有一个图片剪裁插件,必须使用live server的方式打开页面
项目使用了iframe标签,并且涉及到调用父页面的函数,必须使用live server的方式打开页面
# 易错点和注意事项
该插件的快捷键不是 Alt + B,不要按照之前的思维打开你的页面,需要慢慢适应。
如果你的live server不能用,安装 preview on web server 插件也可以,安装之后,编辑器打开html文件,点击右键菜单的 “vscode-preview-server:Launch on browser”,就会在浏览器预览页面了。
# 搭建项目目录
- 创建
bigevent文件夹,它就是我们的项目文件夹 - 把
资料里面的assets和home复制到bigevent里面 - vscode打开 bigevent 文件夹
|- home
|- dashboard.html --- 后台首页图表页面
|- assets
|- js --- 空文件夹,里面准备存放自己写的js文件
|- css --- 空文件夹,里面准备存放自己写的css文件
|- images --- 存放的页面布局所需的图片
|- lib --- 存放第三方工具
|- jquery.js
|- template-web.js
|- layui
|- tinymce --- 富文本编辑器插件,添加文章时使用
|- cropper --- 图片剪裁插件(更换头像、添加文章使用)
# 使用Git管理项目
- 初始化
git init - 添加基础的代码 到 暂存区
git add . - 提交代码到本地仓库
git commit -m '提交了基础的代码' - 创建远程仓库(自愿创建码云或github仓库)
- 添加远程仓库地址
git remote add 别名 地址 - 首次推送
git push -u 别名 master
具体操作:
# 项目即推送到码云、也推送到github
# 初始化
git init
# 添加初始文件到暂存区(windows可能看的一堆警告,没有问题,正常)
git add .
# 提交文件到本地仓库
git commit -m '提交了初始的文件'
# 创建远程仓库
# 添加两个远程仓库的 ssh地址
git remote add o1 git@gitee.com:laotang1234/bigevent-123.git
git remote add o2 git@github.com:Laotang1234/bigevent-123.git
# 推送到码云
git push -u o1 master
# 推送到github
git push -u o2 master
后续,新增了什么功能,及时的让Git记录。
# 创建项目通用的JS文件
项目的Ajax请求根路径 为 http://www.itcbc.com:8080 。所以,可以创建 assets/js/common.js 。在该js文件中 使用jQuery提供的 $.ajaxPrefilter() 方法统一配置大事件项目的接口根路径、headers和complete。
/assets/js/common.js
// 全局变量 baseUrl,以便后续多次使用
let baseUrl = 'http://www.itcbc.com:8080';
$.ajaxPrefilter(function (option) {
// option 就是ajax选项;我们可以修改它,也可以增加一些选项
// 1. 统一配置url
option.url = baseUrl + option.url;
// 2. 统一配置请求头
option.headers = {
Authorization: localStorage.getItem('token')
};
// 3. 请求完成后,如果接口返回“身份认证失败”,则需要跳转到登录页面
option.complete = function (xhr) {
var res = xhr.responseJSON;
if (res && res.status === 1 && res.message === '身份认证失败!') {
localStorage.removeItem('token');
location.href = './login.html';
}
}
});
配置好之后,各个页面,在调用接口之前,只需要提前加载好common.js即可
# 登录和注册页面处理
# 准备工作
在 bigevent 根目录中
- 创建 login.html (登录注册页面)
- 创建 /assets/css/login.css
- 创建 /assets/js/login.js
- 加载所需的css和js文件
login.html
<title>Document</title>
<!-- 无论是css还是js,都需要先加载别人的css和js,最后加载自己的css和自己的js -->
<!-- 加载css文件 -->
<link rel="stylesheet" href="./assets/lib/layui/css/layui.css">
<link rel="stylesheet" href="./assets/css/login.css">
<!-- body区,加载js文件 -->
<script src="./assets/lib/jquery.js"></script>
<script src="./assets/lib/layui/layui.all.js"></script>
<script src="./assets/js/common.js"></script>
<script src="./assets/js/login.js"></script>
# 页面布局
login.html
<body>
<!-- logo图片 -->
<img id="logo" src="./assets/images/logo.png" alt="">
<!-- 登录的盒子 start -->
<div class="box login">
<div class="title">大事件后台管理系统</div>
<!-- 登录的表单 start -->
<form class="layui-form" action="">
<!-- 第一项:用户名 -->
<div class="layui-form-item">
<i class="layui-icon layui-icon-username"></i>
<input type="text" name="title" 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="text" name="title" required lay-verify="required" placeholder="请输入密码" autocomplete="off"
class="layui-input">
</div>
<!-- 第三项:按钮 -->
<div class="layui-form-item">
<button class="layui-btn layui-btn-fluid layui-bg-blue" lay-submit lay-filter="formDemo">登录</button>
</div>
<!-- 第四项:超链接 -->
<div class="layui-form-item">
<a href="javascript:;">去注册账号</a>
</div>
</form>
<!-- 登录的表单 end -->
</div>
<!-- 登录的盒子 end -->
<!-- 注册的盒子 start -->
<div class="box register">
<div class="title">大事件后台管理系统</div>
<!-- 注册的表单 start -->
<form class="layui-form" action="">
<!-- 第一项:用户名 -->
<div class="layui-form-item">
<i class="layui-icon layui-icon-username"></i>
<input type="text" name="title" 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="text" name="title" 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="text" name="title" required lay-verify="required" placeholder="请输入确认密码" autocomplete="off"
class="layui-input">
</div>
<!-- 第四项:按钮 -->
<div class="layui-form-item">
<button class="layui-btn layui-btn-fluid layui-bg-blue" lay-submit lay-filter="formDemo">注册</button>
</div>
<!-- 第五项:超链接 -->
<div class="layui-form-item">
<a href="javascript:;">去登录</a>
</div>
</form>
<!-- 注册的表单 end -->
</div>
<!-- 注册的盒子 end -->
<script src="./assets/lib/layui/layui.all.js"></script>
<script src="./assets/lib/jquery.js"></script>
<script src="./assets/js/common.js"></script>
<script src="./assets/js/login.js"></script>
</body>
CSS代码:
html, body {
height: 100%;
}
body {
background-color: #5ea0f1;
}
#logo {
margin: 20px 0 0 240px;
}
/* 登录和注册盒子 */
.box {
height: 310px;
width: 400px;
background-color: #f5da78;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
box-shadow: 5px 5px 5px 5px;
}
.title {
height: 60px;
line-height: 60px;
text-align: center;
font-size: 26px;
}
/* 表单 */
form {
margin: 0 30px;
}
form a {
float: right;
}
/* 字体图标 */
.layui-form-item {
position: relative;
}
.layui-icon {
position: absolute;
left: 6px;
top: 11px;
}
.layui-input {
padding-left: 24px;
}
/* 默认,让注册的盒子隐藏 */
.register {
display: none;
}
# 切换登录和注册的盒子
// --------------------- 切换登录和注册的盒子 --------------------
$('.login a').click(function () {
$('.register').show().prev().hide();
});
$('.register a').click(function () {
$('.login').show().next().hide();
});
# 注册功能
# 完成注册功能
- 注意serialize() 是根据表单项的name属性值获取值的
- 一定要检查表单项的name属性,是否和接口要求的请求参数一致
// ---------------------- 注册功能 ----------------------
// 表单提交 -> 阻止默认行为 -> 收集表单数据(查询字符串) -> ajax提交
$('.register form').on('submit', function (e) {
e.preventDefault();
var data = $(this).serialize();
// console.log(data);
$.ajax({
type: 'POST',
url: '/api/reguser',
data: data,
success: function (res) {
// 提示
layer.msg(res.message);
if (res.status === 0) {
// 清空输入框。找到表单,转成DOM对象,调用DOM方法reset,来重置表单
$('.register form')[0].reset();
// 切换到登录的盒子
$('.login').show().next().hide();
}
}
})
});
如果注册的用户名重复了,但是没有提示。因为如果用户名重复,服务器返回的状态码是500,$.ajax里面的success函数就不会执行了。
所以要加一个 error(请求出错时执行) 或者 complete(请求完成后执行),用于错误提示。
有因为,我们已经在common.js 里面写了 complete 的了,所以只需加一个判断条件即可。
common.js
option.complete = function (xhr) {
var res = xhr.responseJSON;
if (res && res.status === 1 && res.message === '身份认证失败!') {
// 清除掉过期的token
localStorage.removeItem('token');
// 跳转到登录页
location.href = './login.html';
}
// ------------------- 其他错误 -----------------------
if (res && res.status === 1) {
layer.msg(res.message);
}
}
# 表单验证
# 内置验证规则使用方法
layui -> 文档 -> 左侧边栏(内置模块) --> 表单 --> 右(目录)--> 表单验证。
layui提供了表单验证规则。使用方法:
<input type="text" lay-verify="验证规则|验证规则|验证规则" />
layui提供了几个内置的验证规则:
- required(必填项)
- phone(手机号)
- email(邮箱)
- url(网址)
- number(数字)
- date(日期)
- identity(身份证)
比如,一个输入框必填、必填保证邮箱格式,代码如下:
<input type="text" lay-verify="required|email" />
# 自定义验证规则
layui支持自定义验证规则。
// ---------------------- 自定义表单验证 ----------------------
// 必须使用 layui 的内置模块 - form 模块
// 只要使用layui的模块,必须加载模块
var form = layui.form; // 加载form模块
// var laypage = layui.laypage; // =加载laypage分页模块
// var tree = layui.tree; // 加载树形组件模块
// 调用 form 模块内置方法verify,自定义验证规则
form.verify({
// 键(验证规则): 值(验证方法)
// 比如验证用户名长度2~10位,只能是数字字母组合
// user: [/正则表达式/, '验证不通过时的提示']
user: [/^[a-zA-Z0-9]{2,10}$/, '用户名只能是数字字母,且2~10位'], // {2,10} 不是 {2, 10}
len: [/^\S{6,12}$/, '密码6~12位且不能有空格'],
same: function (val) {
// 形参,表示使用该验证规则的输入框的值(谁用这个验证规则,val表示谁的值)
// 案例中,重复密码使用了这个验证规则,所以形参val表示输入的重复密码
if (val !== $('.pwd').val()) {
// return '错误提示'
return '两次密码不一致'
}
}
});
定义完验证规则之后,在HTML页面中,使用该验证规则即可,如下:
用户名
<input type="text" lay-verify="required|user" />
密码框
<input type="password" lay-verify="required|len" />
重复密码框
<input type="password" lay-verify="required|len|same" />
# 细节问题
表单(form标签)必须有 layui-form 这个类。
按钮必须是submit类型的,如果按钮没有指定type,就是提交按钮,那么默认就是submit类型的
按钮必须有 lay-submit 属性,注意是属性,不是类。
HTML中使用验证规则
- 无论用的内置的验证规则,还是自定义的验证规则,用法都一样。
<input lay-verify="验证规则|验证规则" /><input lay-verify="required|email|len|same" />
编写自定义验证规则,需加载 form 模块;(硬性要求:使用layui的模块,必须先加载)
var 变量 = layui.模块名;var form = layui.form;// 加载得到一个对象加载得到的 form 模块,是一个对象,该对象内置一个verify方法,我们调用它编写自定义验证规则。
# 登录功能
// ---------------------- 登录功能 ----------------------
// 表单提交 -> 阻止默认行为 -> 收集表单数据(查询字符串) -> ajax提交
$('.login form').on('submit', function (e) {
e.preventDefault();
var data = $(this).serialize(); // 必须检查name属性值
$.ajax({
type: 'POST',
url: '/api/login',
data: data,
success: function (res) {
layer.msg(res.message);
if (res.status === 0) {
// 登录成功,保存token
localStorage.setItem('token', res.token);
// 跳转到首页面 index.html
location.href = './index.html';
}
}
});
})
# layer弹出层
可以在layui官网 (opens new window)查看弹出层模块的使用,也可以直接进入 layer 独立版本 (opens new window)演示网站。
我们在html中加载的是 layui.all.js,则可以直接使用layer模块,无需加载。
layer.msg() // 方法的作用是在页面中提示一个消息,3秒后自动关闭这个消息框
// 示例如下:
layer.msg('xxxxx');
// 项目中可以使用如下代码:
layer.msg(res.message);
# token的原理
当我们登录成功之后,服务器返回了一个token字符串。
token是一个令牌,访问以 /my 开头的接口的时候,必须携带token,否则会提示身份认证失败。
所以,登录成功之后,获取到token,浏览器端(客户端)需要保存token,以便于后续请求使用。

# 后台首页
# 页面布局
- 到layui官网,文档-->页面元素-->布局-->后台布局。
- 复制后台布局 全部 的代码,粘贴到你的 index.html 中。
- 修改layui.css 和 layui.all.js 的路径。
- 去掉复制过来的全部JS相关代码
- 更换成我们自己的 layui.all.js 即可。
- 至此,index.html 页面布局基本上就实现了。
# 头部处理
- 不对的换掉
- 不要的删除
# 侧边栏导航处理
- 自行调整成和线上效果一样的结构(调整顺序)
- 给“首页” 添加
layui-this类,表示默认该项选中 - 去掉 文章管理 的 “
layui-nav-itemed” 类,刷新后,该项为收缩状态 - 给 ul 添加
lay-shrink="all"属性,则会出现排他(手风琴)效果。
# 创建文件,加载css和js
上面能够修改的都已经修改完毕了,接下来需要的样式需要我们自己编写了,所以不得不创建自己的JS文件和CSS文件了。记得加载他们,包括common.js。
- 创建了 /assets/css/index.css
- 创建了 /assets/js/index.js
# 使用字体图标
<a href=""><i class="layui-icon layui-icon-logout"></i>退出</a>
<a href=""><i class="layui-icon layui-icon-home"></i>首页</a>
<a class="" href="javascript:;"><i class="layui-icon layui-icon-form"></i>文章管理</a>
<a href="javascript:;"><i class="layui-icon layui-icon-username"></i>个人中心</a>
子菜单全部一致
<a href="javascript:;"><i class="layui-icon layui-icon-app"></i>列表一</a>
CSS样式:
/* 所有菜单的调整 */
.layui-icon {
margin: 0 5px;
font-size: 16px;
}
/* 单独调整子菜单 */
.layui-icon-app {
margin-left: 20px;
}
# 头像处理
头部的头像和侧边栏的头像一样
复制头部区域的 a 标签,放到侧边栏开始的位置,修改a标签为div
自定义类。并添加样式,完成最终的效果。
<!-- 侧边栏代码 --> <div class="userinfo" href="javascript:;"> <img src="http://t.cn/RCzsdCq" class="layui-nav-img"> 个人中心 </div>/* 侧边栏的头像位置 div */ div.userinfo { height: 60px; text-align: center; line-height: 60px; }设置欢迎语
<div class="userinfo"> <img src="http://t.cn/RCzsdCq" class="layui-nav-img"> 欢迎你<span class="username">老汤</span> </div>设置文字头像
因为新注册的账号没有图片类型的头像,所以取用户名的第一个字符当做头像。
如果后续,用户更换了图片头像,那么就显示图片头像。
<!-- 头部 --- 添加 span.text-avatar 标签 --> <a href="javascript:;"> <span class="text-avatar">A</span> <img src="http://t.cn/RCzsdCq" class="layui-nav-img"> 个人中心 </a> <!-- 侧边栏 --- 添加 span.text-avatar 标签 --> <div class="userinfo"> <span class="text-avatar">A</span> <img src="http://t.cn/RCzsdCq" class="layui-nav-img"> 欢迎你<span class="username">老汤</span> </div>/* 字体头像 */ .text-avatar { width: 30px; height: 30px; text-align: center; line-height: 30px; font-size: 20px; color: #fff; background-color: #419488; display: inline-block; border-radius: 50%; } /* 默认隐藏两个头像 */ .text-head, .layui-nav-img { display: none; }
# 首页获取用户信息并渲染
开发之前,记得把jQuery和自己的js先加载好
- 封装一个函数
getUserInfo(),完成ajax请求,获取用户信息并渲染 getUserInfo()函数要放到入口函数外部- 封装成函数,后续,其他页面会使用
- 发送请求的时候,必须在请求头中,携带token
- 渲染
- 设置欢迎语
- 优先使用昵称,没有昵称则使用登录账号
- 设置头像
- 优先使用图片,没有图片,则使用名字的第一个字符
- 设置字体头像的时候,不要用show()方法,要自己设置css样式
- 设置欢迎语
function getUserInfo() {
$.ajax({
// type: 'GET', // type不填,默认就是GET
url: '/my/user/userinfo',
success: function (res) {
if (res.status === 0) {
// 1、设置欢迎语(有昵称,就使用昵称,没有昵称,使用用户名)
var myname = res.data.nickname || res.data.username;
$('.myname').text(myname);
// 2、设置头像(有图片,使用图片;没有图片,使用名字的首字母)
if (res.data.user_pic) {
// 使用图片
$('.layui-nav-img').attr('src', res.data.user_pic).show();
$('.text-avatar').hide();
} else {
var t = myname.substr(0, 1).toUpperCase();
// jQuery中的show方法,会设置元素 display:inline;
$('.text-avatar').text(t).css('display', 'inline-block');
$('.layui-nav-img').hide();
}
}
},
// jQuery中ajax选项,有一个headers,通过他,可以设置请求头
headers: {
'Authorization': localStorage.getItem('token')
}
});
}
# 退出功能
- 退出超链接
- 加入 id="logout"
- href="javascript:;" 这点一定要注意,必填
- 点击退出
- 询问
- 删除token
- 跳转到 login.html
// -------------- 退出功能 ---------------------
// 退出的时候,两个操作
// - 删除token
// - 页面跳转到登录页面
$('#logout').click(function () {
// 弹出层,询问是否要退出
layer.confirm('你确定退出吗?', function (index) {
//do something
// 如果点击了确定,删除token,页面跳转
localStorage.removeItem('token');
location.href = '/login.html';
layer.close(index); // 关闭当前弹出层
});
});
# 其他页面处理
# 文件存放思路
如果继续把html文件存放到项目根目录,那么根目录的文件会越来越多,会越来越乱,所以建议单独存放个人中心相关的几个页面。老师的做法如下(仅供参考)
- html放到根目录下的user文件夹
- css,也要放到 /assets/css/user/ 这里
- js,也要放到 /assets/js/user/ 这里
# 首页内容区说明
使用iframe标签
- iframe标签是HTML标签
- iframe在整个页面(父页面)中,占用一个区域,这个区域可以引入其他(子)页面
- src属性用于引入默认的子页面
- 侧边栏的 a 标签,href属性正常挂链接
- 侧边栏的 a 标签,target属性,表示打开新页面的位置;
- 通过指定
target=“iframe标签的name值”,可以在iframe区域打开链接的页面

# 基本资料
# 准备工作
- 创建HTML文件、css文件、js文件
- 创建 /user/userinfo.html
- 创建 /assets/css/userinfo.css
- 创建 /assets/js/user/userinfo.js
- index.html 头部和侧边栏,挂超链接,链接到 /user/userinfo.html,注意target="fm"
# 页面布局
略(因为是复制过来的)
只需要修改一下页面中的文字、修改一下类名、name属性值等等
- 修改了input的name属性值(分别是username、nickname、email)
- 设置登录账号这个input disable属性,因为修改的时候,不允许修改登录账号
- 给邮箱加一个email验证规则
# 为表单赋值
修改页面的js文件,为userinfo.js
思路:
- 发送ajax请求,获取用户信息
- 设置表单各项(每个输入框)的value值。
具体步骤:
需要在 form 中,添加一个隐藏域,用于保存id值,前面已经添加过了
<input type="hidden" name="id" /> <!-- 隐藏域,只要放到 form 里面即可 -->先设置表单各项的 name 属性(username/nickname/email/id)
发送ajax请求,获取用户信息
使用layui的from模块快速为表单赋值
- 为表单设置
lay-filter="user",值随便定义,我这里使用的是 user - JS代码中,一行代码为表单赋值
let form = layui.form; form.val('user', res.data);要求,res.data 这个对象的属性(key)要和表单各项的name属性值相同,才能赋值
- 为表单设置
只要是修改操作:
- 必须要进行数据回填操作,保证输入框是有值的
- 修改的表单中,一般都有隐藏域 id
# 完成更新用户信息的功能
- 设置 登录账号为
disabled- 不允许修改
- 通过 $('form').serialize() 不能获取到 username 值,刚刚好是我们的需要。
- 注册表单的提交事件
- 阻止默认行为
- 收集表单数据,使用 $('form').serialize() 。(id、nickname、email)
- 发送ajax请求,完成更新
- 更新成功之后,提示,并且调用父页面的
getUserInfo()从新渲染用户的头像
// ------------------ 表单提交的时候,完成用户信息的更新 -----------------
// 监听表单的提交事件。
$('form').on('submit', function (e) {
// 阻止默认行为
e.preventDefault();
// 获取id、nickname、email的值
var data = $(this).serialize();
// console.log(data);
// ajax提交给接口,从而完成更新
$.ajax({
type: 'POST',
url: '/my/userinfo',
data: data,
success: function (res) {
// 无论成功还是失败,都要提示
layer.msg(res.message);
if (res.status === 0) {
window.parent.getUserInfo();
}
}
});
});
# 重置表单
// --------------------- 重置表单 -------------------------
$('button:contains("重置")').click(function (e) {
e.preventDefault();
renderForm(); // 调用renderForm(),为表单重新赋值,就可以恢复成原样
});
# 更新密码
# 准备工作
- 创建所需的HTML、js文件、css文件
- 经过观察,所有小页面的布局都一样,所以这里可以使用base.css
- 在 /assets/css 里面创建 base.css (只规定body的背景色、卡片布局的边距)
- 首页侧边栏和头部区域挂好链接
- href=“/user/repwd.html”
- target="fm" fm是iframe的name属性值
- 加载好所需的css和js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/assets/lib/layui/css/layui.css">
<!-- 因为重置密码页面和基本资料页面样式一样 -->
<link rel="stylesheet" href="/assets/css/userinfo.css">
</head>
<body>
<!--
1. html代码,去userinfo.html 中复制
2. 修改文字
3. 去掉第一个输入框的 disabled 属性
4. 去掉 隐藏域id
5. 修改原密码的name属性为 oldPwd;修改新密码的name为 newPwd;去掉重复密码的name属性
-->
<script src="/assets/lib/jquery.js"></script>
<script src="/assets/lib/layui/layui.all.js"></script>
<script src="/assets/js/common.js"></script>
<script src="/assets/js/user/repwd.js"></script>
</body>
</html>
# 表单验证
// --------------------- 1. 表单验证 ------------------------
// 1) 长度6~12位,不能有空格 (两个新密码都要用)
// 2) 新密码不能和原密码相同
// 3) 两次新密码必须一致
// 加载 layui 的 form 模块
var form = layui.form;
// 自定义验证规则
form.verify({
// 1) 长度6~12位,不能有空格
len: [/^\S{6,12}$/, '密码长度必须是6~12且不能有空格'],
// 2) 新密码不能和原密码相同
diff: function (val) {
// 形参表示使用该验证规则的输入框的值,新密码使用这个验证规则,所以val表示填写的新密码
// 获取原密码
var oldPwd = $('input[name=oldPwd]').val();
if (val === oldPwd) {
return '新密码不能和原密码相同';
}
},
// 3) 两次新密码必须一致 (重复密码 用这个验证规则)
same: function (val) {
// val 表示填写的重复密码
// 获取新密码
var newPwd = $('input[name=newPwd]').val();
if (newPwd !== val) {
return '两次密码不一样哟~';
}
}
});
- 三个密码框,都使用len这个验证规则
- 新密码,使用diff,这个验证规则
- 确认密码,使用 same 验证规则
# ajax请求,完成更新
// 监听表单的提交事件
$('form').on('submit', function (e) {
e.preventDefault();
var data = $(this).serialize(); // serialize是根据表单input的name属性值获取值的,所以一定要检查name属性值
$.post('/my/updatepwd', data, function (res) {
// 无论修改成功还是失败,都给出提示
layer.msg(res.message);
if (res.status === 0) {
// 修改成功,清空输入框的值
$('form')[0].reset(); // DOM方法reset表示重置表单
}
});
});
# 更换头像
# 准备工作
创建文件
- 创建 /user/avatar.html
- 创建 /assets/css/user/avatar.css
- 创建 /assets/js/user/avatar.js
index.html 中,侧边栏和头部区域挂好超链接
avatar.html 中 引入所需的css和js文件
<!-- 加载layui.css --> <link rel="stylesheet" href="/assets/lib/layui/css/layui.css"> <!-- 加载cropper.css --> <link rel="stylesheet" href="/assets/lib/cropper/cropper.css"> <!-- 加载自己的css --> <link rel="stylesheet" href="/assets/css/user/avatar.css"> <!-- 加载jQuery --> <script src="/assets/lib/jquery.js"></script> <!-- 加载layui.all.js --> <script src="/assets/lib/layui/layui.all.js"></script> <!-- 按顺序 加载Cropper.js --> <script src="/assets/lib/cropper/Cropper.js"></script> <!-- 按顺序 jquery-cropper.js --> <script src="/assets/lib/cropper/jquery-cropper.js"></script> <!-- 加载common.js --> <script src="/assets/js/common.js"></script> <!-- 加载avatar.js --> <script src="/assets/js/user/avatar.js"></script>
# 复制HTML和CSS(完成布局)
首先,得有一个卡片面板布局(去layui复制)
<div class="layui-card"> <div class="layui-card-header">卡片面板</div> <div class="layui-card-body"> 卡片式面板面板通常用于非白色背景色的主体内<br> 从而映衬出边框投影 </div> </div>去 “cropper的基本用法.md” 笔记中,复制代码(html和css),放到卡片布局的内容区。
完成后的效果:

# 创建剪裁区(初始化剪裁区)
- 使用插件 cropper ,提供的方法,实现剪裁区的创建
- 具体做法:
- 找到剪裁区的图片 (img#image)
- 设置配置项
- 调用cropper方法,创建剪裁区
// --------------- 创建剪裁区 ------------------
// - 找到剪裁区的图片 (img#image)
var $image = $('#image');
// - 设置配置项
var option = {
// 纵横比(宽高比)
aspectRatio: 1, // 正方形
// 指定预览区域
preview: '.img-preview' // 指定预览区的类名(选择器)
};
// - 调用cropper方法,创建剪裁区
$image.cropper(option);
# 点击按钮,可选择图片
- html中加入一个隐藏的文件域
- 点击上传按钮的时候,触发文件域的单击事件
<!-- 加一个隐藏的文件域 -->
<input type="file" id="file" style="display: none;" accept="image/*">
<button type="button" class="layui-btn">上传</button>
// ------------- 点击 上传 ,可以选择图片 ------------
$('button:contains("上传")').click(function () {
$('#file').click();
});
# 更换图片,重置剪裁区
- 找到选择的文件(文件对象)
- 为文件对象创建一个临时的url
- 更换剪裁区的图片
- 先销毁原来的剪裁区
- 更改图片的src属性
- 重新生成剪裁区
// 文件域的内容改变的时候,更换剪裁区的图片
$('#file').change(function () {
// 3.1) 先找到文件对象
// console.dir(this)
var fileObj = this.files[0];
// 3.2) 为选择的图片生成一个临时的url
var url = URL.createObjectURL(fileObj);
// console.log(url);
// 3.3) 更换图片的src属性即可(销毁剪裁区 --> 更换src属性 --> 重新创建剪裁框)
$image.cropper('destroy').attr('src', url).cropper(option);
});
# 点击确定,实现剪裁并修改头像
- 调用 cropper 方法,传递
getCroppedCanvas参数,得到一个canvas图片(对象) - 调用canvas的toDataURL()方法,得到base64格式的图片
- ajax提交即可
// ------------- 4. 点击确定按钮,剪裁图片,把图片转成base64格式,ajax提交字符串,完成更换 ----
$('button:contains("确定")').click(function () {
// 4.1)调用插件方法,剪裁图片;剪裁之后得到一张canvas格式的图片
var canvas = $image.cropper('getCroppedCanvas', {
width: 100,
height: 100
});
// 4.2) 把canvas图片转成base64格式,得到超长字符串
var base64 = canvas.toDataURL('image/png');
// console.log(base64);
// 4.3) ajax提交字符串,完成更新
$.ajax({
type: 'POST',
url: '/my/update/avatar',
data: { avatar: base64 },
success: function (res) {
layer.msg(res.message);
if (res.status === 0) {
// 重新渲染父页面的头像
window.parent.getUserInfo();
}
}
});
});
# 关于base64格式的图片说明
- base64格式只是图片的一种格式,用字符串表示图片的格式
- base64格式的优点:减少http请求,加快小图片的响应速度,减轻服务器的压力
- base64格式的缺点:体积比正常图片大 30% 左右。
- 如果是小图片,可以使用base64格式,大图片不建议使用了。
https://www.css-js.com/tools/base64.html
# 文章列表页布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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="./css/list.css">
</head>
<body>
<div class="layui-card">
<div class="layui-card-header">文章列表</div>
<div class="layui-card-body">
<!-- 内容区一 表单搜索区 start -->
<form class="layui-form" action="">
<div class="layui-form-item">
<div class="layui-inline">
<div class="layui-input-inline" style="width: 200px;">
<select name="city" lay-verify="">
<option value="">请选择一个城市</option>
<option value="010">北京</option>
<option value="021">上海</option>
<option value="0571">杭州</option>
</select>
</div>
<div class="layui-input-inline" style="width: 200px;">
<select name="city" lay-verify="">
<option value="">请选择一个城市</option>
<option value="010">北京</option>
<option value="021">上海</option>
<option value="0571">杭州</option>
</select>
</div>
</div>
<div class="layui-inline">
<div class="layui-input-inline" style="width: 100px;">
<button class="layui-btn" lay-submit lay-filter="formDemo">立即提交</button>
</div>
</div>
</div>
</form>
<!-- 内容区一 表单搜索区 end -->
<!-- 内容区二 表格区 start -->
<table class="layui-table">
<colgroup>
<col width="40%">
<col width="15%">
<col width="15%">
<col width="15%">
<col>
</colgroup>
<thead>
<tr>
<th>文章标题</th>
<th>分类</th>
<th>发布时间</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>静夜思</td>
<td>艺术</td>
<td>2021-01-13 12:39:08</td>
<td>已发布</td>
<td>
<button type="button" class="layui-btn layui-btn-xs">编辑</button>
<button type="button" class="layui-btn layui-btn-xs layui-btn-danger">删除</button>
</td>
</tr>
</tbody>
</table>
<!-- 内容区二 表格区 end -->
<!-- 内容区三 分页区 start -->
<div id="page"></div>
<!-- 内容区三 分页区 end -->
</div>
</div>
<script src="../assets/lib/layui/layui.all.js"></script>
<script src="../assets/lib/jquery.js"></script>
<script src="../assets/lib/template-web.js"></script>
<script src="../assets/js/common.js"></script>
<script src="./js/list.js"></script>
</body>
</html>
CSS:
body {
background-color: #f2f3f5;
padding: 15px;
}
# 添加文章页面布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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/lib/cropper/cropper.css">
<link rel="stylesheet" href="./css/add.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="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">
<select name="city" lay-verify="required">
<option value=""></option>
<option value="0">北京</option>
<option value="1">上海</option>
<option value="2">广州</option>
<option value="3">深圳</option>
<option value="4">杭州</option>
</select>
</div>
</div>
<!-- 第三项:文章内容 -->
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">文本域</label>
<div class="layui-input-block">
<textarea name="desc" placeholder="请输入内容" class="layui-textarea"></textarea>
</div>
</div>
<!-- 第四项:封面图片 -->
<div class="layui-form-item">
<!-- 左侧的 label -->
<label class="layui-form-label">文章封面</label>
<!-- 选择封面区域 -->
<div class="layui-input-block cover-box">
<!-- 左侧裁剪区域 -->
<div class="cover-left">
<img id="image" src="/assets/images/sample2.jpg" alt="" />
</div>
<!-- 右侧预览区域和选择封面区域 -->
<div class="cover-right">
<!-- 预览的区域 -->
<div class="img-preview"></div>
<!-- 选择封面按钮 -->
<button type="button" class="layui-btn layui-btn-danger">选择封面</button>
</div>
</div>
</div>
<!-- 第五项:选择状态 -->
<div class="layui-form-item">
<label class="layui-form-label">单选框</label>
<div class="layui-input-block">
<input type="radio" name="sex" value="男" title="男">
<input type="radio" name="sex" value="女" title="女" checked>
</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>
</div>
</div>
<script src="../assets/lib/layui/layui.all.js"></script>
<script src="../assets/lib/jquery.js"></script>
<script src="../assets/lib/template-web.js"></script>
<!-- 加载富文本编辑器插件,按照顺序加载 -->
<script src="../assets/lib/tinymce/tinymce.min.js"></script>
<script src="../assets/lib/tinymce/tinymce_setup.js"></script>
<!-- 按照顺序,加载剪裁插件的js -->
<script src="../assets/lib/cropper/Cropper.js"></script>
<script src="../assets/lib/cropper/jquery-cropper.js"></script>
<script src="../assets/js/common.js"></script>
<script src="./js/add.js"></script>
</body>
</html>
CSS:
body {
background-color: #f2f3f5;
padding: 15px;
}
/* 封面容器的样式 */
.cover-box {
display: flex;
}
/* 左侧裁剪区域的样式 */
.cover-left {
width: 400px;
height: 280px;
overflow: hidden;
margin-right: 20px;
}
/* 右侧盒子的样式 */
.cover-right {
display: flex;
flex-direction: column;
align-items: center;
}
/* 预览区域的样式 */
.img-preview {
width: 200px;
height: 140px;
background-color: #ccc;
margin-bottom: 20px;
overflow: hidden;
}
JS:
initEditor(); // 调用函数,就会把 textarea 替换为富文本编辑器
// 1. 初始化剪裁框
// 1. 初始化图片裁剪器
var $image = $('#image')
// 2. 裁剪选项
var options = {
aspectRatio: 400 / 280,
preview: '.img-preview'
}
// 3. 初始化裁剪区域
$image.cropper(options)
# 文章列表
# 准备工作
- 准备工作(创建页面、挂好链接、引入所需的css和js文件)
<!-- 加载所需的js和css -->
<link rel="stylesheet" href="../assets/lib/layui/css/layui.css">
<link rel="stylesheet" href="./css/list.css">
<script src="../assets/lib/layui/layui.all.js"></script>
<script src="../assets/lib/jquery.js"></script>
<script src="../assets/lib/template-web.js"></script>
<script src="../assets/js/common.js"></script>
<script src="./js/list.js"></script>
# 页面布局
layui的卡片面板
筛选区
- 找到 “页面元素 --> 表单 --> 目录 --> 组装行内表单”
- 不需要的文字删除
- 更换文本框为 下拉框和按钮
表格区
- 自行复制代码,然后调整宽度、设置按钮
分页区
- 一个 id为page的空div
完整的页面结构
<div class="layui-card">
<div class="layui-card-header">文章列表</div>
<div class="layui-card-body">
<!-- 内容区一 表单搜索区 start -->
<form class="layui-form" action="">
<div class="layui-form-item">
<div class="layui-inline">
<div class="layui-input-inline" style="width: 200px;">
<select name="city" lay-verify="">
<option value="">请选择一个城市</option>
<option value="010">北京</option>
<option value="021">上海</option>
<option value="0571">杭州</option>
</select>
</div>
<div class="layui-input-inline" style="width: 200px;">
<select name="city" lay-verify="">
<option value="">请选择一个城市</option>
<option value="010">北京</option>
<option value="021">上海</option>
<option value="0571">杭州</option>
</select>
</div>
</div>
<div class="layui-inline">
<div class="layui-input-inline" style="width: 100px;">
<button class="layui-btn" lay-submit lay-filter="formDemo">立即提交</button>
</div>
</div>
</div>
</form>
<!-- 内容区一 表单搜索区 end -->
<!-- 内容区二 表格区 start -->
<table class="layui-table">
<colgroup>
<col width="40%">
<col width="15%">
<col width="15%">
<col width="15%">
<col>
</colgroup>
<thead>
<tr>
<th>文章标题</th>
<th>分类</th>
<th>发布时间</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>静夜思</td>
<td>艺术</td>
<td>2021-01-13 12:39:08</td>
<td>已发布</td>
<td>
<button type="button" class="layui-btn layui-btn-xs">编辑</button>
<button type="button" class="layui-btn layui-btn-xs layui-btn-danger">删除</button>
</td>
</tr>
</tbody>
</table>
<!-- 内容区二 表格区 end -->
<!-- 内容区三 分页区 start -->
<div id="page"></div>
<!-- 内容区三 分页区 end -->
</div>
</div>
# 渲染数据
- 定义渲染文章列表的函数 (renderArticle)
- ajax请求参数,我们先定义成全局变量(后面还需要使用)
- 定义renderArticle函数,函数内容,发送ajax请求,获取数据,并调用template渲染
JS代码:
// 分页获取文章列表的请求参数
var data = {
pagenum: 1, // 页码值,比如2,将获取到第2页的数据
pagesize: 2, // 每页有多少条数据,比如5,表示每页5条数据
// cate_id: 1,
// state: '已发布'
}
// ------------------ 获取文章并渲染到表格中 ------------------
function renderArticle() {
$.ajax({
url: '/my/article/list',
data: data,
success: function (res) {
// console.log(res);
// 使用模板引擎,渲染数据
var htmlStr = template('tpl-article', res);
$('tbody').html(htmlStr);
}
});
}
renderArticle();
HTML模板:
<!-- 文章列表的模板 start -->
<script type="text/html" id="tpl-article">
{{each data item}}
<tr>
<td>{{item.title}}</td>
<td>{{item.cate_name}}</td>
<td>{{item.pub_date}}</td>
<td>{{item.state}}</td>
<td>
<a href="./edit.html?id={{item.id}}" class="layui-btn layui-btn-xs">编辑</a>
<button type="button" class="layui-btn layui-btn-xs layui-btn-danger">删除</button>
</td>
</tr>
{{/each}}
</script>
<!-- 文章列表的模板 end -->
# 定义模板引擎过滤器函数
使用自定义函数,处理时间日期
template.defaults.imports.dateFormat = function (time) {
var date = new Data(time);
var y = date.getFullYear();
var m = addZero(date.getMonth() + 1);
var d = addZero(date.getDate());
// 时分秒自己写
return y + '-' + m + '-' + d;
}
// 补零函数
function addZero (n) {
return n < 10 ? '0' + n : n;
}
模板中使用自定义的过滤器函数处理时间
<td>{{item.pub_date | dateFormat}}</td>
# 删除文章
给删除按钮,添加一个data-id属性,值就是当前文章的id,添加一个类 delete
<button data-id="{{item.id}}" type="button" class="delete layui-btn layui-btn-xs layui-btn-danger">删除</button>JS代码中,事件委托的方案,给删除注册单击事件
事件内部,获取id
询问是否要删除
如果确定删除,则发送ajax请求,完成删除
完成删除之后,从新渲染页面
这里的id参数,是一种url参数,只需要在接口后面 连接 上id即可。
比如:/my/article/delete/2 表示删除id为2的文章。
完整的代码:
// -------------------------- 删除 ------------------------------
$('tbody').on('click', 'button:contains("删除")', function () {
var id = $(this).data('id');
layer.confirm('确定删除吗?', function (index) {
$.ajax({
// url: '/my/article/delete/2',
url: '/my/article/delete/' + id,
success: function (res) {
layer.msg(res.message);
if (res.status === 0) {
renderArticle();
}
}
});
layer.close(index); // 关闭弹层
});
})
另外一个删除思路,当前页的文章删没了,我们显示上一页的数据。
- 当确定删除了,首先用dom的方式,把tr移除。
- 当删除的请求成功后,判断tbody里面是否有tr
- 如果有tr,那么还获取当前页的数据
- 如果没有tr,说明当前页的数据被删没了,则 修改 pagenum--,获取上一页的数据
// -------------------------- 删除 ------------------------------
$('tbody').on('click', 'button:contains("删除")', function () {
var id = $(this).data('id');
var that = $(this);
layer.confirm('确定删除吗?', function (index) {
// 使用dom的方式删除该行
that.parents('tr').remove();
$.ajax({
// url: '/my/article/delete/2',
url: '/my/article/delete/' + id,
success: function (res) {
layer.msg(res.message);
if (res.status === 0) {
if ($('tbody').children().length > 0) {
renderArticle();
} else {
data.pagenum--;
if (data.pagenum === 0) return;
renderArticle();
}
}
}
});
layer.close(index); // 关闭弹层
});
})
# 添加文章
# 准备工作
创建html、css、js文件;
链接好所需的css和js文件
index.html 侧边栏挂好链接
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>发布文章</title>
<!-- 加载所需的js和css -->
<link rel="stylesheet" href="../assets/lib/layui/css/layui.css">
<link rel="stylesheet" href="../assets/lib/cropper/cropper.css">
<link rel="stylesheet" href="./css/add.css">
</head>
<body>
内容区,还是卡片面板
<script src="../assets/lib/layui/layui.all.js"></script>
<script src="../assets/lib/jquery.js"></script>
<script src="../assets/lib/template-web.js"></script>
<!-- 加载富文本编辑器插件,按照顺序加载 -->
<script src="../assets/lib/tinymce/tinymce.min.js"></script>
<script src="../assets/lib/tinymce/tinymce_setup.js"></script>
<!-- 按照顺序,加载剪裁插件的js -->
<script src="../assets/lib/cropper/Cropper.js"></script>
<script src="../assets/lib/cropper/jquery-cropper.js"></script>
<script src="../assets/js/common.js"></script>
<script src="./js/add.js"></script>
</body>
</html>
# 页面布局
使用卡片面板
卡片的body区放表单
表单的内容区(content)
- 去 layui --> 文档 --> 表单 --> 小睹为快 复制 多行文本域。
- 在自己的js中,调用一个
initEditor()函数,该函数会把 textarea替换成富文本框
表单的图片裁剪区(cover_img),添加如下的表单行
<div class="layui-form-item"> <!-- 左侧的 label --> <label class="layui-form-label">文章封面</label> <!-- 选择封面区域 --> <div class="layui-input-block cover-box"> <!-- 左侧裁剪区域 --> <div class="cover-left"> <img id="image" src="/assets/images/sample2.jpg" alt="" /> </div> <!-- 右侧预览区域和选择封面区域 --> <div class="cover-right"> <!-- 预览的区域 --> <div class="img-preview"></div> <!-- 选择封面按钮 --> <button type="button" class="layui-btn layui-btn-danger">选择封面</button> </div> </div> </div>CSS样式
/* 封面容器的样式 */ .cover-box { display: flex; } /* 左侧裁剪区域的样式 */ .cover-left { width: 400px; height: 280px; overflow: hidden; margin-right: 20px; } /* 右侧盒子的样式 */ .cover-right { display: flex; flex-direction: column; align-items: center; } /* 预览区域的样式 */ .img-preview { width: 200px; height: 140px; background-color: #ccc; margin-bottom: 20px; overflow: hidden; }JS,实现基本的剪裁框
// 1. 初始化图片裁剪器 var $image = $('#image') // 2. 裁剪选项 var options = { aspectRatio: 400 / 280, preview: '.img-preview' } // 3. 初始化裁剪区域 $image.cropper(options)按钮区
- 使用一个提交按钮
完整的HTML结构:
<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="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">
<select name="cate_id" lay-verify="required">
<option value=""></option>
<option value="0">北京</option>
<option value="1">上海</option>
<option value="2">广州</option>
<option value="3">深圳</option>
<option value="4">杭州</option>
</select>
</div>
</div>
<!-- 第三项:文章内容 -->
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">文章内容</label>
<div class="layui-input-block">
<textarea name="content" placeholder="请输入内容" class="layui-textarea"></textarea>
</div>
</div>
<!-- 第四项:封面图片 -->
<div class="layui-form-item">
<!-- 左侧的 label -->
<label class="layui-form-label">文章封面</label>
<!-- 选择封面区域 -->
<div class="layui-input-block cover-box">
<!-- 左侧裁剪区域 -->
<div class="cover-left">
<img id="image" src="/assets/images/sample2.jpg" alt="" />
</div>
<!-- 右侧预览区域和选择封面区域 -->
<div class="cover-right">
<!-- 预览的区域 -->
<div class="img-preview"></div>
<!-- 选择封面按钮 -->
<button type="button" class="layui-btn layui-btn-danger">选择封面</button>
</div>
</div>
</div>
<!-- 第五项:选择状态 -->
<div class="layui-form-item">
<label class="layui-form-label">文章状态</label>
<div class="layui-input-block">
<input type="radio" name="state" value="已发布" title="发布">
<input type="radio" name="state" value="草稿" title="存为草稿" checked>
</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>
</div>
</div>
# 获取分类渲染到下拉列表
- 获取所有的分类,渲染到下拉框
- ajax请求之后,获取到分类
- 使用模板引擎渲染select框
- 动态添加select框之后,发现页面中的下拉框看不见了,需要使用 form.render() 方法更新渲染。
// ------------------ 获取分类,渲染到下拉框中 -------------
$.ajax({
url: '/my/category/list',
success: function (res) {
var str = template('tpl-category', res);
$('select').html(str);
// 模板引擎处理完之后,重新渲染select
form.render('select');
}
});
HTML模板:
<script type="text/html" id="tpl-category">
<option value="">请选择文章类别</option>
{{each data item}}
<option value="{{item.id}}">{{item.name}}</option>
{{/each}}
</script>
# 处理封面区
你可以复制之前的代码。
// -------------------------- 处理封面图片 -----------------
// 1. 初始化剪裁框
// 1.1) 初始化图片裁剪器
var $image = $('#image')
// 1.2) 裁剪选项
var options = {
aspectRatio: 400 / 280,
preview: '.img-preview'
}
// 1.3) 初始化裁剪区域
$image.cropper(options);
// 2. 点击 “选择封面” 能够选择图片
$('button:contains("选择封面")').click(function () {
$('#file').trigger('click');
});
// 3. 图片切换的时候,更换剪裁区的图片
$('#file').change(function () {
// 3.1) 找到文件对象
var fileObj = this.files[0];
// 3.2) 创建url
var url = URL.createObjectURL(fileObj);
// 3.3) 更换图片
$image.cropper('destroy').attr('src', url).cropper(options);
});
# 实现最终的发布
- 把表单中,每个表单元素的name检查一下,因为FormData是根据name获取值的
- 注册表单提交事件
- 收集表单各项数据 (FormData只收集到了 title/state/cate_id 这三个值)
- content需要通过插件 tinymce 的特有方式来获取,获取之后,更改fd中的content值
- tinyMCE.activeEditor.getContent() 使用这行代码获取文章内容
- 使用 fd.set('content', tinyMCE.activeEditor.getContent());
- 完成图片裁剪,转换成blob格式,并将得到的图片追加到FormData中
// --------------------- 完成最终的添加文章 ----------------------
$('#add-form').on('submit', function (e) {
e.preventDefault();
// 收集表单数据(必须是FormData)
var fd = new FormData(this);
// fd对象中,有content,但是值为空; 根本就没有 图片
// 1. 获取富文本编辑器里面的内容,并不是追加到fd中,而是更改fd里面的内容
fd.set('content', tinyMCE.activeEditor.getContent());
// 2. 剪裁图片,转成 blob 形参(二进制形式或文件对象形式),追加到fd中
var canvas = $image.cropper('getCroppedCanvas', {
width: 400,
height: 280
});
// 把canvas图片转成二进制形式
canvas.toBlob(function (blob) {
// 追加文件对象到fd中
fd.append('cover_img', blob);
// 检查一下,fd对象中,是否取得了接口要求的所有参赛
// fd.forEach((val, key) => {
// console.log(key, val);
// });
// return;
// 发送ajax请求,完成最终的添加
$.ajax({
type: 'POST',
url: '/my/article/add',
data: fd,
success: function (res) {
layer.msg(res.message);
if (res.status === 0) {
// 添加成功,跳转到 文章列表 页面
location.href = '/article/article.html'
}
},
processData: false, // 不要处理数据;意思是不要把对象形式的fd转换成查询字符串形式
contentType: false // 不要加默认的请求头(application/x-www-form-urlencoded),让浏览器自行设置请求头
});
});
})
# 编辑文章
# 思路
复制添加文章页为编辑页面(edit.html),css和js同样复制一份,并修改css和js链接。
编辑页面,打开之后,需要做数据回填。
后续实现,和添加基本一样。
# 实现
复制 add.html 为 edit.html (编辑页面)、css和js自行复制,别忘记修改css和js链接。
文章列表页面(list.html),给 ”编辑“ 挂超链接,链接到 edit.html ,并且传递 id 参数
<a href="./edit.html?id={{val.id}}" class="layui-btn layui-btn-xs">编辑</a>vscode中千万不要直接打开edit.html ,否则获取不到文章id;
应该先打开文章列表页面,通过点击编辑按钮跳转到edit.html才是正确的。
edit.js 中 获取地址栏的id,根据id查询一篇文章详情,然后完成表单数据渲染
// 获取地址栏的id,这个id是文章的id; var id = new URLSearchParams(location.search).get('id'); // console.log(id);
```
在下拉框的分类渲染成功后,完成数据回填
// -------------------------- 获取分类,渲染到下拉框的位置 -------- $.ajax({ url: '/my/article/list', success: function (res) { var html = template('tpl-category', res); $('select[name=cate_id]').html(html); form.render('select'); // 下拉框的分类渲染完成,然后再去发送ajax请求,获取文章详情 // 根据id可以获取文章详情(标题、内容、状态、图片.....)全部获取到 $.ajax({ // url: '/my/article/:id', // 把 :id 换成真实的id即可 url: '/my/article/' + id, success: function (res) { // console.log(res); // 获取到详情后,做数据回填 (使用layui提供的 form.val()) form.val('article', res.data); // 一定先做数据回填,然后在把 textarea 换成 富文本编辑器 initEditor(); // 更换图片(销毁剪裁区 --> 更换图片 --> 重建剪裁区) $image .cropper('destroy') .attr('src', baseUrl + '/' + res.data.cover_img) .cropper(options); } }); } });edit.js中,图片剪裁默认铺满整个区域
var options = { // 宽高比 aspectRatio: 400 / 280, autoCropArea: 1, // 让剪裁框铺满整个剪裁区 // 设置预览区的选择器 preview: '.img-preview' };添加Id
// 追加Id data.append('id', id);修改添加文章的
接口为更新文章的接口即可,其他都不需要修改。
# 分页
- 文章列表页,加载layui的laypage模块
- 编写渲染分页的函数 (showPage)
- 渲染完文章列表之后,马上渲染分页(在renderArticle函数里面,ajax请求成功后,调用
showPage(res.total)) - showPage函数
- 根据官方文档,生成分页效果
- jump事件中,修改请求参数中的pagenum和pagesize,并重新渲染列表
/**** 加载layui的laypage模块 *******/
var laypage = layui.laypage;
/**** 全局设置请求参数 *******/
var data = {
pagenum: 1, // 页码值
pagesize: 2, // 每页显示多少条
// cate_id: ,
// state: ,
};
/**** 定义renderArticle函数,获取文章列表数据;成功后调用createPage() 函数 ******/
function renderArticle() {
$.ajax({
url: '/my/article/list',
data: data,
success: function (res) {
console.log(res);
// res.total // 总数
// 通过模板引擎,渲染
let str = template('list', res);
$('tbody').html(str);
// 当ajax请求成功之后,获取到总数之后,调用显示分页的函数
showPage(res.total);
}
});
}
/********* 定义showPage函数 **********/
// 实现分页
function showPage (t) {
laypage.render({
elem: 'page', // 不要加 #
count: t, // 表示总计有多少条数据
limit: data.pagesize, // 每页显示多少条
limits: [2, 3, 4, 5],
curr: data.pagenum, // 起始页(控制页码的背景色,表示是选中状态)
// prev: '上一个'
layout: ['limit', 'prev', 'page', 'next', 'count', 'skip'],
// 点击页码的时候,会触发下面的jump函数。页面刷新之后,也会触发一次
jump: function (obj, first) {
// console.log(obj); // 表示前面控制分页的所有属性
// console.log(first); // 刷新页面之后,是tru,再点击页码,它就是undefined了
// 点击页码的时候,jump函数会触发,此时,改变data.pagesize和data.pagenum,调用renderArticle即可看对对应页的数据
if (!first) {
// console.log(obj.curr);
data.pagenum = obj.curr;
data.pagesize = obj.limit;
renderArticle();
}
}
});
}
实现分页,就是修改请求参数(pagenum和pagesize),然后重新发送ajax请求,重新渲染页面。
# 筛选
# 处理搜索区的两个下拉框
分类的获取
// -------------------------- 筛选 ------------------------------ var form = layui.form; // 1. 获取真实的分类,渲染到下拉框的位置 $.ajax({ url: '/my/category/list', success: function (res) { // console.log(res) var str = template('tpl-category', res); // $('#category').append(str); $('#category').html(str); // 更新渲染 // form.render('select', 'lay-filter属性值'); form.render('select'); } })<!-- 分类下拉框留空 --> <select id="category" lay-verify=""> </select><!-- 分类的模板 --> <script type="text/html" id="tpl-category"> <option value="">请选择一个分类</option> {{each data item}} <option value="{{item.id}}">{{item.name}}</option> {{/each}} </script>状态自行处理即可
<select id="state" lay-verify=""> <option value="">所有状态</option> <option value="已发布">已发布</option> <option value="草稿">草稿</option> </select>
# 完成搜索功能
- 思路
- 根据搜索条件,改变请求参数即可。
- 监听搜索区的表单(自己加id=search)的提交事件
- 获取下拉框的值,根据下拉框的id获取值
- 修改获取文章列表的请求参数
- 重置页码为1
- 重新渲染文章列表
// 2. 完成筛选
$('#search').on('submit', function (e) {
e.preventDefault();
// 获取两个下拉框的值
var cate_id = $('#category').val();
var state = $('#state').val();
// 设置ajax请求的参数
if (cate_id) {
data.cate_id = cate_id;
} else {
delete data.cate_id; // delete 用于删除对象的属性
}
if (state) {
data.state = state;
} else {
delete data.state;
}
// 重置页码为 1
data.pagenum = 1;
renderArticle(); // 调用renderArticle();渲染页面即可
})
# 处理common.js
当token过期了,需要跳转到登录页重新登录。
但是,从 index.html 跳转到 login.html ,路径是 ./login.html
从其他小页面跳转到 login.html ,路径是 ../login.html,而且应该让父页面跳转。
所以,当ajax请求完成后,判断如果是身份认证失败了。继续判断是哪个页面。
option.complete = function (xhr) {
var res = xhr.responseJSON;
if (res && res.status === 1 && res.message === '身份认证失败!') {
// 清除掉过期的token
localStorage.removeItem('token');
// 跳转到登录页 (location.pathname 表示url的路径部分)
if (location.pathname === '/index.html') {
location.href = './login.html';
} else {
window.parent.location.href = '../login.html';
}
}
// 其他错误
if (res && res.status === 1) {
layer.msg(res.message);
}
}