用express脚手架做后台管理系统(思路+代码展示)

所用技术:
1.采用前后端分离:
前后端分离和前后端不分离的区别:
前后端不分离:前端页面看到的效果都是由后端控制和渲染的,前后端耦合度高;
前后端分离:前端根据自己的需求渲染页面效果,前后端耦合度低。
 
2.ajax
通过ajax请求数据,处理后返回结果
 
3.jquery
主要是使用iquery中的$.ajax
 
4.数据库:
mongoose
 
5.bootstrap
主要是用于静态布局
 
思路:
一,登录注册:
1.在public文件夹新建index.html 文件,书写静态布局,利用模块化将html代码进行封装---->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="./css/common/reset.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="./css/index/index.css">
</head>

<body>
<div class="login_container"></div>
</body>

</html>
<script src="https://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
<script src="./js/js/common/register.js"></script>
<script src="./js/js/common/login.js"></script>
<script src="./js/js/common/page.js"></script>
2.在public文件夹下新建js文件夹,在js文件夹下新建common文件夹,存放index要封装的模块化JS文件---->
3.在common文件夹下新建page.js文件:创建一个Page对象(该Page对象就是index文件中的登录注册的静态布局)---->>给Page对象一个container实例属性,init以及createContent的原型方法---->
function Page() {
    //实例属性:
    this.container = $(".login_container");    //登录注册最外层div的class名
    this.flag = true;        //设置开关,在注册和登录之间实现跳转
    this.init();
   
}

//原型方法:
Page.prototype = {
    init:function(){
        this.createContent();
    },
    //渲染到页面的方法:需要在Register和Login之后调用:
    createContent:function(params=this.flag){     //判断有参数则直接登录,没有参数则注册传参
        this.container.html('')
        if(params){
           //new一个login对象(login.js)
            this.login =  new Login(this.container);
        }else{
           //new一个register对象(register.js)
            this.register = new Register(this.container);
        }
    }
}

new Page();

4.在common文件夹下新建login.js和register.js文件---->主要是创建登录,注册模板

//创建一个Register对象:
function Register(container){
    this.container = container;
    this.init();
}

//Resgiter模板
Register.template = `
<div class="login">
            <div class="logo">
                <img src="https://cas.1000phone.net/cas/images/login/logo.png">
            </div>
            <form class="form">
                <div class="form-group">
                    <label for="register_username">用户名</label>
                    <input type="email" class="form-control" id="register_username" placeholder="Email">
                </div>
                <div class="form-group">
                    <label for="register_password">密码</label>
                    <input type="password" class="form-control" id="register_password" placeholder="Password">
                </div>
                <div class="form-group">
                    <div class="alert alert-danger" role="alert">忘记密码?</div>
                    <div class="alert alert-info" role="alert" id="js_login">去登陆</div>
                </div>
                <button type="button" class="btn btn-info login_btn" id="login_btn">注册</button>
            </form>
        </div> 
`

Register.prototype = {
    init:function(){
        this.createDom();
        this.handlePush();
        this.handleRegister();
    },
    //创建div,将Register.prototype追加到div中,再将div追加到页面中
    createDom:function(){
        this.el = $("<div class='content'></div>");
        this.el.append(Register.template);
        this.container.append(this.el);
    },
    //点击去登录,切换到登录页面
    handlePush(){
        this.el.find("#js_login").on("click",$.proxy(this.handleLogin,this))  
    },
    handleLogin(){
         new Page().createContent(true);
    },
     //点击去注册,切换到注册页面
    handleRegister(){
        this.el.find("#login_btn").on("click",$.proxy(this.handleRegisterBtn,this))
    },

    //给注册按钮添加点击事件:通过$.ajax向服务器发送请求,并将服务器处理的结果返回给客户端
    handleRegisterBtn(){
        //获取客户信息(用户名和密码)
       var username = this.el.find("#register_username").val();
       var password = this.el.find("#register_password").val();

       //var一个userInfo对象,存放用户名和密码:因为key和value一样,所以只写一个就可以了
    //    var userInfo = {
    //        username:username,
    //        password:password
    //    }
       var userInfo = {
           username,
           password
       }
      //向服务端发送请求:将客户传递的参数传递给服务端,服务端进行处理,判断用户名是否存在 
        $.ajax({
            type:"post",
            url:"/api/register",    //接口是由后端提供的,通过路由建立前后端之间的连接
            data:userInfo,    
            dataType:"json",
            success:$.proxy(this.handleRegisterSuccess,this)    //获取数据成功返回handleRegisterSuccess方法
        })
    },
    handleRegisterSuccess(data){
        if(data.status){
            alert("注册成功");    
            //注册成功跳转到登录页面
            new Page().createContent(true);    
        }
    }
}
//创建Login对象:
function Login(container){
    //实例属性:
    this.container = container;
    this.init();
}

//登录模板
Login.template = `
<div class="login">
            <div class="logo">
                <img src="https://cas.1000phone.net/cas/images/login/logo.png">
            </div>
            <form class="form">
                <div class="form-group">
                    <label for="login_username">用户名</label>
                    <input type="email" class="form-control" id="login_username" placeholder="Email">
                </div>
                <div class="form-group">
                    <label for="login_password">密码</label>
                    <input type="password" class="form-control" id="login_password" placeholder="Password">
                </div>
                <div class="form-group">
                    <div class="alert alert-danger" role="alert">忘记密码?</div>
                    <div class="alert alert-info" role="alert" id="js_register">去注册</div>
                </div>
                <button type="button" class="btn btn-info login_btn" id="login_btn">登陆</button>
            </form>
        </div> 
`
//原型方法
Login.prototype = {
    init(){
        this.createDom();
        this.handlePush();
        this.loginBtn();
    },
    createDom:function(){
        this.el = $("<div class='content'></div>");
        this.el.append(Login.template);
        this.container.append(this.el);
    },
    //点击去注册切换到注册页面
    handlePush(){
       this.el.find("#js_register").on("click",$.proxy(this.handleRegister,this))  
    },
    handleRegister(){
        new Page().createContent(false);
    },
    //给登录按钮添加点击事件:
    loginBtn(){
        this.el.find("#login_btn").on("click",$.proxy(this.handleLoginCallBack,this))
    },
    handleLoginCallBack(){
        //var一个userInfo对象
        var userInfo = {
            username:this.el.find("#login_username").val(),
            password:this.el.find("#login_password").val()
        }
       //通过ajax将客户端的请求发送给服务端:并接受服务端返回的数据:
        $.ajax({
            type:"post",
            url:"/api/login",
            data:userInfo,
            success:$.proxy(this.handleLoginSucc,this)
        })
    },
    //登录成功则跳转到详情页
    handleLoginSucc(data){
        if(data.status){
            location.href="http://localhost:3000/html/detail.html"
        }
    }
}

以上前端代码书写完毕:从代码中不难看出:前端主要是做了静态布局,以及向后端发送请求,并将返回结果回馈给客户端,进行渲染页面

接下来理一下后端代码:

在客户端向服务端发送请求后,服务端接收到请求后,是要对请求数据做处理的,即对数据的增删改查,主要是放在model层的:

我们先来处理一下注册数据:处理数据之前我们先来连接一下数据库:

新建util文件夹,并新建database.js文件:

//引入mongoose模块:
var mongoose = require("mongoose");
//设置路径,连接数据库
let url = "mongodb://127.0.0.1:27017/bk1824"
mongoose.connect(url);
//导出数据库模块
module.exports = {
    mongoose
}

有了数据库我们再来model文件夹下处理数据:

我们将注册登录的数据放在user.js文件中:

//连接数据库
const mongoose = require("../util/database").mongoose;
//在做数据的增删改查时需要先创建表:在数据库中,会默认在表明后加“s”,当表名结尾字母是y时,变y为i再加es
const User = mongoose.model("user",{
    //设置表的字段:(mongodb和mongoose的区别之一:mongoose可以设置字段)
    username:String,
    password:String
})

//
//在处理注册登录数据时,都需要先在数据库中查出对应数据,再进行判断
const userFind = (userInfo,cb)=>{
    User.findOne(userInfo).then((res)=>{
        cb(res)
    })
}

//
//判断注册数据中用户名没被占用,则调用该方法将数据增加到数据库中:
const userAdd = (userInfo,cb)=>{
    let user = new User(userInfo);
    user.save().then((res)=>{
        cb(res);
    })
}

//导出查,增模块:在controller层引入跑业务逻辑
module.exports = {
    userFind,
    userAdd
}

有了操作数据库的方法,接下来我们跑一下业务逻辑,业务逻辑主要在controller文件夹下,注册登录的业务逻辑主要放在user.js文件下:

//引入model/user.js文件,使用增,查方法:
const userModel = require("../model/user");
//引入加密模块  1.     主要用来给密码加密,增加安全性,提高用户体验
const crypto = require('crypto');
//引入token:   判断用户是否已经登录      需要在前端中验证
const jwt = require("jsonwebtoken");
  
//注册的业务逻辑:先获取用户输入的数据,再从数据库中查找对应的数据,判断用户输入的数据在数据库中能否查到,如果有,则用户名已存在,
//注册失败,查不到,添加到数据库中,注册成功
const register = (req,res)=>{
    //解构赋值:因为是POST请求数据的,所以用req.body获取数据:此处获取的是用户传递的参数
    let {username,password} = req.body;
//调用查的方法,从数据库查数据:
    userModel.userFind({username},function(data){
        //如果数据不存在,则添加进数据库:
        if(!data){
            //
            //创建sha256算法  2.    给数据加密:md5和sha256的区别:md5可以解密,sha256不能解密,所以相对安全
            const hash = crypto.createHash('sha256');
            //需要加密的字符 3.
            hash.update(password);
           
            //对密码进行加密 4.
            //将加密的数据添加到数据库中:
            userModel.userAdd({
                username:username,
                password:hash.digest('hex')   //加盐
            },function(){
                //处理完数据返回结果:
               res.json({
                status:true,
                Info:"注册成功"
               })
            })
        }else{
            res.json({
                status:false,
                Info:"用户名已存在"
            })
        }
    })
}


const login = (req,res)=>{
    //获取用户名和密码
    let {username,password} = req.body;
    //查看当前用户名是否存在 如果不存在告诉用户用户名不存在  如果存在则进行密码判断
    userModel.userFind({username},function(data){
        if(data){
            //创建sha256算法
            const hash = crypto.createHash('sha256');
            //进行加密
            hash.update(password);
            //因为数据库中的密码是加密的  所以我们进行对比的时候也需要进行加密在对比
            if(data.password == hash.digest('hex')){

                //设置token
                let token = jwt.sign({username},"bk1824",{'expiresIn':'1h'})
                //种cookie
                res.cookie("token",token)
                res.cookie("userInfo",username)

                //成功
                res.json({
                    status:true,
                    info:"登陆成功",
                })
            }else{
                //失败
                res.json({
                    status:false,
                    info:"密码错误"
                })
            }

        }else{
            //用户名不存在
            res.json({
                status:false,
                info:"用户名不存在"
            })
        }
    })
}
//导出模块:
module.exports = {
    register,
    login
}


/*
    md5加密  解密

    sha256加密


    123456 + bk1824 + 秘钥 = 随机字符串



    token


    1、安装
        cnpm install jsonwebtoken -S

    2、引入
        const jwt = require("jsonwebtoken");

    3、设置token
        jwt.sign(payload,秘钥,过期时间)
        payload:相关信息
        秘钥:随机字符
        过期时间 token什么时候过期
*/

万事俱备,只欠东风,最最重要的路由文件:

var express = require('express');
var router = express.Router();
var userController = require("../controller/user");
/* /api/register */
router.post('/register',userController.register);
//登陆 /api/login
router.post('/login',userController.login);





module.exports = router;


/*
    MVC模式
        架构思想
        M:model层  数据的增删改查
        V:view层   视图的展示
        C:controller层   负责业务逻辑

    MVP

    MVVM

*/

注册登录效果到这儿就告一段落了,接下来再做一下职位的增删改查:

还是先来码一码前端代码:

我们的前端文件还是统一放在public中,至于为啥要放在public中,主要是因为因为我们使用了express框架,既然使用了人家,就得遵循人家的规则嘛,在express中,会优先渲染public中的文件:

我们还是利用bootstrap来做静态布局,用JS模板渲染页面:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="../css/common/reset.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="../css/detail/detail.css">
</head>

<body>
    <div class="header">
        <div class="logo">
            <img src="http://jx.1000phone.net/Public/assets/css/images/logo.png?1552286991" />
        </div>
        <div class="info">
            <p id="js_userName"></p>
            <p id="js_logout" class="logout">退出</p>
        </div>
    </div>
    <!-- tabBar -->
    <div class="container_detail">
        <div class="tabbar">
            <ul>
                <li class="active">系统首页</li>
                <li>职位列表</li>
                <li>职位管理</li>
            </ul>
        </div>
        <div class="content">
            <!-- <div class="list">
                <div class="list_job">
                   <div class="job_des">
                        <div class="job_name">前端开发工程师</div>
                        <div class="job_price">8000</div>
                        <div class="job_ex">经验3-5年 本科</div>
                   </div>
                   <div class="com_des">
                       <div class="company_logo">
                           <img src="https://www.lgstatic.com/thumbnail_100x100/i/image3/M00/28/02/Cgq2xlqa2jWAGAnMAAArW2y3MsY901.jpg"/>
                       </div>
                       <div class="company_name">北京阿里科技有限公司</div>
                   </div>
                   <div class="operation">
                       <button class="btn btn-danger">删除</button>
                       <button class="btn btn-info">修改</button>
                   </div>
                </div>
            </div> -->
        </div>
        <!-- 模块框 -->
        <div class="modal fade" tabindex="-1" role="dialog" id="jobModel">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
                                aria-hidden="true">&times;</span></button>
                        <h4 class="modal-title">请修改内容</h4>
                    </div>
                    <div class="modal-body">
                        <form>
                            <div class="form-group">
                                <label for="job_modify_name">职位名称</label>
                                <input type="text" class="form-control" id="job_modify_name" placeholder="请输入职位名称">
                            </div>
                            <div class="form-group">
                                <label for="job_modify_price">薪资</label>
                                <input type="text" class="form-control" id="job_modify_price" placeholder="薪资范围">
                            </div>
                            <div class="form-group">
                                <label for="job_modify_ask">要求</label>
                                <input type="text" class="form-control" id="job_modify_ask" placeholder="招聘要求">
                            </div>
                            <div class="form-group">
                                <label for="company_modify_name">公司名称</label>
                                <input type="text" class="form-control" id="company_modify_name" placeholder="请输入公司名称">
                            </div>
                            <div class="form-group">
                                <label for="logo_modify">上传公司logo</label>
                                <input type="file" id="logo_modify" multiple>
                            </div>
                        </form>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-primary" id="modify_btn" data-dismiss="modal">修改</button>
                    </div>
                </div>
            </div>
        </div>

    </div>
</body>

</html>
<script src="https://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
<script src="https://cdn.bootcss.com/js-cookie/2.2.0/js.cookie.min.js"></script>
<script src="../js/js/common/auth.js"></script>
<script src="../js/js/detail/logout.js"></script>
<script src="../js/js/detail/joblist.js"></script>
<script src="../js/js/detail/tabbar.js"></script>
<script src="../js/js/detail/job.js"></script>
<script src="../js/js/detail/modify.js"></script>

静态布局就不详细说了,毕竟审美不同,可以随意调的,这个随意,主要是理一下JS代码:

public文件夹---detail---js-----job.js

function Job() {
    this.content = $(".content");
    this.init()
}

Job.template = `
<form>
                    <div class="form-group">
                        <label for="job_name">职位名称</label>
                        <input type="text" class="form-control" id="job_name" placeholder="请输入职位名称">
                    </div>
                    <div class="form-group">
                        <label for="job_price">薪资</label>
                        <input type="text" class="form-control" id="job_price" placeholder="薪资范围">
                    </div>
                    <div class="form-group">
                        <label for="job_ask">要求</label>
                        <input type="text" class="form-control" id="job_ask" placeholder="招聘要求">
                    </div>
                    <div class="form-group">
                        <label for="company_name">公司名称</label>
                        <input type="text" class="form-control" id="company_name" placeholder="请输入公司名称">
                    </div>
                    <div class="form-group">
                        <label for="logo">上传公司logo</label>
                        <input type="file" id="logo" multiple>
                    </div>
                    <button type="button" class="btn btn-default" id="js_jobBtn">提交</button>
                </form>
`

//原型对象
Job.prototype = {
    init(){
        this.createDom();
        this.JobClick();
    },
    //动态渲染到页面中:
    createDom(){
        this.el = $("<div class='from'></div>");
        this.el.append(Job.template);
        this.content.html(this.el)
    },
    //点击提交,上传数据:
    JobClick(){
        this.el.find("#js_jobBtn").on("click",$.proxy(this.handleJobCB,this))
    },
    handleJobCB(){
        //创建formData 模拟表单提交数据  兼容性问题    此处用模拟表单提交数据主要是因为要上传图片
        //获取要上传的数据:
        var formData = new FormData();
        var jobName = this.el.find("#job_name");
        var jobPrice = this.el.find("#job_price");
        var jobAsk = this.el.find("#job_ask");
        var companyName = this.el.find("#company_name");
        var logo = this.el.find("#logo");
        
      
        
        //append key val  key是服务端接收的key值
        formData.append("jobname",jobName.val());
        formData.append("jobprice",jobPrice.val());
        formData.append("jobask",jobAsk.val());
        formData.append("companyname",companyName.val());
        formData.append("companylogo",logo[0].files[0]);
       
       
//通过ajax将请求的数据发送给服务器:
        $.ajax({
            type:"post",
            url:"/job/addjob",
            data:formData,
            cache:false,    //不读取缓存中的结果 true的话会读缓存  其实post本身就不会读取缓存中的结果
            contentType:false,  //数据编码格式不使用jquery的方式 为了避免 JQuery 对其操作,从而失去分界符,而使服务器不能正常解析文件。
            processData:false,//默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成
            //一个查询字符串,以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的
            //信息,请设置为 false。
            success:$.proxy(this.handleSuccess,this)
        })
    },
    handleSuccess(data){
       if(data.status){
           alert("添加成功");
           new JobList();
       }
    }
}
原文地址:https://www.cnblogs.com/kinoko-1009/p/10527006.html