- A+
6. layui大事件项目
文件位置:1.Node.js零基础入门教程node.js—资料day8素材大事件项目
在assets/js/baseAPI.js
中修改统一请求根路径
6.1 调整接口名称
纯粹是因为之前本人手贱,故意修改名称与端口所致的。
在assets/js/login.js
中修改注册的post接口
6.2 express启动
先在VSCode的扩展中加上express插件先
接着启动之前的api_server
后端项目
nodemon app.js
然后再回到前端项目,按住ctrl+shift+p
调出Express: Host Current Workspace and Open in Browser
6.3 最终效果
输入我的数据库信息(因人而异):
- 账号:ks
- 密码:123456
6.4 Layui布局问题
6.4.1 文章类别列表删除失效问题
打开assets/js/article/art_cate.js
,找到删除文章类别的异步处理函数,通过console.log控制台打印命令发现,之所以不能删除是因为没有返回status,没返回它则是因为找不到id,因为它返回为空。
而造成这个问题的原因则是因为前端找不到这个属性名ID,但我们数据库写的是id。
aricle/art_cate.html
6.4.2 文章类别列表的编辑失效问题
因为上一节的更新文章类别的校验规则对象body参数里面定义了我们数据库的id为Id,但是我们的前端只能找数据库的存在的信息,而不是跑去后端验证,所以出现跟上面一样的问题。
更改schema/artcate.js
的更新分类校验对象。
// 校验规则对象 - 更新分类 exports.update_cate_schema = { body: { id, name, alias, }, }
更改router_handler/artcate.js
里面的更新文章处理分类对象的body校验对象的Id为id。
// 更新文章分类的处理函数 exports.updateCateById = (req, res) => { // const sql = `select * from ev_article_cate where name=? or alias=?` const sql = `select * from ev_article_cate where id != ? and (name=? or alias=?)` // 执行查重操作 db.query( sql, [req.body.id, req.body.name, req.body.alias], (err, results) => { // 执行 SQL 语句失败 if (err) return res.cc(err) // 判断 分类名称 和 分类别名 是否被占用 if (results.length === 2) return res.cc('分类名称与别名被占用,请更换后重试!') if ( results.length === 1 && results[0].name === req.body.name && results[0].alias === req.body.alias ) return res.cc('分类名称与别名被占用,请更换后重试!') if (results.length === 1 && results[0].name === req.body.name) return res.cc('分类名称被占用,请更换后重试!') if (results.length === 1 && results[0].alias === req.body.alias) return res.cc('分类别名被占用,请更换后重试!') // 更新文章分类 const sql = `update ev_article_cate set ? where id=?` db.query(sql, [req.body, req.body.id], (err, results) => { // 执行 SQL 语句失败 if (err) return res.cc(err) // console.log(results) // SQL 语句执行成功,但是影响行数不等于 1 if (results.affectedRows !== 1) return res.cc('更新文章分类失败!') // 更新文章分类成功 res.cc('更新文章分类成功!', 0) }) } ) // res.send('ok') }
之后通过测试软件进行接口测试是没问题,这里就懒得展示了。
但是即便这点击编辑后,弹出修改表单窗口也没多大的关系,它会提示Id必须为Number,这就很奇怪了。
回到我们前端的编辑问题上,编辑要依靠id绑定了btn-edit
和form-edit
经过的接口有两个,第一个是根据id获取文章分类,绑定点击事件click的异步操作,看到之前把$value.Id
改为了$value.id
,控制台显示的data数据没有问题。
可是当点击修改弹窗的修改表单时,即到第二个接口updatacate
,控制台打印的信息却是:
这就很令人困惑了,明明都通过接口测试了,控制台却说没把第一个接口的data.id
值传给了第二个接口?也许真的没有传到,查看修改类别的this对象,可以看到控制台输出的data值没有id,而是Id。
这也正好说明了之前Id必须为Number的怪相。
后面在aricle/art_cate.html
的form表单修改隐藏域的Id为id即可:
后面拿个alter也可以看到数据没问题了
6.4.3 文章发布的类别下拉框问题
方案一:改用input(垃圾的方案)
在文章发布那块,使用layui的select组件后,尽管能够方便下拉显示数据库中文章分类的内容,但是无论怎么填写都会提示这里是必填项,要么选择去掉select,要么改成input项,完全不清楚发生了什么bug?
方案二:改Id为id。
在art_pub.html
。
6.4.4 文章列表不能显示分类的内容
原因:因为数据库本身的数据都没有分类名,只有分类id而已。
art_list.html
6.5 改进页面
6.5.1 注册、重置密码页面添加密码强度
1) 重置密码页面
效果
代码
拿来博客:注册密码/修改密码之密码强度判断
user/user_pwd.html
点击查看代码
<!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/user/user_pwd.css" /> </head> <body> <!-- 卡片区域 --> <div class="layui-card"> <div class="layui-card-header">修改密码</div> <div class="layui-card-body"> <form class="layui-form"> <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" for="psd">新密码</label> <div class="layui-input-block"> <input onKeyUp="javascript:passwordChangeStatuss(this.value);" onBlur="javascript:passwordChangeStatuss(this.value);" type="password" name="newPwd" required lay-verify="required|pwd|samePwd" placeholder="请输入新密码" autocomplete="off" class="layui-input" /> <span class="psdInfo"></span> </div> <!-- <div class="strong"> <p class="fl"> <span class="hover">弱</span> <span class="">中</span> <span class="">强</span> </p> </div> --> </div> <div class="layui-form-item"> <label class="layui-form-label">密码强度</label> <div class="layui-btn-group pwd-item"> <label id="l" class="layui-btn layui-btn-primary" >低</label > <label id="m" class="layui-btn layui-btn-primary" >中</label > <label id="h" class="layui-btn layui-btn-primary" >高</label > </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" /> <span class="psd1Info"></span> </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> <!-- 导入 layui 的 js --> <script src="/assets/lib/layui/layui.all.js"></script> <!-- 导入 jQuery --> <script src="/assets/lib/jquery.js"></script> <!-- 导入 baseAPI --> <script src="/assets/js/baseAPI.js"></script> <!-- 导入自己的 js --> <script src="/assets/js/user/user_pwd.js"></script> </body> </html>
user_pwd.js
:
点击查看代码
$(function () { var form = layui.form form.verify({ pwd: [/^[S]{6,12}$/, '密码必须6到12位,且不能出现空格'], samePwd: function (value) { if (value === $('[name=oldPwd]').val()) { return '新旧密码不能相同!' } }, rePwd: function (value) { if (value !== $('[name=newPwd]').val()) { return '两次密码不一致!' } }, }) $('.layui-form').on('submit', function (e) { e.preventDefault() $.ajax({ method: 'POST', url: '/my/updatepwd', data: $(this).serialize(), success: function (res) { if (res.status !== 0) { return layui.layer.msg('更新密码失败!') } layui.layer.msg('更新密码成功!') // 重置表单 $('.layui-form')[0].reset() }, }) }) }) //密码强度判断 function passwordChangeStatuss(pwd) { if (pwd == '' || pwd == null) { $('.pwd-item label').attr('class', 'layui-btn layui-btn-primary') } else { S_level = checkStrong(pwd) switch (S_level) { case 0: $('.pwd-item label').attr( 'class', 'layui-btn layui-btn-primary' ) case 1: $('#l').attr('class', 'layui-btn layui-btn-danger') $('#m').attr('class', 'layui-btn layui-btn-primary') $('#h').attr('class', 'layui-btn layui-btn-primary') break case 2: $('#l').attr('class', 'layui-btn layui-btn-danger') $('#m').attr('class', 'layui-btn layui-btn-warm') $('#h').attr('class', 'layui-btn layui-btn-primary') break default: $('#l').attr('class', 'layui-btn layui-btn-danger') $('#m').attr('class', 'layui-btn layui-btn-warm') $('#h').attr('class', 'layui-btn') } } } //判断输入密码的类型 function CharMode(iN) { if (iN >= 48 && iN <= 57) //数字 return 1 if (iN >= 65 && iN <= 90) //大写 return 2 if (iN >= 97 && iN <= 122) //小写 return 4 else return 8 } //bitTotal函数 //计算密码模式 function bitTotal(num) { modes = 0 for (i = 0; i < 4; i++) { if (num & 1) modes++ num >>>= 1 } return modes } //返回强度级别 function checkStrong(sPW) { if (sPW.length <= 8) return 0 //密码太短 Modes = 0 for (i = 0; i < sPW.length; i++) { //密码模式 Modes |= CharMode(sPW.charCodeAt(i)) } return bitTotal(Modes) }
2) 注册页面
效果
代码
assets/login.css
点击查看代码
html, body { margin: 0; padding: 0; height: 100%; width: 100%; background: url('/assets/images/login_bg.jpg') no-repeat center; background-size: cover; } .loginAndRegBox { width: 400px; height: 350px; background-color: #fff; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); } .title-box { height: 60px; background: url('/assets/images/login_title.png') no-repeat center; } .reg-box { display: none; } .layui-form { padding: 0 30px; } .links { display: flex; justify-content: flex-end; } .links a { font-size: 12px; } .layui-form-item { position: relative; } .layui-icon { position: absolute; left: 10px; top: 10px; } .layui-input { padding-left: 32px; } .warn{ display: inline-block; width:22px; height:22px; background: url("../images/paywarn.png"); background-repeat: no-repeat; background-size:22px 22px; vertical-align: top; }
login.html
点击查看代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>LinFeng后台-登录/注册</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="【nodejs】大事件后台管理系统(四)——layui前端布局与改进" alt="" /> </div> <!-- 登录注册区域 --> <div class="loginAndRegBox"> <div class="title-box"></div> <!-- 登录的div --> <div class="login-box"> <!-- 登录的表单 --> <form class="layui-form" id="form_login"> <!-- 用户名 --> <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|pwd" placeholder="请输入密码" autocomplete="off" class="layui-input" /> </div> <!-- 登录按钮 --> <div class="layui-form-item"> <!-- 注意:表单提交按钮和普通按钮的区别,就是 lay-submit 属性 --> <button class="layui-btn layui-btn-fluid layui-btn-normal" lay-submit > 登录 </button> </div> <div class="layui-form-item links"> <a href="javascript:;" id="link_reg">去注册账号</a> </div> </form> </div> <!-- 注册的div --> <div class="reg-box"> <!-- 注册的表单 --> <form class="layui-form" id="form_reg"> <!-- 用户名 --> <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 onKeyUp="javascript:passwordChangeStatuss(this.value);" onBlur="javascript:passwordChangeStatuss(this.value);" type="password" name="password" required lay-verify="required|pwd" placeholder="请输入密码" autocomplete="off" class="layui-input" /> </div> <div class="layui-form-item"> <label class="layui-form-label" ><i class="warn"></i>密码强度</label > <div class="layui-btn-group pwd-item"> <label id="l" class="layui-btn layui-btn-primary" >低</label > <label id="m" class="layui-btn layui-btn-primary" >中</label > <label id="h" class="layui-btn layui-btn-primary" >高</label > </div> </div> <!-- 密码确认框 --> <div class="layui-form-item"> <i class="layui-icon layui-icon-password"></i> <input type="password" name="repassword" required lay-verify="required|pwd|repwd" placeholder="再次确认密码" autocomplete="off" class="layui-input" /> </div> <!-- 注册按钮 --> <div class="layui-form-item"> <!-- 注意:表单提交按钮和普通按钮的区别,就是 lay-submit 属性 --> <button class="layui-btn layui-btn-fluid layui-btn-normal" lay-submit > 注册 </button> </div> <div class="layui-form-item links"> <a href="javascript:;" id="link_login">去登录</a> </div> </form> </div> </div> <!-- 导入 Layui 的JS文件 --> <script src="/assets/lib/layui/layui.all.js"></script> <!-- 导入 JQuery --> <script src="/assets/lib/jquery.js"></script> <!-- 导入自己封装的 baseAPI.js --> <script src="/assets/js/baseAPI.js"></script> <!-- 导入自己的 JavaScript 脚本 --> <script src="/assets/js/login.js"></script> </body> </html>
assets/js/login.js
:
点击查看代码
$(function () { // 点击“去注册账号”的链接 $('#link_reg').on('click', function () { $('.login-box').hide() $('.reg-box').show() }) // 点击“去登录”的链接 $('#link_login').on('click', function () { $('.login-box').show() $('.reg-box').hide() }) // 从 layui 中获取 form 对象 var form = layui.form var layer = layui.layer // 通过 form.verify() 函数自定义校验规则 form.verify({ // 自定义了一个叫做 pwd 校验规则 pwd: [/^[S]{6,12}$/, '密码必须6到12位,且不能出现空格'], // 校验两次密码是否一致的规则 repwd: function (value) { // 通过形参拿到的是确认密码框中的内容 // 还需要拿到密码框中的内容 // 然后进行一次等于的判断 // 如果判断失败,则return一个提示消息即可 var pwd = $('.reg-box [name=password]').val() if (pwd !== value) { return '两次密码不一致!' } }, }) // 监听注册表单的提交事件 $('#form_reg').on('submit', function (e) { // 1. 阻止默认的提交行为 e.preventDefault() // 2. 发起Ajax的POST请求 var data = { username: $('#form_reg [name=username]').val(), password: $('#form_reg [name=password]').val(), } $.post('/api/register', data, function (res) { if (res.status !== 0) { return layer.msg(res.message) } layer.msg('注册成功,请登录!') // 模拟人的点击行为 $('#link_login').click() }) }) // 监听登录表单的提交事件 $('#form_login').submit(function (e) { // 阻止默认提交行为 e.preventDefault() $.ajax({ url: '/api/login', method: 'POST', // 快速获取表单中的数据 data: $(this).serialize(), success: function (res) { if (res.status !== 0) { return layer.msg('登录失败!') } layer.msg('登录成功!') // 将登录成功得到的 token 字符串,保存到 localStorage 中 localStorage.setItem('token', res.token) // 跳转到后台主页 location.href = '/index.html' }, }) }) }) //密码强度判断 function passwordChangeStatuss(pwd) { if (pwd == '' || pwd == null) { $('.pwd-item label').attr('class', 'layui-btn layui-btn-primary') } else { S_level = checkStrong(pwd) switch (S_level) { case 0: $('.pwd-item label').attr( 'class', 'layui-btn layui-btn-primary' ) case 1: $('#l').attr('class', 'layui-btn layui-btn-danger') $('#m').attr('class', 'layui-btn layui-btn-primary') $('#h').attr('class', 'layui-btn layui-btn-primary') break case 2: $('#l').attr('class', 'layui-btn layui-btn-danger') $('#m').attr('class', 'layui-btn layui-btn-warm') $('#h').attr('class', 'layui-btn layui-btn-primary') break default: $('#l').attr('class', 'layui-btn layui-btn-danger') $('#m').attr('class', 'layui-btn layui-btn-warm') $('#h').attr('class', 'layui-btn') } } } //判断输入密码的类型 function CharMode(iN) { if (iN >= 48 && iN <= 57) //数字 return 1 if (iN >= 65 && iN <= 90) //大写 return 2 if (iN >= 97 && iN <= 122) //小写 return 4 else return 8 } //bitTotal函数 //计算密码模式 function bitTotal(num) { modes = 0 for (i = 0; i < 4; i++) { if (num & 1) modes++ num >>>= 1 } return modes } //返回强度级别 function checkStrong(sPW) { if (sPW.length <= 8) return 0 //密码太短 Modes = 0 for (i = 0; i < sPW.length; i++) { //密码模式 Modes |= CharMode(sPW.charCodeAt(i)) } return bitTotal(Modes) }