代码下载
完善服务器
对于上一篇博客开发的服务端项目,还需要增加文章列表查询
,文章删除
,文章更新
三个接口。
文章列表查询接口
验证表单数据
在路径 schema/user.js
中增加 articles 验证表单数据,其中使用的 .allow('')
表示允许空字符串:
articles: {
query: {
pagenum,
pagesize,
cate_id: joi.number().integer().min(1).allow('').optional(),
state: joi.string().valid('已发布', '草稿').allow('').optional()
}
},
处理函数
- 文章查询这里是根据
cate_id
和state
进行的多表条件查询,对相应参数进行有效判断而给出合适的sql
查询语句。 - 数量查询通过
select count (*/字段) from 表名
来进行。 - 分页查询通过
select * from 表名 limit 数值1, 数值2
来进行,其中 数值1 表示跳过前面多少条数据,数值2 表示取多少条数据。 - 排序通过
order by 字段 asc/desc
来进行。
// 查询文章
const articles = (req, res) => {
console.log('query: ', req.query);
// 跳过的条数
let num = (req.query.pagenum - 1)*req.query.pagesize;
// 查询文章的sql
let sql;
// 查询文章数量的sql
let numSql;
// 文章查询是根据参数按条件查询
if (typeof req.query.cate_id === 'number' && typeof req.query.state === 'string' && req.query.state.length > 0) {
sql = `select articles.*, article_cate.name cate_name, article_cate.alias cate_alias, article_cate.is_delete cate_is_delete from articles, article_cate where cate_id = ? and state = ? and articles.cate_id = article_cate.id order by pub_date desc limit ${num}, ${req.query.pagesize}`;
numSql = `select count (*) total from articles, article_cate where cate_id = ? and state = ? and articles.cate_id = article_cate.id`
} else if (typeof req.query.cate_id === 'number') {
sql = `select articles.*, article_cate.name cate_name, article_cate.alias cate_alias, article_cate.is_delete cate_is_delete from articles, article_cate where cate_id = ? and articles.cate_id = article_cate.id order by pub_date desc limit ${num}, ${req.query.pagesize}`;
numSql = `select count (*) total from articles, article_cate where cate_id = ? and articles.cate_id = article_cate.id`
} else if (typeof req.query.state === 'string' && req.query.state.length > 0) {
sql = `select articles.*, article_cate.name cate_name, article_cate.alias cate_alias, article_cate.is_delete cate_is_delete from articles, article_cate where state = ? and articles.cate_id = article_cate.id order by pub_date desc limit ${num}, ${req.query.pagesize}`;
numSql = `select count (*) total from articles, article_cate where state = ? and articles.cate_id = article_cate.id`
} else {
sql = `select articles.*, article_cate.name cate_name, article_cate.alias cate_alias, article_cate.is_delete cate_is_delete from articles, article_cate where articles.cate_id = article_cate.id order by pub_date desc limit ${num}, ${req.query.pagesize}`;
numSql = `select count (*) total from articles, article_cate where articles.cate_id = article_cate.id`
}
console.log('sql: ', sql);
console.log('numSq服务器托管网l: ', numSql);
let params = new Array();
console.log('init params: ', params);
if (typeof req.query.cate_id === 'number') {
params.push(req.query.cate_id);
}
if (typeof req.query.state === 'string' && req.query.state.length > 0) {
params.push(req.query.state);
}
console.log('param: ', params);
// 查询文章
db.query(sql, params, (err, result) => {
if (err) {
res.cc(err);
} else {
console.log('result: ', result);
// 查询文章数量
db.query(numSql, params, (e, r) => {
if (err) {
res.cc(err);
} else {
console.log('total result: ', r[0].total);
let data = {
data: result,
total: r[0].total
}
res.send({
status: 0,
data: data,
message: '文章列表获取成功!'
});
}
});
}
});
}
实现路由
在路径 router/article.js
中实现如下路由:
routor.post('/addArticle', uploads.single('cover_img'), expressJoi(schema.addArticle), handler.addArticle);
文章删除接口
文章删除的实现跟文章类别删除类似,具体实现见代码……
文章更新接口
文章更新的实现跟文章发布和用户信息跟新类似,具体实现见代码……
安装VSCode插件辅助开发
由于使用VSCode访问开发的项目页面使用的是 file 协议,file 协议在开发当中会有很多的问题,通常需要 http 协议来访问开发的 html 文件,Live Server
和 Express
这两个插件都可以做到。
Live Server
- 在插件市场,搜索 Live Server 并安装
- 在页面上鼠标右键,选择 Open With Live Server 即可快速使用 http 协议访问页面,点击下方 Port 按钮可以关闭
Express
由于 Live Server 在页面保存之后会自动刷新整个页面,在不需要自动刷新的时候就不是很方便了,这时 Express 就会比较合适。
- 在插件市场,搜索 Express 并安装
- 打开命令面板(F1,或者Windows和Linux上的Ctrl+Shift+P,或者OSX上的Shift+CMD+P)
- 输入或选择 “Express: Host current workspace and open in browser”
配置 ajaxPrefilter
在目录中新建 baseApi.js 文件,编写如下代码:
// 在每个请求之前被发送和$.ajax()处理它们前处理,设置自定义Ajax选项或修改现有选项。
let host = 'http://127.0.0.1/';
$.ajaxPrefilter(function(options) {
console.log('---ajaxPrefilter----');
// 统一设置请求头
if (options.url.indexOf('my/') === 0) {
options.headers = {
Authorization: localStorage.getItem('token')
}
}
// 在发起真正的 Ajax 请求之前,统一拼接请求的根路径
options.url = host + options.url;
console.log('url: ', options.url);
console.log('type: ', options.type);
console.log('headers: ', options.headers);
console.log('params: ', options.data);
// 统一挂载 complete 回调
options.complete = function(res) {
console.log('complete: ', res.responseJSON);
// 处理身份验证失败
if (res.responseJSON.status === 1 && res.responseJSON.message === '身份验证失败!') {
// 清除 token 跳到登录页面
localStorage.removeItem('token');
location.href = 'project/login.html';
}
}
});
登录注册
使用 layui
绘制页面,具体实现见代码……
表单的验证
导入 layui 的 js 文件,为需要验证的表单项添加 lay-verify 属性,同时指定具体的校验规则即可。
通过 layui.form.verify() 函数自定义校验规则:
// 表单验证
layui.form.verify({
username: [
/^[w]{2,20}$/,
'用户名必须为2~20位字母和数字!'
],
password: [
/^[S]{6,16}$/,
'密码必须为6~16位非空格字符!'
],
repassword: function(value) {
if ($('.signin [name=password]').val() !== value) {
return '两次输入的密码不一致!';
}
}
});
实现
登录注册切换:
// 去注册、去登录
$('.to-signin a').click(function(){
$('.login').hide();
$('.signin').show();
});
$('.to-login a').click(function(){
$('.signin').hide();
$('.login').show();
});
登录注册实现:
let layer = layui.layer;
// 登录
$('.login form').on('submit', function(e) {
// 阻止默认行为
e.preventDefault();
let params = {
// 属性选择器
username: $('.login [name=username]').val(),
password: $('.login [name=password]').val()
}
$.post('api/login', params, function(data) {
// 登录成功
if (data.status === 0) {
// 缓存token
localStorage.setItem('token', data.token);
// 跳首页
location.href = 'index.html'
}
layer.msg(data.message);
});
});
// 注册
$('.signin form').submit(function(e) {
// 阻止默认行为
e.preventDefault();
$('.to-login a').click();
let params = {
username: $('.signin [name=username]').val(),
password: $('.signin [name=password]').val()
}
$.post('api/signin',params, function(data) {
// 注册成功
if (data.status === 0) {
layer.msg('注册成功,请登录!');
// 切换到登录
$('.to-login a').click();
// 填充用户名
$('.login [name=username]').val(params.username);
// 获得焦点
$('.login [name=password]').focus();
} else {
layer.msg(data.message);
}
});
});
按需为表单项添加校验规则:
…
主页
使用 layui
绘制页面,具体实现见代码……
使用iframe标签在内容主体区域显示网页内容
在页面主体的 div 中添加 iframe :
为 首页 链接添加 href 和 target 属性:
首页
为 首页 对应的导航 Item 项添加 layui-this 属性选中此 Item:
首页
**禁止过渡动画,强制清除 链接的 CSS3 动画: **
a {
/* 禁止过渡动画 */
transition: none !important;
}
在目录中新建 index.js 文件,实现如下代码渲染用户头像:
// 用户信息数据
var userinfo = null;
// 渲染头像
function renderAvatar(user) {
let name = user.nickname || user.username;
$('#welcome').html(`欢迎${name}`);
if (user.user_pic) {
$('.layui-nav-img').attr('src', user.user_pic).show();
$('.text-avatar').hide();
} else {
// 渲染大写首字母
$('.text-avatar').html(name[0].toUpperCase()).show();
$('.layui-nav-img').hide();
}
// 渲染完成后,保存数据
userinfo = user;
}
获取用户的基本信息:
$(function(){
// 用户信息
getUserinfo();
});
// 请求用户信息数据
function getUserinfo() {
$.get('my/userinfo', function(data) {
if (data.status === 0) {
renderAvatar(data.data);
} else {
layer.msg(data.message);
}
})
}
实现退出功能:
$(function(){
// 退出
$('#logout').click(function() {
console.log('logout');
layer.confirm('是否退出?', {
icon: 3,
title: '提示'
}, function(index) {
console.log(index);
// 清除 token
localStorage.removeItem('token');
// 进入登录页
location.href = 'login.html';
// 关闭 询问框
layer.close(index);
});
});
});
基本资料
使用 layui
绘制页面,具体实现见代码……
获取用户的基本信息
在目录中新建 userinfo.js 文件,实现如下代码填充表单数据:
let form = layui.form;
var layer = layui.layer;
// 填充初始数据
// 通过 window.parent 调用父框架 属性、方法 必须在http协议中打开
form.val('userinfo', window.parent.userinfo);
}
中的子页面,如果想要调用父页面中的属性、方法,使用 window.parent 即可(但是必须http协议中打开)。
调用 layui.form.val()
可以快速为表单赋值。
实现表单的重置效果,阻止表单的默认重置行为,再重新为表单赋值即可:
// 重置对表单的修改
$('#reset-btn').click(function(e) {
// 阻止默认行为
e.preventDefault();
// 用原始数据重新填充表单
form.val('userinfo', window.parent.userinfo);
});
发起请求更新用户的信息,阻止表单的默认提交行为,并发起数据请求:
// 提交数据
$('.layui-form').submit(function(e) {
// 阻止默认行为
e.preventDefault();
let params = $(this).serialize();
$.post('my/userinfo', params, function(data) {
if (data['status'] === 0) {
// 调用父框架的方法重新获取数据(缺点就是需要重新请求网络)
// window.parent.getUserinfo();
// 通过修改父框架的数据重新渲染(不需要请求网络,但是需要拼接数据)
let userinfo = window.parent.userinfo
// 属性选择器
userinfo.nickname = $('.layui-form [name=nickname]').val();
userinfo.email = $('.layui-form [name=email]').val();
console.log('userinfo: ', userinfo);
window.parent.renderAvatar(userinfo);
} else {
layer.msg(data['message']);
}
});
});
重置密码
使用 layui
绘制页面,具体实现见代码……
发起请求实现重置密码的功能:
$('.layui-form').submit(function(e) {
// 阻止默认行为
e.preventDefault();
$.post('my/updatePassword', {
oldPassword: $('[name=oldPassword]').val(),
newPassword: $('[name=password]').val()
}, function(data) {
if (data.status === 0) {
$(this)[0].reset();
layui.layer.msg('修改密码成功!');
} else {
console.log('data: ', data);
layui.layer.msg(data.message);
}
});
});
使用 reset()
方法实现重置表单功能:
$('#reset-btn').on('click', function() {
$('.layui-form')[0].reset();
})
更换头像
使用 layui
绘制页面,具体实现见代码……
cropper 实现图片裁剪预览
在 中导入 cropper.css 样式表,在 的结束标签之前,按顺序导入如下的 js 脚本:
定义裁剪区与预览区的 html 结构:
100 x 100
50 x 50
实现基本裁剪效果:
// 获取裁剪区域的 DOM 元素
var $image = $('#image');
// 配置选项
const options = {
// 纵横比
aspectRatio: 1,
// 指定预览区域
preview: '.img-preview'
};
// 创建裁剪区域
$image.attr('src', window.parent.userinfo.user_pic).cropper(options);
更换裁剪的图片,首先拿到用户选择的文件;然后根据选择的文件,创建一个对应的 URL 地址;最后销毁 旧的裁剪区域,重新设置图片路径 ,创建新的裁剪区域:
let file = filelist[0];
console.log('file: ', file);
let url = URL.createObjectURL(file);
console.log('url: ', url);
$image.cropper('destroy') // 销毁旧的裁剪区域
.attr('src', url) // 重新设置图片路径
.cropper(options); // 重新初始化裁剪区域
读取文件,可以使用 URL.createObjectURL(file)
,还可以使用 FileReader
对象:
// 或者使用 FileReader 读取图片
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function() {
console.log('result: ', reader.result);
$image.cropper('destroy') // 销毁旧的裁剪区域
.attr('src', reader.result) // 重新设置图片路径
.cropper(options); // 重新初始化裁剪区域
};
将裁剪后的图片,输出为 base64 格式的字符串:
var dataURL = $image.cropper('getCroppedCanvas', { // 创建一个 Canvas 画布
width: 100,
height: 100
}).toDataURL('image/png'); // 将 Canvas 画布上的内容,转化为 base64 格式的字符串
将裁剪后的图片,输出为二进制数据对象:
$image.cropper('getCroppedCanvas', { // 创建一个 Canvas 画布
width: 400,
height: 280
}).toBlob(function(blob) {
console.log('image: ', blob);
});
将裁剪后的头像上传到服务器,更新父页面中的数据并渲染头像:
$.post('my/updateAvatar', {
user_pic: dataURL
}, function(data) {
if (data.status === 0) {
let userinfo = window.parent.userinfo;
userinfo.user_pic = dataURL;
window.parent.renderAvatar(userinfo);
layui.layer.msg('修改头像成功!');
} else {
layui.layer.msg(data.message);
}
});
文章分类
使用 layui
绘制页面,具体实现见代码……
使用模板引擎渲染表格的数据,导入模板引擎并定义模板:
{{each}}
{{$value.name}}
{{$value.alias}}