[Node.js] mySQL数据库 -- 英雄管理系统(完善)

英雄管理系统

项目要求

a.注册页要有验证码,账号重复、密码重复、验证码错误要提示
b.登录页面用户名和密码要正确,不正确要提示
c.进入主页面,显示查找、显示新增、头像、姓名、技能、操作(编辑和删除)
d.点击查找可以快速找到列表中的信息,没有信息要显示
e.编辑和新增包括姓名、技能、头像、保存
f.点击删除,删除的头像、姓名、技能、操作(编辑和删除)在后端保存,不能够真正的删除(软删除)
g.页面要求简洁、美观、大方

 

项目代码下载

 
英雄管理系统(完善)下载地址

 

实现效果展示

  • 总体效果
    在这里插入图片描述
  • 注册页面
    在这里插入图片描述
  • 登录界面
    在这里插入图片描述
  • 查找界面
    在这里插入图片描述
  • 新添界面
    在这里插入图片描述
  • 编辑界面
    在这里插入图片描述
  • 删除界面
    在这里插入图片描述
     
     

项目准备阶段

  • 首先,我们要从王者荣耀官网上爬取一定数量的内容,为之后做准备爬取王者荣耀.js在这里插入图片描述
  • 导包

const express = require(“express”);
const hm = require(“mysql-ithm”);
const cors = require(“cors”);
const multer = require(“multer”);
var bodyParser = require(“body-parser”);
var svgCaptcha = require(‘svg-captcha’); //验证码插件
const cookieSession = require(“cookie-session”);

  • 连接数据库
hm.connect({
    host: 'localhost',//数据库地址
    port: '3306',
    user: 'root',//用户名,没有可不填
    password: '123',//密码,没有可不填
    database: 'cqmanager503'//数据库名称
});
  • 创建英雄模型和用户模型
// 英雄模型
let heroModel = hm.model("hero", {
    heroName: String,
    heroSkill: String,
    heroIcon: String,
    isDelete: String,
});
// 用户模型
let userModel = hm.model('users', {
    username: String,
    password: String
});
  • 托管静态资源
app.use(express.static('www'));
app.use(express.static('uploads'));
app.use(express.static('static'));

  • 配置中间件
app.use(cors());
// 4.1 body-parser:解析post参数,给req添加属性body,存储解析好的post参数
app.use(bodyParser.urlencoded({ extended: false }));
// 4.4 cookie-session中间件:给req添加session成员
app.use(cookieSession({
    name: 'session',
    keys: ['1', '2'],//加密(盐)
    // 有效期
    maxAge: 7 * 24 * 60 * 60 * 1000 // 7*24 hours
}));

  • 开启服务器
app.listen(4399, () => {
    console.log('服务器开启成功');
});

 
 

参数说明

  • API接口
接口说明URL请求方式请求参数返回值
查询英雄列表/hero/listgetsearch :英雄名称,不传返回所有[heros:{英雄列表}]
查询英雄详情/hero/infogetid:英雄id{data:英雄详情}
编辑英雄/hero/updatepostname,skill, icon,id{code: 200)
删除英雄/hero/deletepostid{code: 200)
新增英雄/hero/addpostname,skill, icon{code: 200)
验证码/captchaget验证码图片
用户注册/user/registerpostusername, password, code:验证码{code:200|401|402}
用户登录/user/loginpostusername, password{code:200|401|402}
退出登录/logoutget
  • 服务器
服务器说明作用描述
http://127.0.0.1:4399服务器基地址
200请求成功状态码
401用户名已存在或者用户名错误
402密码错误或者验证码错误
500服务器内部错误
302服务器重定向

 
 

服务端流程

查询英雄列表

请求并且判断是否传入参数

  • 没有传入查询参数
    • 要查询所有的英雄
      • 判断是否查询到
        • 查询到
          • 返回服务器内部错误
        • 没有查询到
          • 返回内部
  • 有传入查询参数
    • 模糊查询,还有一个并且语法 and isDelete = ‘false’
      • 判断是否查询到
        • 查询到
          • 返回服务器内部错误
        • 没有查询到
          • 返回内部
// 5.1 查询英雄列表(查询没有删除所有的英雄)
app.get('/hero/list', (req, res) => {
    // (1) 请求
    let { search } = req.query;
    //console. log(search);//有查询参数就是有值的,没有查询参数就是undefined.
    // b.判断
    // (2) 处理(查询数据库)
    if (!search) {
        //如果进到这里来了, 说明没有查询参数,我就要查询出所有的英雄。
        heroModel.find('isDelete="false" ',(err, results) => {
            if (err) {
                res.send({
                    code: 500,
                    msg: '服务器内部错误'
                });
            } else {
                // (3) 响应
                res.send({
                    code: 200,
                    heros: results
                })
            }
        });
    } else {
        // 如果有search,则根据条件查询数据(包含查询)
        heroModel.find(`heroname like "%${search}%" and isDelete="false"`, (err, results) => {
            if (err) {
                res.send({
                    code: 500,
                    msg: '服务器内部错误'
                });
            } else {
                // (3) 响应
                res.send({
                    code: 200,
                    heros: results
                })
            }
        });
    }
});

 

新增英雄

这里需要用到 multer 模块
app.post(’/profile’, upload.single(‘avatar’), function (req, res, next) {
    // req.file is the avatar file
    // req.body will hold the text fields, if there were any
})

  • 这里需要接收用户新增的英雄名,英雄技能,英雄头像
  • 将接受到的参数插入到数据库中
    • 判断是否接受到
      • 接收到
        • 返回新增成功
      • 没有接收到
        • 返回服务器内部错误
// 5.5 新增英雄
app.post("/hero/add", upload.single("heroIcon"), (req, res) => {
    //a.接收用户新增的英雄名,英雄技能,英雄头像
    let { heroName, heroSkill, isDelete = "false" } = req.body;
    let heroIcon = req.file.filename;
    //b.插入到数据库中
    heroModel.insert(
        { heroName, heroSkill, heroIcon, isDelete },
        (err, results) => {
            if (err) {
                res.send({
                    code: 500,
                    msg: "服务器内部错误" + err,
                });
            } else {
                res.send({
                    code: 200,
                    msg: "新增成功",
                });
            }
        }
    );
});

 

根据 ID 查询英雄详情
  • 接收传递过来的英雄ID
  • 根据id查询英雄的详细信息
    • 判断查询信息
      • 没有错误,且有数据
        • 则返回data: results[0]
      • 有错误
        • 返回服务器内部错误
      • 无错误,但没有英雄
        • 返回没有此英雄,或已被删除!
// 5.2 查询英雄的详情(编辑的第一步:根据ID查询,显示到编辑页)
app.get("/hero/info", (req, res) => {
    // a. 接收传递过来的英雄ID
    let { id } = req.query;
    // b.根据id查询英雄的详细信息
    heroModel.find(`id=${id} and isDelete="false"`, (err, results) => {
        if (err == null && results.length != 0) {
            res.send({
                code: 200,
                data: results[0],
            });
        } else if (err) {
            res.send({
                code: 500,
                mgs: "服务器内部错误"
            });
        } else {
            res.send({
                code: 201,
                mgs: "没有此英雄,或已被删除!",
            });
        }
    });
});

 

英雄编辑
  • 判断是否修改了头像
    • 修改了头像
      • obj 对象里面就有id, heroName,heroSkill , heroIcon
    • 没有修改头像
      • obj 对象里面就只有id,heroName, heroSkill

这里要调用一下mysq1-ithm模块,使用其中的方法吧数据更新带数据库中

  • 判断是否更新到数据库
    • 更新了
      • 返回修改成功
    • 没有更新
      • 返回服务器内部错误
// 5.3 编辑英雄
app.post("/hero/update", upload.single("heroIcon"), (req, res) => {
    //如果不改头像,只改名字和技能.我们希望这种需求也是可以的。
    //赋值
    let { id, heroName, heroSkill } = req.body;
    let obj = {
        heroName,
        heroSkill,
    };
    //如果修改了头像,那obj对象里面就有id, heroName,heroSkill , heroIcon
    //如果没有修改头像,那obj对象里面就只有id,heroName, heroSkill
    if (req.file != undefined) {
        //能够进到这里来 ,说明传了修改后的头像进来。
        obj.heroIcon = 'http://127.0.0.1:4399/' + req.file.filename;
    }
    //调用mysq1-ithm模块中的方法,把数据更新到数据库中.
    heroModel.update(`id=${id}`, obj, (err, results) => {
        if (err) {
            res.send({
                code: 500,
                msg: "服务器内部错误"
            });
        } else {
            res.send({
                code: 200,
                msg: "修改成功",
            });
        }
    });
});

 

英雄删除(软删除)

实际上修改的是当前id的英雄isDelete为true

  • 接收前端传递过来的要删除的英雄id
  • 调用mysql-ithm模块中的方法,来完成
    • 判断是否删除英雄(改为true)
      • isDelete为true
        • 返回删除成功
      • isDelete不为true
        • 返回服务器内部错误
// 5.4 删除英雄(软删除,实际上修改的是当前id的英雄isDelete为true)
app.post("/hero/delete", (req, res) => {
    // a.接收前端传递过来的要删除的英雄id
    let  { id } = req.body;
    //b.调用mysql-ithm模块中的方法,来完成
    heroModel.update(`id=${id}`, { isDelete: "true" }, (err, results)=> {
        if (err) {
            res.send({
                code: 500,
                msg: "服务器内部错误"
            });
        } else {
            res.send({
                code: 200,
                msg: "删除成功"
            });
        }
    });
});

 

验证码

在这里插入图片描述

利用 svg-captcha :验证码插件(因为不是每个地方都会用到,不需要像中间件那样要用app.use())

  • 创建一个验证码
  • 返回验证码(实际上就是一个svg格式的图片)
  • 响应给客户端
// 5.6 验证码
// svg-captcha:验证码插件(因为不是每个地方都会用到,不需要像中间件那样要用app.use())
// (2)声明全局变量存储验证码文本
//b.搞一个全局变量来保存一下这个验证码文字
let captchaText = '';
//c.使用
app.get('/captcha', (req, res) => {
    // //创建一个验证码
    var captcha = svgCaptcha.create();
    // console.log(captcha);
    // 文本:服务器存起来用于注册接口验证
    //返回验证码
    captchaText = captcha.text;
    // 图片:响应给客户端
    // 返回验证码,实际上就是一个svg格式的图片
    res.type('svg');
    res.status(200).send(captcha.data);
});

 

用户注册

在这里插入图片描述

  • 获取前端传递过来的用户名/加密的密码/验证码
  • 验证验证码输入的是否正确
    • 不正确
      • 返回验证码错误
    • 正确
      • 判断用户名是否已经被注册
        • 无法判断
          • 返回服务器内部错误-查询用户名是否存在
        • 可以判断
          • 如果用户名存在
            • 返回用户名已存在
          • 如果用户名不存在
            • 判断是否接受
              • 接受到
                • 返回注册成功
              • 无法接受
                • 返回服务器内部错误-注册时错误

常用的加密方式

在这里插入图片描述

// 5.7 用户注册
app.post("/user/register", (req, res) => {
    //a.获取前端传递过来的用户名/加密的密码/验证码
    let { username, password, code } = req.body;
    console.log(username, password, code);
    //b.验证验证码输入的是否正确.
    if (code.toLocaleLowerCase() != captchaText.toLocaleLowerCase()) {
        res.send({
            code: 402,
            msg: "验证码错误",
        });
    } else {
        //进到这里来了, 说明验证码是对的,那就要开始注册了.
        //要不要去数据库中判断一下以前这个用户名是否已经被注册了?
        userModel.find(`username="${username}"`, (err, results) => {
            if (err) {
                res.send({
                    code: 500,
                    msg: "服务器内部错误-查询用户名是否存在",
                });
            } else {
                //说明可以查询成功,但是还要看看查出来有没有结果.
                if (results.length > 0) {
                    // 用户名是否存在
                    res.send({
                        code: 401,
                        msg: "用户名已存在",
                    });
                } else {
                    //该用户名不存在,不存在就往册呀(增加/插入操作)
                    userModel.insert(
                        {
                            username,
                            password,
                        }, (err, results) => {
                            // 注册时的错误判断
                            if (err) {
                                res.send({
                                    code: 500,
                                    msg: "服务器内部错误-注册时错误'",
                                });
                            } else {
                                res.send({
                                    code: 200,
                                    msg: "注册成功",
                                });
                            }
                        }
                    );
                }
            }
        });
    }
});

 

用户登录
  • 先获取用户登录时候,传递过来的用户名和加密后的密码
  • 去数据库中判断有没有这样的账号和密码.
    • 判断是否查询成功
      • 查询失败
        • 返回服务器内部错误
      • 查询成功
        • 返回是否有数据
          • 有数据
            • 返回登录成功
          • 没有数据
            • 返回账号或密码错误
// 5.8 用户登录
app.post("/user/login", (req, res) => {
    //a.先获取用户登录时候,传递过来的用户名和加密后的密码
    let { username, password } = req.body;
    //b.去数据库中判断有没有这样的账号和密码.
    userModel.find(`username="${username}" and password="${password}"`, (err, results) => {
        //判断
        if (err) {
            res.send({
                code: 500,
                msg: "服务器内部错误",

            });
        } else {
            //判断查成功的结果,有没有数据
            if (results.length > 0) {
                // 发送的session的键是user,值是一个对象,对象里面包含账号密码.
                req.session.user = { username, password };
                res.send({
                    code: 200,
                    msg: "登录成功",
                });
            } else {
                res.send({
                    code: 401,
                    msg: "账号或密码错误",
                });
            }
        }
    });
});

 

检测是否登录
  • 判断是否登录
    • 登录了
      • 响应回去的就是有内容的
    • 没有登录
      • 响应回去的就是没有内容的
// 5.0 判断有没有登录
app.get("/isLogin", (req, res) => {
    //如果你登录了, 那你响应回去的就是有内容的.
    //如果你没有登录,那你响应回去的就是没有内容的.
    res.send(req.session.user);
});

 

退出登录
  • 清除session
  • 使用重定向技术刷新首页
// 5.9 退出登录
app.get('/logout', (req, res) => {
    // (1) 清除session
    req.session = null;
    // (2) 使用重定向技术刷新首页
    res.writeHead(302, {
        Location: './login.html'
    });
    res.end();
});

 
 

客户端流程

查询英雄列表

在这里插入图片描述

  • 获取传递过来的英雄id
  • 发送ajax请求
  • 对应的信息显示在对应的页面标签上,
//  一、根据ID发送Ajax请求,获取详细信息 并显示在页面标签上
//1. 获取传递过来的英雄id
var id = window.location.search.split('=')[1]
//2.发送ajax请求
$.ajax({
    url: 'http://127.0.0.1:4399/hero/info',
    type: 'get',
    data: { id },
    success: function (backData) {
        //对应的信息显示在对应的页面标签上,
        if (backData.code == 200) {
            $('#id').val(id)//隐藏域存放id
            $('#name').val(backData.data.heroName)
            $('#skill').val(backData.data.heroSkill)
            $('#iconImg').attr('src', backData.data.heroIcon)
        }
    }
})

 

新增英雄

在这里插入图片描述

  • 图片预览
    • 获取用户选择的图片
    • 创建一个url
    • 把url赋值给预览用的img的src属性
  • 完成新增
    • 给新增按钮设置一个点击事件
    • 去掉默认提交行为
    • 创建一个FormData对象,获取新增的英雄的信息(名字,技能,头像)
    • 发送ajax请求
    • 跳转首页
// 入口函数
$(function () {
    // 1. 图片预览
    $('#icon').on('change', function () {
        // 1. 获取用户选择的图片
        var file = this.files[0]
        // 2. 创建一个url
        var url = URL.createObjectURL(file)
        // 3. 把url赋值给预览用的img的src属性
        $('#iconImg').attr('src', url)
    })
    // 2. 完成新增
    //1.给新增按钮设置一个点击事件
    $('.btn-insert').on('click', function (e) {
        //2.去掉默认提交行为
        e.preventDefault()
        //3.创建一个FormData对象,获取新增的英雄的信息(名字,技能,头像)
        // var fd = new FormData(document.querySelector('form'))
        var fd = new FormData($('#form')[0])
        //form表单是dom元素,form表单中需要获取数据的标签都应该有name属性并且name属性的值应该和接口参数值一致
        //4.发送ajax请求.
        $.ajax({
            url:'http://127.0.0.1:4399/hero/add',
            type:'post',
            dataType:'json',
            data:fd,
            contentType: false,
            processData: false,
            success: function(backData){
                console.log(backData);
                if (backData.code==200) {
                    alert('新增成功');
                    // 跳转首页
                    window.location.href='./index.html';
                }
            }
        });
    })
})

 

根据 ID 查询英雄详情

在这里插入图片描述

  • 给查询按钮设置一个点击事件
  • 去掉默认的提交行为
  • 获取用户输入的要查询的关键词
  • 发送ajax请求,获取数据
  • 通过模板引擎渲染页面
//二:查询符合条件(英雄名包含查询关键词)的所有英雄
//1.给查询按钮设置一个点击事件.
$('#searchBtn').on('click', function (e) {
    //2.去掉默认的提交行为
    e.preventDefault()
    //3.获取用户输入的要查询的关键词
    let search = $('#search').val().trim()
    //4.发送ajax请求,获取数据
    $.ajax({
        type: 'get',
        url: 'http://127.0.0.1:4399/hero/list',
        data: { search },
        success: function (backData) {
            // console.log(backData);
            if(backData.heros.length == 0){
                $('tbody'). html ('没有数据');
                return;
            }
            if(backData.code == 200){
                //5.通过模板引擎渲染
                // 调用模板引擎渲染页面
                var resHtml = template('cq',backData);
                $('tbody'). html (resHtml);
            } else {
                alert('出问题了')
            }
        }
    });
})

 

英雄编辑

在这里插入图片描述

  • 给编辑按钮设置一个点击事件
  • 去掉默认的提交行为
  • 获取編辑后的英雄信息(FormData对象获取)
  • 发送ajax请求,完成編辑
  • 完成了编辑,提示一下跳转到首页
// 二:头像预览
$('#heroIcon').on('change', function () {
    var file = this.files[0]
    var url = URL.createObjectURL(file)
    $('#iconImg').attr('src', url)
})
// 保存按钮事件
// 1. 给编辑按钮设置一个点击事件
$('.btn-save').on('click', function (e) {
    //2.去掉默认的提交行为
    e.preventDefault()
    //3.获取編辑后的英雄信息(FormData对象获取)
    var fd = new FormData(document.querySelector('form'))
    // form表单是dom元素,要获取值的标签要有name属性,name属性的值要和接口参数一致
    //4.发送ajax请求,完成編辑
    $.ajax({
        type: 'post',
        url: 'http://127.0.0.1:4399/hero/update',
        // data是FormData对象,不要忘记2个false.
        contentType: false,
        processData: false,
        data: fd,
        success: function (backData) {
            if (backData.code == 200) {
                //5.完成了编辑,提示一下跳转到首页
                alert('编辑成功')
                window.location.href = '/index.html'
            }
        }
    })
})

 

英雄删除

在这里插入图片描述

  • 注册点击事件
  • 获取当前点击的这个删除按钮自定义属性保存的英雄id
  • 发送ajax请求,把id传过去
  • 删除成功了,就重 新加载一下数据,或者刷新一下页面
//1.注册点击事件.
$('tbody').on('click', '.btn-delete', function () {
	if (confirm( '你确定要删除吗?')) {
		//2.获取当前点击的这个删除按钮自定义属性保存的英雄id
		var id = $(this).attr('data-id')
		//3.发送ajax请求,把id传过去
		$.ajax({
			type: 'post',
			url: 'http://127.0.0.1:4399/hero/delete',
			data: {
				id
			},
			success: function (backData) {
				if (backData.code == 200) {
				//4. 删除成功了,就重 新加载一下数据,或者刷新一下页面
					alert('删除成功')
				getData()
				}
			}
		})
	}
})

 

验证码

在这里插入图片描述

  • 点击验证码图片刷新验证码
    • 验证码不能使用ajax发送请求
      • 原因:验证码服务器返回的不是字符串,而是图片二进制数据
    • 验证码只需重设img标签的src属性即可
      • 问题:img标签自带缓存功能,如果src一致,则只发一次请求
      • 解决方案:添加随机参数
// 1.点击验证码图片刷新验证码
$('.code').on('click', function () {
    $(this).attr('src', 'http://127.0.0.1:4399/captcha?sb=' + Math.random());
});

 

用户注册

在这里插入图片描述

  • 注册按钮的点击事件
  • 取消默认的提交行为
  • 获取用户注册时候输入的用户名/密码/验证码
  • 在密码加密之后获取密码,获取到的就是密文
  • 发送ajax请求,完成注册
//二:注册
// 2.注册按钮的点击事件
$('#register').on('click', function (e) {
    // 2.取消默认的提交行为
    e.preventDefault();
    // 3.获取用户注册时候输入的用户名/密码/验证码
    let username = $('#username').val().trim()//账号
    let code = $('#code').val().trim()//验证码
    $('#password').val(md5($('#password').val().trim(), 'dg'))//密码

    //在密码加密之后获取密码,获取到的就是密文
    let password = $('#password' ).val().trim();
    console . log(password) ;
    // md5加密 md5();
    // 第一个参数:要加密的明文
    // 第二个参数:加盐(作用:增加密码的复杂度;特点:同样的明文,盐不同,得到的密文不同)
    // 细节:尽量不在控制台打印用户的明文和密码
    // let password = $('#password').val();
    // let miwen = md5(password,'jane');
    // 取出表单数据,加密完之后,重新赋值给表单
    /*
       FormData:(1)自动获取每个表单的name和value拼接成参数 (2)上传文件
       $('#form').serialize():(1)自动获取每个表单的name和value拼接成参数
     */
    //4.发送ajax请求,完成注册
    $.ajax({
        url: 'http://127.0.0.1:4399/user/register',
        type: 'post',
        // dataType: 'json',
        data: {
            username,
            password,
            code
        },

        success: function (backData) {
            if (backData.code == 200) {
                alert('注册成功');
                // 跳转首页
                window.location.href = "./login.html";
            } else {
                alert(backData.msg);
            }
        }
    });
})

 

用户登录

在这里插入图片描述

  • 给登录按钮设置一个点击事件
  • 去掉默认的提交行为
  • 获取用户输入的账号和密码(要和注册的时候一样的加密规则的加密后的密码)
  • 把用户输入的密码用md5加盐的方式加密后,重新赋值给密码输入框
  • 发送ajax请求,完成登录
$(function () {
    //一:登录
    //1.给登录按钮设置一个点击事件
    $('.btn-login').on('click', function (e) {
        //2.去掉默认的提交行为
        e.preventDefault()
        //3.获取用户输入的账号和密码(要和注册的时候一样的加密规则的加密后的密码)
        let username = $('#username').val().trim()
        // 把用户输入的密码用md5加盐的方式加密后,重新赋值给密码输入框
        $('#password').val(md5($('#password').val().trim(), 'dg'))
        let password = $('#password').val().trim()
        //4.发送ajax请求,完成登录
        $.ajax({
            type: "post",
            url: 'http://127.0.0.1:4399/user/login',
            data: { username, password },
            success: function (backData) {
                if (backData.code == 200) {
                    alert('登录成功');
                    window.location.href = './index.html'
                } else {
                    alert(backData.msg)
                }
            }
        })
    })
})

 

检测是否登录

在页面加载后自动发送请求判断是否登陆

<script>
    (function () {
        $.ajax({
            type: 'get',
            url: '/isLogin',
            success: function (backData) {
                if (backData == '') {
                    alert('没有登陆')
                    window.location.href = '/login.html'
                }
            }
        })
    })()
</script>
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值