资源地址
- 线上 DEMO 项目地址:http://www.escook.cn:8086/
- 项目的 API 接口地址: https://www.showdoc.cc/escook?page_id=3707158761215217
- Layui : https://layui.itze.cn/index.html
- 项目的
API
接口文档地址: https://www.showdoc.cc/escook?page_id=3707158761215217
项目前期的准备工作
初始化项目结构
- 将
素材
目录下的assets
和home
文件夹,拷贝到code
目录下assets > css
文件夹 自己编写的css
代码assets > fonts
字体图标文件夹assets > images
存放图标文件夹assets > js
自己js
代码的文件夹assets > lib
第三方依赖的文件夹home > dashboard.html
后台首页的页面效果
- 在
code
目录下新建login.html
和index.html
页面
使用GitHub管理大事件的项目
- 在
code
目录中运行git init
命令 - 在
code
目录中运行git add .
命令 - 在
code
目录下运行git commit -m "init project"
命令 - 新建 Github 仓库
web_bigevent
- 将本地仓库和Github仓库建立关联关系
- 将本地仓库的代码推送到Github仓库中
- 运行
git checkout -b login
命令,创建并切换到login
分支
1.登录注册
1.登录和注册表单的切换
注册单击事件,切换表单点击了对应按钮,让对应的div进行显示,另外一个进行隐藏
// 1.点击注册,显示注册框,隐藏登录框
$("#link_reg").on("click", function () {
$(".reg-box").show();
$(".login-box").hide();
});
// 2.点击登录,显示注登录框,隐藏注册
$("#link_login").on("click", function () {
$(".reg-box").hide();
$(".login-box").show();
});
2.发起注册用户的Ajax请求
1实现登录表单的验证
-
在
layui
中,默认有帮我们验证表单元素的逻辑 -
导入
layui
的js
文件 -
为需要验证的表单项添加
lay-verify
属性,同时指定具体的校验规则即可。 -
并且可以添加多个校验规则,规则名用|隔开
verify()里面要传一个对象,属性名是校验规则名,属性值可以是一个回调函数,也可以是一个数组。
**function(value, item){ }//参数value:表单的值;参数item:表单的DOM对象
**数组的两个值分别代表:[正则匹配、匹配不符时的提示文字]
// 自定义表单校验规则
layui.form.verify({
// 规则的名称:规则定义
pass: [/^[\S]{6,12}$/, "密码必须6到12位,且不能出现空格"],
// 判断密码框和确认密码框的值是否一样
regpwd: function (value, item) {
var pwd = $("#form_reg input[name='password']").val();
if (pwd !== value) {
// value 使用此规则表单的value 的值
// item 使用此规则表单的元素
return "两次输入结果不一样";
}
},
});
2.发起注册用户的Ajax请求
查阅接口文档,关注以下几个重点信息
- 请求URL
- 请求方式
- 参数名
- 响应数据
/*
3.1)注册表单submit 事件
2).阻止表单默认行为
3)收集表单事件 (使用layui内置的表单验证功能)
4)发送ajax 请求
*/
$("#form_reg").on("submit", function (e) {
e.preventDefault();
console.log($(this).serialize());
$.ajax({
method: "post",
url: "/api/reguser",
data: $(this).serialize(),
success: function (res) {
console.log(res);
if (res.status !== 0) {
// return alert("注册失败");
return layui.layer.msg(res.message, { icon: 5 });
}
// alert("注册成功");
layui.layer.msg(res.message, { icon: 6 });
// 手动点击
$("#link_login").click();
},
});
});
3.发起登录的Ajax请求
- 查阅接口文档,关注几个重点要(请求URL,请求方式,请求参数,响应数据)
- 请求成功之后提示用户,保存token信息到本地存储,跳转到后台主页
token 用来标识用户是否登录的令牌。
后台的页面需要用户登录之后才能查阅,那么权限校验的机制也就出来了,需要检验权限的页面后台先判断请求头里面是否有token,以此来判断是否是登录状态。
// 登录功能
$("#form_login").on("submit", function (e) {
e.preventDefault(); // 阻止跳转
// 获取表单数据
var data = $(this).serialize();
console.log(data);
// 发送ajax请求
$.ajax({
method: "post",
url: "/api/login",
data: data,
success: function (res) {
console.log(res);
if (res.status !== 0) {
// 不通过时设置 layui.layer的提示信息
return layui.layer.msg(res.message, { icon: 5 });
}
// 保留用户的授权接口信息,后面操作数据时需要用
// 登录成功之后 ,返回的token数据保存到本地存储中,为以后调用其他接口准备
localStorage.setItem("token", res.token);
layui.layer.msg(res.message, { icon: 6 }, function () {
// 跳转到后台首页
location.href = "/index.html";
});
},
});
});
`$.ajax() > ajaxPrefilter过滤器 -> 发送请求给服务器`
jQuery.ajaxPrefilter()
函数用于指定预先处理Ajax参数选项的回调函数。- 在所有参数选项被
jQuery.ajax()
函数处理之前,你可以使用该函数设置的回调函数来预先更改任何参数选项。
$.ajaxPrefilter( function(options){
// options:(对象)当前AJAX请求的所有参数选项,包含:url、contentType等
options.url = ''; // 修改ajax请求的地址
});
// 通过ajax的预处理函数,给请求的url拼接根地址
// 发送ajax前进行配置的一些属性,url设置全局改变一处,所有跟随改变
$.ajaxPrefilter(function (options) {
// 拼接跟地址
// options ajax发送请求前配置的参数
options.url = "http://www.liulongbin.top:3007" + options.url;
// 给有权限的接口,设置请求头
// 判断当前的请求地址 , 是否需要验证(url 地址中是否包含'/my/')
if (options.url.indexOf("/my/") !== -1) {
options.headers = { Authorization: localStorage.getItem("token") };
}
});
4.提交login
分支的代码到GitHub
- 运行
git add .
命令 - 运行
git commit -m "完成了登录和注册的功能"
命令 - 运行
git push -u origin login
命令 (把本地的login分支推送到远程的login分支) - 运行
git checkout master
命令(切换到master分支) - 运行
git merge login
命令(把本地的login分支合并到master分支) - 运行
git push
命令(把本地的master分支推送到远程master分支) - 运行
git checkout -b index
命令(创建index分支,用于开发后台index页面)
5.iframe标签
![iframe标签的用法](https://gitee.com/shen-longfei001/image-bed/raw/master/ImageBed/202203061224939.png)
// 1.默认选中首页 layui-this
<li class="layui-nav-item layui-this">
<a href="/home/dashboard.html" target="fm"> <span class="iconfont icon-home"></span>首页 </a>
</li>
// 2.默认显示首页
<div class="layui-body">
<!-- 内容主体区域 -->
<iframe name="fm" src="/home/dashboard.html" frameborder="0"></iframe>
</div>
// 3.去掉动画,隐藏滚动条
.layui-body {
overflow: hidden;
}
a {
transition: none !important;
}
6.获取用户基本信息
- 定义一个
getUserInfo
函数(用来获取用户信息),当页面加载完毕之后调用这个函数 - 利用
$.ajax()
进行网络请求,查阅文档,获取关键信息 - 我们请求的时候就需要设置请求头信息,把我们获取到的
token
传递给后台 - 数据获取失败提示用户
// 调用获取用户信息的函数
getUserInfo();
// 1.封装获取用户信息的函数
function getUserInfo() {
$.ajax({
method: "get",
url: "/my/userinfo",
// 设置请求头携带 token 数据
// headers: { Authorization: localStorage.getItem("token") },
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
layui.layer.msg(res.message, { icon: 6 });
// 调用渲染用户信息的函数
renderAvatar(res.data);
},
});
}
7.渲染用户头像和名称
如果请求成功,我们根据服务器返回的数据来渲染页面
- 定义
renderAvatar
函数,接收服务器返回的用户数据 - 获取用户的名称
- 设置欢迎的文本,找到关键元素进行设置
- 按需渲染用户的头像,如果用户有头像,那么就直接设置图片头像,如果没有设置文本头像
// 1.1封装一个渲染用户信息的函数
function renderAvatar(user) {
console.log(user);
// 渲染欢迎语 ,如果有名称就显示名称,没有就显示原来的
var name = user.nickname || user.username;
$("#welcome").html(`欢迎 ${name}`);
// 判断用户是否有头像
if (user.user_pic) {
// 隐藏文字框,设置图片的src并显示
$(".text-avatar").hide();
$(".layui-nav-img").attr("src", user.user_pic);
} else {
// 隐藏头像,设置文字头像的内容大写并显示
$(".layui-nav-img").hide();
var first = name[0].toUpperCase();
$(".text-avatar").html(first);
}
}
8.统一为有权限的接口设置headers请求头
- 在
baseAPI
的ajaxPrefilter
中添加如下代码 - 判断
url
里面是否携带/my/
- 如果携带,那么我们就设置
options.headers
// 给有权限的接口,设置请求头
// 判断当前的请求地址 , 是否需要验证(url 地址中是否包含'/my/')
if (options.url.indexOf("/my/") !== -1) {
options.headers = { Authorization: localStorage.getItem("token") };
}
9.实现退出功能
- 给退出按钮绑定点击事件,取消
a
标签的默认行为 - 用户点击后,弹出提示框(
layui
中有弹出层的相关代码),如果用户点击确认 - https://www.layui.com/doc/modules/layer.html#layer.confirm
- 移除本地缓存的 token,并且跳转到登录页面
// 点击按钮,实现退出功能
$('#btnLogout').on('click', function() {
// 提示用户是否确认退出
layer.confirm('确定退出登录?', { icon: 3, title: '提示' }, function(index) {
// 1. 清空本地存储中的 token
localStorage.removeItem('token')
// 2. 重新跳转到登录页面
location.href = '/login.html'
// 关闭 confirm 询问框
layer.close(index)
})
})
10.控制用户的访问权限
在调用有权限接口的时候,指定complete
回调函数:
// 限制用户的访问权限(未登录直接强制跳转到登录页面)
// 不论成功还是失败,最终都会调用 complete 回调函数
options.complete = function (xhr) {
if (xhr.responseJSON.status == 1 && xhr.responseJSON.message === "身份认证失败!") {
// 强制清空 token 数据
localStorage.removeItem("token");
// 跳转到登录页
location.href = "/login.html";
}
};
11.优化权限控制的代码
将权限控制的代码,从每个请求中,抽离到 ajaxPrefilter
中:
// 通过ajax的预处理函数,给请求的url拼接根地址
// 发送ajax前进行配置的一些属性,url设置全局改变一处,所有跟随改变
$.ajaxPrefilter(function (options) {
// 拼接跟地址
// options ajax发送请求前配置的参数
options.url = "http://www.liulongbin.top:3007" + options.url;
// 给有权限的接口,设置请求头
// 判断当前的请求地址 , 是否需要验证(url 地址中是否包含'/my/')
if (options.url.indexOf("/my/") !== -1) {
options.headers = { Authorization: localStorage.getItem("token") };
}
// 限制用户的访问权限(未登录直接强制跳转到登录页面)
options.complete = function (xhr) {
if (xhr.responseJSON.status === 1 && xhr.responseJSON.message === "身份认证失败!") {
// 强制清空 token 数据
localStorage.removeItem("token");
// 跳转到登录页
location.href = "/login.html";
}
};
});
提交GIT代码
2.基本资料
隐藏的和只读的输入框
// 隐藏的输入框
<input type="hidden" name="id">
// 只读的不能修改值的输入框 readonly(只读) disabled(禁用)
<input type="text" name="username" required disabled class="layui-input" readonly />
1.获取用户的基本信息
// 1.获取渲染用户信息
initUserinfo();
function initUserinfo() {
$.ajax({
method: "get",
url: "/my/userinfo",
success: function (res) {
console.log(res);
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
// 快速为表单赋值
layui.form.val("formUserInfo", res.data);
},
});
}
2.使用form.val方法快速为表单赋值
// 为表单达快速赋值 layui.form.val()
<form class="layui-form" action="" lay-filter="formUserInfo">
</form>
// 为表单达快速赋值 ("表单名称",数据)
layui.form.val("formUserInfo", res.data);
3.发起请求更新用户的信息,验证昵称规则
// 2.验证昵称规则
layui.form.verify({
nickname: function (value) {
if (value.length > 6) return "昵称的长度不能超过六位";
},
});
// ------------------------------------
// 2.修改表单的数据
$(".layui-form").on("submit", function (e) {
e.preventDefault();
var data = $(this).serialize();
// 发起 ajax 数据请求
$.ajax({
method: "post",
url: "/my/userinfo",
data: data,
success: function (res) {
if (res.status != 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
layui.layer.msg(res.message, { icon: 6 });
// 修改完表单数据自动更新欢迎语
// 调用父页面index.js 中的全局函数getUserInfo
// user_info.html 和 indexedDB.html 是父子关系,需要调用父类的方法
// 把index.html 中的获取信息和渲染信息提到全局作用域中
// 调用父页面中的方法,重新渲染用户的头像和用户的信息
window.parent.getUserInfo();
},
});
});
注意:<iframe>
中的子页面,如果想要调用父页面中的方法,使用 window.parent
即可。
3.重置密码
1.为密码框定义校验规则
定义如下的三个校验规则:
// 验证密码的格式(6-12)
layui.form.verify({
pwd: [/^\S{6,12}$/, "密码必须是6到12位非空字符"],
// 新密码不能和原始密码一样
samPwd: function (value) {
var oldPwd = $("input[name=oldPwd]").val().trim();
console.log(oldPwd);
if (value === oldPwd) {
return "新旧密码不能一样";
}
},
// 新密码和确认新密码一致的规则
rePwd: function (value) {
var newPwd = $("input[name=newPwd]").val().trim();
if (newPwd !== value) {
return "两次密码不一致";
}
},
});
2.发起请求实现重置密码的功能
// 修改密码重置
$(".layui-form").on("submit", function (e) {
e.preventDefault();
let data = $(this).serialize();
$.ajax({
method: "post",
url: "/my/updatepwd",
data: data,
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
layui.layer.msg(res.message, { icon: 6 });
// 重置表单,reset只能用于DOM元素[0]
$(".layui-form")[0].reset();
},
});
});
4.更换头像
- 通过 accept 属性,可以指定,允许用户选择什么类型的文件
// 通过 accept 属性,可以指定,允许用户选择什么类型的文件
<input type="file" id="file" accept="image/png,image/jpeg" />
- 获取拿到用户选择的文件
e.target.files
function (e)
// 获取用户选择的文件
var filelist = e.target.files;
// 拿到用户选择的文件
var file = e.target.files[0];
- 将文件转化为文件路径 ( URL.createObjectURL )
- 减少向服务器发送请求次数
var newImgURL = URL.createObjectURL(file)
1.实现裁剪区域图片的替换
实现基本裁剪效果:
// 1.1 获取裁剪区域的 DOM 元素
var $image = $('#image')
// 1.2 配置选项
const options = {
// 纵横比
aspectRatio: 1,
// 指定预览区域
preview: '.img-preview'
}
// 1.3 创建裁剪区域
$image.cropper(options)
- 单击上传按钮弹出文件选择框
// 单击上传按钮弹出文件选择框
$("#btnChooseImage").on("click", function () {
$("#file").click();
});
- 为文件选择框绑定 change 事件
// 为文件选择框绑定 change 事件
$("#file").on("change", function (e) {
// 获取用户选择的文件
var filelist = e.target.files;
if (filelist.length === 0) {
return layer.msg("请选择照片!");
}
// 1. 拿到用户选择的文件
var file = e.target.files[0];
// 2. 将文件,转化为路径
var imgURL = URL.createObjectURL(file);
// 3. 重新初始化裁剪区域
$image
.cropper("destroy") // 销毁旧的裁剪区域
.attr("src", imgURL) // 重新设置图片路径
.cropper(options); // 重新初始化裁剪区域
});
2.将裁剪后的头像上传到服务器
将裁剪后的图片,输出为 base64 格式的字符串
// 为确定按钮,绑定点击事件
$('#btnUpload').on('click', function() {
// 1. 要拿到用户裁剪之后的头像
var dataURL = $image
.cropper('getCroppedCanvas', {
// 创建一个 Canvas 画布
width: 100,
height: 100
})
.toDataURL('image/png') // 将 Canvas 画布上的内容,转化为 base64 格式的字符串
// 2. 调用接口,把头像上传到服务器
$.ajax({
method: 'POST',
url: '/my/update/avatar',
data: {
avatar: dataURL
},
success: function(res) {
if (res.status !== 0) {
return layer.msg('更换头像失败!')
}
layer.msg('更换头像成功!')
window.parent.getUserInfo()
}
})
})
本地代码推送到 Git Hub
user
分支推送到 Git Hub 上git push -u origin user
user
分支和master
合并git merge user
master
推送到 Git Hub 上git push
5.文章分类
1.获取并使用模板引擎渲染表格的数据(查)
-
在页面底部导入模板引擎:
<script src="/assets/lib/template-web.js"></script>
-
定义模板:
<!-- 表格数据的模板 -->
<script type="text/html" id="tpl-cate">
{{each data}}
<tr>
<td>{{$value.name}}</td>
<td>{{$value.alias}}</td>
<td>
<button type="button" class="layui-btn layui-btn-xs btn-edit" data-id='{{$value.Id}}'>编辑</button>
<button type="button" class="layui-btn layui-btn-danger layui-btn-xs btn-delete" data-id='{{$value.Id}}'>删除</button>
</td>
</tr>
{{/each}}
</script>
- 发起请求获取数据:
//!【1】封装一个获取分类数据的函数,渲染到页面 (查)
initArtCateList();
function initArtCateList() {
$.ajax({
url: "/my/article/cates",
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
console.log(res);
// layui.layer.msg(res.message, { icon: 6 });
// 调用模版
var htmlStr = template("tpl-cate", res);
// 数据渲染到页面
$("tbody").html(htmlStr);
},
});
}
2.点击按钮弹出模态框,添加分类 (增)
- 点击按钮弹出模态框
// 定义全局变量.保存 添加分类 模态框的索引
var indexAdd;
$("#btnAddCate").on("click", function () {
indexAdd = layer.open({
type: 1,
title: "类别添加",
area: ["500px", "300px"],
// 模版内容拿到设为 content 的值
content: $("#dialog-add").html(),
});
});
- 获取表单数据,添加分类
// 添加分类的表单是通过模态框渲染到页面的
// 通过事件委托动态给表单祖册 submit 事件
$("body").on("submit", "#form-add", function (e) {
e.preventDefault();
// 获取表单数据
let data = $(this).serialize();
console.log(data);
// 发送ajax请求
$.ajax({
method: "post",
url: "/my/article/addcates",
data: data,
success: function (res) {
console.log(res);
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
// layui.layer.msg(res.message, { icon: 6 });
// 关闭模态框
layui.layer.close(indexAdd);
// 重新渲染页面
initArtCateList();
},
});
});
3.点击按钮弹出模态框,编辑分类 (改)
- 点击按钮弹出模态框,点击事件委托给tbody
// 定义关闭模态框的索引
var indexEdit;
// 1.点击按钮弹出模态框,点击事件委托给tbody
$("tbody").on("click", ".btn-edit", function () {
indexEdit = layer.open({
type: 1,
title: "类别添加",
area: ["500px", "300px"],
// 模版内容拿到设为 content 的值
content: $("#dialog-edit").html(),
});
// 获取分类的id,获取分类的数据
let id = $(this).attr("data-id");
// 发送ajax请求渲染页面数据
$.ajax({
url: "/my/article/cates/" + id,
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
// layui.layer.msg(res.message, { icon: 6 });
// 给表单赋值(layui中可以一键赋值);(要赋值的表单,数据)
layui.form.val("form-edit", res.data);
},
});
});
- 编辑的表单赋值(事件委托body)
$("body").on("submit", "#form-edit", function (e) {
e.preventDefault();
// 获取表单数据
let data = $(this).serialize();
$.ajax({
method: "post",
url: "/my/article/updatecate",
data: data,
success: function (res) {
console.log(res);
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
layui.layer.msg(res.message, { icon: 6 });
// 关闭模态框
layui.layer.close(indexEdit);
// 数据编辑成功重新渲染页面
initArtCateList();
},
});
});
4.删除分类 (删)
- 给删除按钮注册 click 事件,事件委托
$("body").on("click", ".btn-delete", function () {
// 获取分类的id,获取分类的数据
let id = $(this).attr("data-id");
// 询问是否确定要删除
layer.confirm("确定要删除?", { icon: 3, title: "提示" }, function (index) {
$.ajax({
url: "/my/article/deletecate/" + id,
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
layui.layer.msg(res.message, { icon: 6 });
// 数据删除成功重新渲染页面
initArtCateList();
},
});
// 关闭弹框
layer.close(index);
});
});
6.发布文章
1.渲染文章类别对应的下拉选择框结构
- 获取分类数据(封装一个函数并调用)
// 1.获取分类数据(封装一个函数并调用)
initCate();
function initCate() {
$.ajax({
url: "/my/article/cates",
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.status, { icon: 5 });
}
// 2.渲染数据到下拉列表
var htmlStr = template("tpl-cate", res);
$("select").html(htmlStr); // 更新渲染表单元素(因为是动态插入到页面的)
// 更新渲染表单元素(因为是动态插入到页面的)
layui.form.render();
},
});
}
- 定义列表数据的模板结构
<!-- 定义列表数据的模板结构 -->
<script type="text/html" id="tpl-cate">
<select name="cate_id" lay-verify="required">
{{each data }}
<option value="{{$value.Id}}">{{$value.name}}</option>
{{/each}}
</select>
</script>
2.渲染封面裁剪区域
参考 富文本和封面.pdf
//! 【2】初始化富文本编辑器
initEditor();
//! 【3】 初始化图片裁剪区(插件)
// 1. 初始化图片裁剪器
var $image = $("#image");
// 2. 裁剪选项
var options = {
aspectRatio: 400 / 280,
preview: ".img-preview",
};
// 3. 初始化裁剪区域
$image.cropper(options);
3.更换裁剪区域的图片
- 单击"选择封面",弹出文件选择框
// 1.单击"选择封面",弹出文件选择框
$("#btnChooseImage").on("click", function () {
// 隐藏的文件框打开
$("#coverFile").click();
});
- 监听
coverFile
的change
事件,获取用户选择的文件列表:
// 用户选择了新文件则更换裁剪区
$("#coverFile").on("change", function () {
// 获取选中的文件列表
let fileList = this.files;
// 判断有没有选择文件
if (fileList.length <= 0) {
return "没有选中文件";
}
// 获取到选择的文件
let file = fileList[0];
// 根据选择的文件,创建一个对应的 URL 地址:
let newImgURL = URL.createObjectURL(file);
$image
.cropper("destroy") // 销毁旧的裁剪区域
.attr("src", newImgURL) // 重新设置图片路径
.cropper(options); // 重新初始化裁剪区域
});
4.定义文章的发布状态
// 全局变量,保存转态的值
var state = "已发布";
$("#btnSave2").on("click", function () {
state = "草稿";
});
5.基于Form表单创建FormData对象
- 基于Form表单创建FormData对象,绑定submit事件
$("#form-pub").on("submit", function (e) {
e.preventDefault();
// 1.收集数据(FormData对象)
// var fd = new FormData(form表单dom对象);
var fd = new FormData(this);
// 2.追加文章转态的数据
fd.append("state", state);
// 3.裁剪封面,转成二进制文件,追加到fd中
$image
.cropper("getCroppedCanvas", {
// 创建一个 Canvas 画布
width: 400,
height: 280,
})
.toBlob(function (blob) {
// 将 Canvas 画布上的内容,转化为文件对象
// 得到文件对象后,进行后续的操作
// 裁剪完毕后,调用此回调函数,参数blob就是裁剪后的二进制文件
fd.append("cover_img", blob);
// fd.forEach(function (value, item) {
// console.log(item, value);
// });
// 调用发布文章的方法;
publishArticle(fd);
});
});
6.发起Ajax请求实现发布文章的功能
- 定义一个发布文章的方法:
function publishArticle(fd) {
$.ajax({
method: "post",
url: "/my/article/add",
data: fd,
// 注意:如果向服务器提交的是 FormData 格式的数据,
// 必须添加以下两个配置项
contentType: false,
processData: false,
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
// 提示框关闭后自哦动自动执行此函数
// 跳转到文章列表
// 获取父页面index.html中“文章列表”按钮标签
layui.layer.msg(res.message, { icon: 6 }, function () {
window.parent.$("#article-list").click();
// 发布文章成功后,跳转到文章列表页面
// location.href = "/article/art_list.html";
});
},
});
}
7.将开发完成的项目代码推送到GitHub
- 运行
git add .
命令 - 运行
git commit -m "完成文章管理相关功能的开发"
命令 - 运行
git push -u origin article
命令 - 运行
git checkout master
命令 - 运行
git merge article
命令 - 运行
git push
命令
7.文章列表
1.获取文章数据,展示到页面(查)
-
封装一个函数,获取文章数据
-
调用函数
// 定义一个全局变量,保存参数
var p = {
pagenum: 1, // 页码值,默认请求第一页的数据
pagesize: 2, // 每页显示多少条数据
cate_id: "", // 文章分类的 Id
state: "", // 文章的状态
};
getArticeList();
function getArticeList() {
$.ajax({
url: "/my/article/list",
data: p,
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
var htmlStr = template("tpl-list", res);
$("tbody").html(htmlStr);
renderPage(res.total);
},
});
}
- 把文章渲染到页面(模版引擎)
<!-- 列表模版 -->
<script type="text/html" id="tpl-list">
{{each data}}
<tr>
<td>{{$value.title}}</td>
<td>{{$value.cate_name}}</td>
<td>{{$value.pub_date|dataFormat}}</td>
<td>{{$value.state}}</td>
<td>
<button type="button" class="layui-btn layui-btn-xs btn-edit" article_id="{{$value.Id}}">编辑</button>
<button type="button" class="layui-btn layui-btn-danger layui-btn-xs btn-delete" data-id="{{$value.Id}}">删除</button>
</td>
</tr>
{{/each}}
</script>
2.定义过滤器函数,对发布时间进行格式化
template.defaults.imports.dataFormat = function (date) {
const dt = new Date(date);
var y = dt.getFullYear();
var m = padZero(dt.getMonth() + 1);
var d = padZero(dt.getDate());
var hh = padZero(dt.getHours());
var mm = padZero(dt.getMinutes());
var ss = padZero(dt.getSeconds());
return y + "-" + m + "-" + d + " " + hh + ":" + mm + ":" + ss;
};
// 定义补零的函数
function padZero(n) {
return n > 9 ? n : "0" + n;
}
3.根据文章的分类和转态筛选数据
- 把所有数据展示到下拉列表中
initCate();
function initCate() {
$.ajax({
url: "/my/article/cates",
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
var htmlStr = template("tpl-cate", res);
$("select[name=cate_id]").html(htmlStr);
layui.form.render();
},
});
}
- 给筛选表单,绑定submit提交事件
$("#form-search").on("submit", function (e) {
e.preventDefault();
// 获取分类的id ,获取文章的转态
var cate_id = $("[name=cate_id]").val();
var state = $("[name=state]").val();
// 修改查询参数对象 p
p.cate_id = cate_id;
p.state = state;
// 调用函数获取文章
getArticeList();
});
4.渲染分页按钮
function renderPage(total) {
layui.laypage.render({
elem: "pageBox", // 存放按钮的容器,没有#
count: total, // 数据的总量
limit: p.pagesize, // 每页显示的条数
curr: p.pagenum, // 当前是第几页
// 自定义页面按钮的排版
layout: ["page", "next", "prev", "count", "limit", "skip"],
limits: [2, 4, 6, 8, 10], // 每页显示条数的选项
// 1)当第一次渲染分页按钮时,jump回调函数就会被调用一次
// 2)当切换分页时,jump 回调函数就会被执行
jump: function (obj, first) {
// obj.分页的配置对象,(obj.limit, obj.curr 当前点击的页码)
// first(true,undefined)
// 判断一下是否被点击,避免无限循环
if (!first) {
// 获取当前的最新页码,更改查询参数,调用函数重新获取文章数据
p.pagenum = obj.curr;
// 获取当前最新的每页的条数,更改查询参数,调用函数重新获取文章数据
p.pagesize = obj.limit;
getArticeList();
}
},
});
}
5.删除文章(删)
1.给删除按钮绑定click事件(事件委托)
2.获取文章id
3.询问
4.调接口删除文章
5.重新渲染(判断当前是否在第一页)
$("tbody").on("click", ".btn-delete", function () {
var id = $(this).attr("data-id");
// 单击删除按钮时,统计删除按钮的数量,并保存
var len = $(".btn-delete").length;
layer.confirm("是否要删?", { icon: 3, title: "提示" }, function (index) {
$.ajax({
url: "/my/article/delete/" + id,
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
layui.layer.msg(res.message, { icon: 6 });
// 判断一下是否是当前页最后一条数据
if (len === 1) {
// 大于一页的时候再减
p.pagenum > 1 && p.pagenum--;
}
getArticeList();
},
});
// 关闭提示框
layer.close(index);
});
});
6.单击编辑按钮跳到编辑页面
// 1.编辑按钮是动态添加的,用事件委托
$("tbody").on("click", ".btn-edit", function () {
// 2.存储文章的id 本地存储
localStorage.setItem("article_id", $(this).attr("article_id"));
// 3.跳转到编辑页
location.href = "/article/art_edit.html";
});
8.文章编辑
1.页面初始化操作(查)
- 分类的下拉列表
initCate();
function initCate() {
// 获取所有分类数据
$.ajax({
url: "/my/article/cates",
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
// 渲染数据到下拉列表
var htmlStr = template("tpl-cate", res);
// console.log(htmlStr);
$("select").html(htmlStr);
// 更新渲染表单元素(因为是动态插入到页面的)
layui.form.render();
},
});
}
-
初始化富文本编辑器
-
初始化裁剪区域
// 2.初始化富文本编辑器
initEditor();
// 初始化图片裁剪区(插件)
// 1. 初始化图片裁剪器
var $image = $("#image");
// 2. 裁剪选项
var options = {
aspectRatio: 400 / 280,
preview: ".img-preview",
};
// 3. 初始化裁剪区域
$image.cropper(options);
2.根据id获取文章数据,并渲染到表单中
geteditData();
function geteditData() {
$.ajax({
url: "/my/article/" + id,
success: function (res) {
console.log(res);
if (res.status !== 0) {
return layui.layer.msg(res, message, { icon: 5 });
}
// 拼接服务器中的图片地址
$image
.cropper("destroy") // 销毁旧的裁剪区域
.attr("src", "http://www.liulongbin.top:3007" + res.data.cover_img) // 重新设置图片路径
.cropper(options); // 重新初始化裁剪区域
// 3).给表单赋值(layui中可以一键赋值)
layui.form.val("form-edit", res.data);
},
});
}
3.获取表单数据,上传服务器
// 点击选择封面上传文件框打开
$("#btnChooseImage").on("click", function () {
$("#coverFile").click();
});
// 用户选择了新文件则更换裁剪区
$("#coverFile").on("change", function () {
// 获取选中的文件列表
var fileList = this.files;
// 判断有没有选择文件
if (fileList.length <= 0) {
return layui.layer.msg("请选择文件", { icon: 5 });
}
var file = fileList[0];
// 根据选择的文件,创建一个对应的 URL 地址:
var newImgURL = URL.createObjectURL(file);
$image
.cropper("destroy") // 销毁旧的裁剪区域
.attr("src", newImgURL) // 重新设置图片路径
.cropper(options); // 重新初始化裁剪区域
});
获取数据
$("#form-edit").on("submit", function (e) {
e.preventDefault();
// 收集数据(FormData对象)
// var fd = new FormData(form表单dom对象);
var fd = new FormData(this);
$image
.cropper("getCroppedCanvas", {
// 创建一个 Canvas 画布
width: 400,
height: 280,
})
.toBlob(function (blob) {
// 将 Canvas 画布上的内容,转化为文件对象
// 得到文件对象后,进行后续的操作
// 裁剪完毕后,调用此回调函数,参数blob就是裁剪后的二进制文件
// 裁剪文章图片,并追加到fd 中
fd.append("cover_img", blob);
fd.forEach(function (value, item) {
console.log(item, value);
});
ediltlishArticle(fd);
});
});
封装一个调用发布文章的接口
// 封装一个调用发布文章的接口
function ediltlishArticle(fd) {
$.ajax({
method: "post",
url: "/my/article/edit",
data: fd,
// 数据是FormData对象需要配置两个额外参数
contentType: false,
processData: false,
success: function (res) {
if (res.status !== 0) {
return layui.layer.msg(res.message, { icon: 5 });
}
layui.layer.msg(res.message, { icon: 6 }, function () {
// 提示框关闭后自哦动自动执行此函数
// 跳转到文章列表
// 获取父页面index.html中“文章列表”按钮标签
window.parent.document.getElementById("article-list").click();
});
},
});
}