Node+Express+mySql搭建新闻系统

搭建开发环境

全局安装expressexpress-generator 脚手架

npm install express -g
npm install -g express-generator

创建项目
我使用ejs模板引擎,所以是-e

express -e expressNews

初始化项目

cd expressNews 
npm install 

使用VS Code打开项目,找到package.json,修改scripts

"scripts": {
    "start": "node ./bin/www",
    "dev": "nodemon ./bin/www"
 },

在启动项目时使用nodemon指令来启动,这样当我们项目代码更改时它能够自动重新运行.
如果还没安装就在控制台输入npm i nodemon -g全局安装nodemon.

修改项目入口文件app.js

//...
//删除users路由,同时删除routers目录下的users.js
var usersRouter = require('./routes/users');

//添加监听端口
app.listen(8100, function () { 
  console.log('启动成功:http://localhost:8100')
})

//...
//删除user路由挂载
app.use('/users', usersRouter);

启动项目

npm dev

运行结果:
npm start

搭建前端页面

目录结构

├── app.js
├── bin
│   └── www
├── node_modules
├── package-lock.json
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
├── routes
│   ├── index.js
│   ├── login.js
│   └── signup.js
└── views
    ├── error.ejs
    ├── index.ejs
    ├── login.ejs
    └── signup.ejs

登录页面 login.ejs

<!-- views/login.ejs -->
<!doctype html>
<html>
<head>
	<title>Node Authentication</title>
	<link rel="stylesheet" href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css"> <!-- load bootstrap css -->
	<link rel="stylesheet" href="http://libs.baidu.com/fontawesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
	<style>
		body { padding-top:80px; }
	</style>
</head>
<body>
    <div class="container">
        <div class="col-sm-6 col-sm-offset-3">
            <h1><span class="fa fa-sign-in"></span> Login</h1>
            <!-- LOGIN FORM -->
            <form action="/login" method="post">
                <div class="form-group">
                    <label>Username</label>
                    <input type="text" class="form-control" name="username">
                </div>
                <div class="form-group">
                    <label>Password</label>
                    <input type="password" class="form-control" name="password">
                </div>
                <div class="checkbox">
                    <label>
                        <input type="checkbox" name="remember" value="yes">Remember Me
                    </label>
                </div>
                <button type="submit" class="btn btn-primary btn-lg">Login</button>
            </form>
            <hr>
            <p>Need an account? <a href="/signup">Signup</a></p>
            <p>Or go <a href="/">home</a>.</p>
        </div>
    </div>
</body>
</html>

注册页面signup.ejs

<!-- views/signup.ejs -->
<!doctype html>
<html>
<head>
	<title>Node Authentication</title>
	<link rel="stylesheet" href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css"> <!-- load bootstrap css -->
	<link rel="stylesheet" href="http://libs.baidu.com/fontawesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
	<style>
		body 		{ padding-top:80px; }
	</style>
</head>
<body>
<div class="container">
<div class="col-sm-6 col-sm-offset-3">
	<h1><span class="fa fa-sign-in"></span> Signup</h1>
	<!-- LOGIN FORM -->
	<form action="/signup" method="post">
   <div class="form-group">
    <label>Username</label>
    <input type="text" class="form-control" name="username">
   </div>
   <div class="form-group">
    <label>Password</label>
    <input type="password" class="form-control" name="password">
   </div>
   <div class="form-group">
    <label>Confirm Password</label>
    <input type="password" class="form-control" name="repassword">
   </div>

   <button type="submit" class="btn btn-primary btn-lg">Signup</button>
	</form>
	<hr>
	<p>Already have an account? <a href="/login">Login</a></p>
	<p>Or go <a href="/">home</a>.</p>
</div>
</div>
</body>
</html>

创建路由文件routes/login.js,routes/signup.js

//`routes/login.js`
var express = require('express')
var router = express.Router()
router.get('/', function (req, res) { 
 res.render('login')
})
module.exports = router
//`routes/signup.js`
var express = require('express')
var router = express.Router()
router.get('/', function (req, res) { 
 res.render('signup')
})
module.exports = router

修改app.js

//添加路由
var loginRouter = require('./routes/login');
var signupRouter = require('./routes/signup');

//挂载路由
app.use('/login', loginRouter);
app.use('/signup', signupRouter);

完成以上,我们创建的登录页面http://localhost:8100/login和注册页面http://localhost:8100/signup就可以访问了。

引入数据库

先在数据库中创建一个表用来存储信息,我创建的这个表名是userinfo,结构如下图
数据表

在项目中安装mysql

npm install mysql --save

创建数据库配置文件models/db.config.js,引入数据库mysql,创建数据库连接池。

//引入数据库
const mysql = require("mysql");
var connection = mysql.createPool({
  host: 'localhost',
  user: 'root',
  password: '123456',
  database:'User'//指定要操作哪个数据库
});

module.exports = connection;

获取数据库连接

使用数据库连接池,创建models/user.model.js文件
1.这里我先创建User数据对象,用于接收注册时所填账号和密码。
2.监听数据库是否连接成功
3.getConnection获取数据库连接
4.在User对象上添加一个signup函数,在路由中使用函数回调的方式返回注册结果
5.编写sql插入数据操作语句,在最后使用callback回调函数返回操作结果

const sql = require("./db.config.js");
// constructor
const User = function(user) {
 this.username = user.username;
 this.password = user.password;
};

sql.on('connection', function () { 
 console.log('数据库连接成功')
})

sql.getConnection(function (err,connection) {
 //注册用户
 User.signup = function (newUser,callback) {  
  const sql = "INSERT INTO userinfo(id,username,password) VALUES(0,?,?)";
   connection.query(sql,[newUser.username,newUser.password],function(err,result){
    if(err){
     console.log("USE Error:"+err.message);
     return;
    }
    console.log("注册用户");
    callback(err,result);
   });
 }

 //根据用户名查找用户
 User.findByUser = function (username,callback) { 
  const sql = "SELECT * FROM userinfo WHERE username =?"
  connection.query(sql, [username], function (err,result) { 
      if(err){
				console.log("findByUser Error:"+err.message);
				return;
			}
			console.log("根据用户名查找用户");
			callback(err,result);
  })
 }

})

module.exports = User

由于在注册的时候,往往会提示我们账号是否已经使用,所以在注册账号时需要通过用户名在数据库查找是否存在账号,如果不存在则注册成功,存在则提示更换新账号。
因此findByUser函数的作用就是在注册时根据用户名查找用户。

注册路由实现

1.注册需要将新用户的账号和密码写入数据库,账号可以直接写入数据库,但密码一般不会直接存入到数据库中,会将密码加密后存入数据库中,能够提高账号的安全性。
2.密码加密用到一个包 crypto ,可以直接引入。

//routes/signup.js
var express = require('express')
var router = express.Router()
var crypto = require('crypto')
var User = require('../models/user.model')
var TITLE_REG = '注册';

//页面渲染
router.get('/', function (req, res) { 
 res.render('signup', { title: TITLE_REG })
})

//注册表单提交
router.post('/', function (req, res) {
 var password = req.body.password,
     username = req.body.username
 //密码加密
 var md5 = crypto.createHash('md5')
 var userPwd = md5.update(password).digest('hex')
 //创建一个数据库操作对象(User)
 console.log(userPwd,userPwd.length)
 const user = new User({
     username: username,
     password: userPwd
 });
 //数据库查找用户名称是否存在
 User.findByUser(username, (err, data) => { 
  if (err) {
    res.locals.error = err;
    res.render('signup', { title: TITLE_REG });
   return;
  }
  if (data != null && data.length > 0) {
   res.locals.error =  "用户名已存在"
   res.render('signup', { title: TITLE_REG });
   return;
  }
  //注册
  User.signup(user, (err, data) => { 
   if (err) { 
     res.locals.error = err;
     res.render('signup', { title: TITLE_REG });
      return
   }
    res.locals.success = '注册成功,请点击   <a class="btn btn-link" href="/login" role="button"> 登录 </a>' ;
    res.render('signup', { title: TITLE_REG });
  })
 })
 
})
module.exports = router

注册页面效果

前面搭建前端页面时已经写好了静态页面,这里展示注册页面最终代码。

<!-- views/signup.ejs -->
<!DOCTYPE html>
<html>
  <head>
    <title><%-title%></title>
    <link
      rel="stylesheet"
      href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css"
    />
    <!-- load bootstrap css -->
    <link
      rel="stylesheet"
      href="http://libs.baidu.com/fontawesome/4.0.3/css/font-awesome.min.css"
    />
    <!-- load fontawesome -->
    <style>
      body {
        padding-top: 80px;
      }
    </style>
  </head>
  <body>
    <div id="container" class="container">
      <div class="col-sm-6 col-sm-offset-3">
        <h1><span class="fa fa-sign-in"></span> Signup</h1>
        <% if(locals.success){%>
        
<%- success %>
<%}%> <%if(locals.error){%>
<%- error %>
<%}%> <!-- LOGIN FORM --> <form action="" method="post"> <div class="form-group"> <label>Username</label> <input type="text" class="form-control" id="username" name="username" /> </div> <div class="form-group"> <label>Password</label> <input type="password" class="form-control" id="password" name="password" /> </div> <div class="form-group"> <label>Confirm Password</label> <input type="password" class="form-control" id="repassword" name="repassword" /> </div> <button id="btnSub" type="submit" class="btn btn-primary btn-lg"> Signup </button> </form> <hr /> <p>Already have an account? <a href="/login">Login</a></p> <p>Or go <a href="/">home</a>.</p> </div> </div> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script type="text/javascript"> $(function () { //禁用“确认重新提交表单” window.history.replaceState(null, null, window.location.href); $('#btnSub').on('click', function () { var usernameVal = $.trim($('#username').val()), passwordVal = $.trim($('#password').val()), repasswordVal = $.trim($('#repassword').val()), errorTip = '<div id="errorTip" class="alert alert-warning"></div> '; $('#errorTip').remove(); if (usernameVal.length == 0) { $('#container').prepend(errorTip); $('#errorTip').text('用户名不能为空'); $('#username').focus(); return false; } if (passwordVal.length == 0) { $('#container').prepend(errorTip); $('#errorTip').text('密码不能为空'); $('#password').focus(); return false; } if (repasswordVal.length == 0) { $('#container').prepend(errorTip); $('#errorTip').text('确认密码不能为空'); $('#repassword').focus(); return false; } if (passwordVal != repasswordVal) { $('#container').prepend(errorTip); $('#errorTip').text('两次密码不一致'); $('#repassword').focus(); return false; } return true; }); }); </script> </body> </html>

注册页面

登录路由实现

在登录的时候再将密码通过同样的方式crypto进行加密,与数据库中的存储的密码进行比对,相同的话则登录成功。

//routes/login.js
var express = require('express')
var router = express.Router()
var crypto = require('crypto')
var User = require('../models/user.model')
var TITLE_LOGIN = '登录';

//页面渲染
router.get('/', function (req, res) { 
  res.render('login', { title: TITLE_LOGIN })
})
//用户登录
router.post('/', function (req, res) { 
 //接收登录参数
  var password = req.body.password,
    username = req.body.username,
    isRem = req.body.remember,
    md5 = crypto.createHash('md5');
  //根据用户名查找用户判断是否存在
  User.findByUser(username, function (err,result) { 
    if (err) {
      res.locals.error = err;
      res.render('login', { title: TITLE_LOGIN });
     return;
    }
    password = md5.update(password).digest('hex');
    if (result.length == 0) { 
      res.locals.error = '用户不存在';
      res.render('login',{title:TITLE_LOGIN});
      return;
    }
    //判断用户名密码是否和数据库相同
    if (result[0]['username'] != username || result[0]['password'] != password) {
      res.locals.error = '用户名或密码有误';
      res.render('login', { title: TITLE_LOGIN });
      return;
    } else { 
     //cookie记住登录状态
      if(isRem){
         res.cookie('islogin', username, { maxAge: 360000 });                 
      }
      res.locals.username = username;
      req.session.username = res.locals.username;  
      console.log(req.session.username);                        
      res.redirect('/');
    }
  })
})

module.exports = router

最后登录成功的状态是存在session中的,这样在刷新的时候就能保持登录状态了。

登录页面效果

前面搭建前端页面时已经写好了静态页面,这里展示登录页面最终代码。

<!-- views/login.ejs -->
<!DOCTYPE html>
<html>
  <head>
    <title><%-title%></title>
    <link
      rel="stylesheet"
      href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css"
    />
    <!-- load bootstrap css -->
    <link
      rel="stylesheet"
      href="http://libs.baidu.com/fontawesome/4.0.3/css/font-awesome.min.css"
    />
    <!-- load fontawesome -->
    <style>
      body {
        padding-top: 80px;
      }
    </style>
  </head>
  <body>
    <div id="container" class="container">
      <div class="col-sm-6 col-sm-offset-3">
        <h1><span class="fa fa-sign-in"></span> Login</h1>
        <% if(locals.success){%>
        
<%- success %>
<%}%> <%if(locals.error){%>
<%- error %>
<%}%> <!-- LOGIN FORM --> <form action="" method="post"> <div class="form-group"> <label>Username</label> <input type="text" class="form-control" id="username" name="username" /> </div> <div class="form-group"> <label>Password</label> <input type="password" class="form-control" id="password" name="password" /> </div> <div class="checkbox"> <label> <input type="checkbox" name="remember" value="yes" />Remember Me </label> </div> <button id="btnSub" type="submit" class="btn btn-primary btn-lg"> Login </button> </form> <hr /> <p>Need an account? <a href="/signup">Signup</a></p> <p>Or go <a href="/">home</a>.</p> </div> </div> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script type="text/javascript"> $(function () { //禁用“确认重新提交表单” window.history.replaceState(null, null, window.location.href); $('#btnSub').on('click', function () { var usernameVal = $.trim($('#username').val()), passwordVal = $.trim($('#password').val()), errorTip = '<div id="errorTip" class="alert alert-warning"></div> '; $('#errorTip').remove(); if (usernameVal.length == 0) { $('#container').prepend(errorTip); $('#errorTip').text('用户名不能为空'); $('#username').focus(); return false; } if (passwordVal.length == 0) { $('#container').prepend(errorTip); $('#errorTip').text('密码不能为空'); $('#password').focus(); return false; } return true; }); }); </script> </body> </html>

登录页面

express-session保存登录状态

session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而 session 保存在服务器上。
安装 express-session

npm install express-session --save

app.js中引入
cookie-parser(express默认已安装)

//...
var cookieParser = require('cookie-parser');
var session = require('express-session')
//...
var indexRouter = require('./routes/index');
var loginRouter = require('./routes/login');
var logoutnRouter = require('./routes/logout');
var signupRouter = require('./routes/signup');
//...
app.use(cookieParser("huhao"));
//使用session中间件
app.use(session({
  secret: 'huhao',// 相当于是一个加密密钥,值可以是任意字符串
  resave:true, // 强制session保存到session store中
	saveUninitialized:false// 强制没有“初始化”的session保存到storage中
}))

app.use('/', indexRouter);
app.use('/login', loginRouter);
app.use('/logout', logoutnRouter);
app.use('/signup', signupRouter);

这里还引入和挂载了退出登录相关路由logout,后面会创建对应的文件。

首页路由实现

进入页面通过session.username判断是否登录,如果存在进入首页,不存在进入登录页面。

//routes/index.js
var express = require('express');
var router = express.Router();
var moment = require('moment')
var News = require('../models/news.model')
var TITLE_INDEX = '新闻'

/* GET home page. */
router.get('/', function (req, res) {
  if(req.cookies.islogin){
    console.log('cookie:'+req.cookies.islogin);
    req.session.username = req.cookies.islogin;
  }
  if(req.session.username){
    console.log('session:'+req.session.username);
    res.locals.username = req.session.username;
  }else{
    res.redirect('/login');
    return;
  }
  res.render('index', { title: TITLE_INDEX });
});
module.exports = router;

退出登录

通过destroy()方法清空session数据,跳转到登录页面。
创建routes/logout.js文件

var express = require("express");
router = express.Router();

router.get('/',function(req,res){
	req.session.destroy();
	res.redirect('/login');
});

module.exports = router;

完成以上步骤就实现了整个用户注册和登录的功能。

实现新闻管理

1.先在数据库中创建一个表用来存储信息,我创建的这个表名是news,结构如下图
新闻数据表
2.连接新闻数据库,创建models/news.model.js文件
3.创建News函数接收新增新闻的参数
4.写了一个公共的sql执行函数
5.获取数据库连接
6.编写新增,删除,修改,查找的sql函数

const sql = require("./db.config.js");
// constructor
const News = function(news) {
 this.title = news.title;
 this.content = news.content;
 this.img = news.img;
 this.time = news.time
};

//sql执行函数
function querySql (connection,sql,data,callback) { 
 connection.query(sql, data, function (err,result) { 
  if(err){
   console.log("Error:"+err.message);
   return;
  }
  callback(err,result);
 })
}

sql.getConnection(function (err,connection) { 
   //新增
   News.add = function (newsData, callback) { 
    const sql = "INSERT INTO news(id,title,content,img,time) VALUES(0,?,?,?,?)";
    const data = [newsData.title, newsData.content, newsData.img, newsData.time]
    querySql(connection,sql,data,callback)
   }
   //删除
   News.delete = function (id, callback) { 
    const sql = "DELETE FROM news WHERE id=?";
    const data = [id]
    querySql(connection,sql,data,callback)
  }
   //修改
  News.update = function (newsData, callback) { 
    const sql = "UPDATE news SET title=?,content=?,img=?,time=? WHERE id=?";
    const data = [newsData.title,newsData.content,newsData.img,newsData.time,newsData.id]
    querySql(connection,sql,data,callback)
  }

   //根据id查找
  News.findNews = function (id, callback) { 
    const sql = "SELECT * FROM news WHERE id=?";
    const data = [id]
    querySql(connection,sql,data,callback)
  }
 
  //倒序查找所有新闻
  News.findNewsAll = function (callback) { 
   const sql = "SELECT * FROM news order by time desc";
   querySql(connection,sql,null,callback)
 }
})

module.exports = News

首页新闻路由功能实现

这里图片我是直接写死使用网络的图片,新增新闻时只能添加新闻标题和新闻内容。

//routes/index.js
var express = require('express');
var router = express.Router();
var moment = require('moment')
var News = require('../models/news.model')
var TITLE_INDEX = '新闻'

/* GET home page. */
router.get('/', function (req, res) {
  if(req.cookies.islogin){
    console.log('cookie:'+req.cookies.islogin);
    req.session.username = req.cookies.islogin;
  }
  if(req.session.username){
    console.log('session:'+req.session.username);
    res.locals.username = req.session.username;
  }else{
    res.redirect('/login');
    return;
  }
  //查询新闻列表
  News.findNewsAll(function (err, result) { 
    if (err) {
      res.locals.error = err
      res.render('index', { title: TITLE_INDEX, newList:[] });
      return;
    }
    res.render('index', { title: TITLE_INDEX, newList:result });
  })
});

// 添加新闻
router.post('/add', function (req, res) {
  const newsData = new News({
    title: req.body.title,
    content: req.body.content,
    img: 'http://n.xxxx.cn/news/1_img/upload/73a76861/105/w1023h682/20221218/c75c-b2d14d7c81670c1601b5f657ddac3542.jpg',
    time:moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
  })
  console.log(newsData)
  News.add(newsData, function (err, result) { 
    if (err) {
      res.send({
          status:400,
          msg:err
      })
     return;
    }
    res.send({
        status:200,
        msg:'success',
        data:result.insertId > 0
    })
  })
})

//删除
router.delete('/delete', function (req, res) { 
  News.delete(req.body.id, function (err, result) { 
    if (err) {
      res.send({
          status:400,
          msg:err
      })
     return;
    }
    res.send({
        status:200,
        msg:'success',
        data:true
    })
  })
})

// 根据id查询新闻
router.get('/findnews', function (req, res) { 
  News.findNews(req.query.id, function (err, result) { 
    if (err) {
      res.send({
          status:400,
          msg:err
      })
     return;
    }
    res.send({
      status:200,
      msg:'success',
      data:result[0] || null
    })
    
  })
})

//修改
router.put('/update', function (req, res) { 
  const data = {
    id:req.body.id,
    title: req.body.title,
    content: req.body.content,
    img: 'http://n.xxxx.cn/default/1_img/upload/3933d981/72/w1024h648/20221217/17f5-813de74b361d5679f078f0167faa6696.jpg',
    time:moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
  }
  News.update(data, function (err,result) { 
    if (err) {
      res.send({
        status:400,
        msg:err
      })
     return;
    }
    res.send({
      status:200,
      msg:'success',
      data:true
  })
  })
})
module.exports = router;

首页新闻展示效果

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel="stylesheet" href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css"> <!-- load bootstrap css -->
	   <link rel="stylesheet" href="http://libs.baidu.com/fontawesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
    <style>
      .header,.newItem,.new-info{
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      .new-info img{
        width: 100px;
        margin-right: 10px;
        border-radius: 5px;
      }
      .btn-nav{
        margin-bottom: 10px;
      }
      .nulldata{
        text-align: center;
        padding:50px 0;
        font-size:16px;
        color:#999;
      }
    </style>
  </head>
  <body>
    <% include header%>
    <div class="container">
      <div class="btn-nav">
        <button type="button" class="btn btn-primary btn-sm" onclick="addNews()">新增</button>
      </div>
      <ul class="list-group">
        <li class="list-group-item newItem">
          <div class="new-info">
            <img src="item.img" alt="">
            <div><%- item.title %></div>
          </div>
          <div>
            <a href="/news?id=item.id" class="btn btn-primary btn-sm active" role="button">详情</a>
            <button type="button" class="btn btn-warning btn-sm" onclick="updateFn('item.id')">修改</button>
            <button type="button" class="btn btn-danger btn-sm" onclick="deleteFn('item.id')">删除</button>
          </div>
        </li>
        <!---把上面li写在循环里面--->
        <!-- <%if(newList.length > 0){%>
            <% newList.forEach(function(item){ %>
            
          <% }) %>
        <%} else {%>
          <div class="nulldata">暂无数据</div>
        <% } %> -->
      </ul>
    </div>

    <!---新增 修改--->
    <div class="modal fade" tabindex="-1" role="dialog" id="addModal">
      <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" id="modal-body">
            <div class="form-group">
              <label for="exampleInputEmail1">新闻标题</label>
              <input type="email" class="form-control" id="newtitle" name="title" placeholder="请输入新闻标题">
            </div>
            <div class="form-group">
              <label for="exampleInputPassword1">新闻内容</label>
              <textarea class="form-control" rows="3" id="newcontent" name="content"></textarea>
            </div>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
            <button type="button" class="btn btn-primary" id="submit">确定</button>
          </div>
        </div><!-- /.modal-content -->
      </div><!-- /.modal-dialog -->
    </div><!-- /.modal -->
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script src="http://libs.baidu.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
    <script type="text/javascript">
      //显示新增
      function addNews(){
        $('#addModal').modal('show')
        $('.modal-title').text('新增新闻')
      }
      //删除新闻
      function deleteFn(id){
          $.ajax({
            url: "/delete",
            type: "delete",
            data: {id},
            success:function(res){
              if(res.data){
                window.location.reload()
              }
            },
            error:function(err){
              console.log(err)
            }
          })
        }
        //修改新闻
        let curNewsId = null // 当前要修改的新闻id
        function updateFn(id){
          curNewsId = id
          $('#addModal').modal('show')
          $('.modal-title').text('修改新闻')
          $.ajax({
            url:'/findnews',
            type:'get',
            data:{id},
            success:function(res){
              console.log(res)
              if(res.data){
                $('#newtitle').val(res.data.title)
                $('#newcontent').val(res.data.content)
              }
            },
            error:function(err){
              console.log(err)
            }
          })
        }
      $(function(){
        //添加新闻
        $("#submit").on('click',function(){
          var errorTip = '<div id="errorTip" class="alert alert-warning"></div> ';
          var successTip = '<div id="successTip" class="alert alert-success"></div> ';
            $("#errorTip,#successTip").remove();
          var title = $.trim($('#newtitle').val()),
          content = $.trim($('#newcontent').val())
          if(title.length == 0){
              $("#modal-body").append(errorTip); 
              $("#errorTip").text('新闻标题不能为空')              
              $('#newtitle').focus();
              return false;
          }
          if(content.length == 0){
              $("#modal-body").append(errorTip); 
              $("#errorTip").text('新闻内容不能为空')              
              $('#newcontent').focus();
              return false;
          }
          const data = {title,content}
          if(curNewsId){
            data.id = curNewsId
          }
          if($('.modal-title').text() == '修改新闻'){
            $.ajax({
                url: "/update",
                type: "put",
                data: data,
                success: function(res) {
                    console.log(res)
                    if(res.data){
                      window.location.reload()
                    }
                },
                error: function(err) {
                  console.log(err)
                  $("#modal-body").append(errorTip); 
                  $("#errorTip").text('添加失败') 
                }
            })
          } else {
            $.ajax({
                url: "/add",
                type: "post",
                data: data,
                success: function(res) {
                    console.log(res)
                    if(res.data){
                    window.location.reload()
                    }
                },
                error: function(err) {
                  console.log(err)
                  $("#modal-body").append(errorTip); 
                  $("#errorTip").text('添加失败') 
                }
            })
          }
          
        })
      })
    </script>
  </body>
</html>

首页新闻展示

新闻详情路由实现

创建routes/news.js文件

var express = require('express');
var router = express.Router();
var moment = require('moment')
var News = require('../models/news.model')
var TITLE_NEWS = '新闻详情'


 // 根据id查询新闻
router.get('/', function (req, res) { 
  if(req.cookies.islogin){
    console.log('cookie:'+req.cookies.islogin);
    req.session.username = req.cookies.islogin;
  }
  if(req.session.username){
    console.log('session:'+req.session.username);
    res.locals.username = req.session.username;
  }else{
    res.redirect('/login');
    return;
  }
  //新闻详情
 News.findNews(req.query.id, function (err, result) { 
     const newsData = {
      ...result[0]
     }
     newsData.time = moment(result[0]['time']).format('YYYY-MM-DD HH:mm:ss')
     res.render('news', { title: TITLE_NEWS,news:newsData })
  })
})
module.exports = router

新闻详情前端页面

创建views/news.ejs文件

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link
      rel="stylesheet"
      href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css"
    />
    <!-- load bootstrap css -->
    <link
      rel="stylesheet"
      href="http://libs.baidu.com/fontawesome/4.0.3/css/font-awesome.min.css"
    />
    <!-- load fontawesome -->
    <style>
      .header {
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
    </style>
  </head>
  <body>
    <% include header%>
    <div class="container">
      <div class="form-group">
        <label for="exampleInputEmail1">新闻标题</label>
        <div><%- news.title%></div>
      </div>
      <div class="form-group">
        <label for="exampleInputPassword1">新闻内容</label>
        <div><%- news.content%></div>
      </div>
      <div class="form-group">
        <label for="exampleInputPassword1">新闻图片</label>
        <div><img src="<%- news.img%>" alt=""></div>
      </div>
      <div class="form-group">
        <label for="exampleInputPassword1">时间</label>
        <div><%- news.time%></div>
      </div>
    </div>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script src="http://libs.baidu.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
    <script type="text/javascript"></script>
  </body>
</html>

完整的app.js文件

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var session = require('express-session')
var logger = require('morgan');

var indexRouter = require('./routes/index');
var loginRouter = require('./routes/login');
var logoutnRouter = require('./routes/logout');
var signupRouter = require('./routes/signup');
var newsRouter = require('./routes/news');

var app = express();

app.listen(8100, function () { 
  console.log('启动成功:http://localhost:8100')
})

// view engine setup
// 指定模版目录
app.set('views', path.join(__dirname, 'views'));
// 设置模版引擎,'view engine' 为固定属性
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser("huhao"));
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
  secret: 'huhao',
  resave:true,
	saveUninitialized:false
}))

app.use('/', indexRouter);
app.use('/login', loginRouter);
app.use('/logout', logoutnRouter);
app.use('/signup', signupRouter);
app.use('/news', newsRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

最后的目录结构

├── app.js
├── bin
│   └── www
├── models
│   ├── db.config.js
│   ├── news.model.js
│   └── user.model.js
├── node_modules
├── package-lock.json
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
├── routes
│   ├── index.js
│   ├── login.js
│   ├── logout.js
│   ├── news.js
│   └── signup.js
└── views
    ├── error.ejs
    ├── header.ejs
    ├── index.ejs
    ├── login.ejs
    ├── news.ejs
    └── signup.ejs

源码入口链接: Node-Express-mySql


  目录