网站搭建 / 计算机技术 · 2021年5月23日 0

基于Express的网页——复盘

前言

我想做一个简单的备忘录网站。

 

 

 


基础环境与代码

基础环境

环境搭建

首先是用npm init初始化一个环境。

npm init

然后安装express

npm install express

好了,我们先把express安装好。

目录结构

我们的项目的目录结构大致如下:

基本代码

静态登录页面

首先是静态登录页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>复盘空间</title>

    <link href="./css/mainface.css" type="text/css" rel="stylesheet">
    <link href="./css/star.css" type="text/css" rel="stylesheet">
    
</head>
<body>
    <!--创建画布-->
    <canvas id="mycanvas"></canvas>
    <div id="loginPanel">
        <h1><span class="title">复盘空间<span></h1>
        <form action="user/login" method="POST">
            <p>管理员: <input type="text" name="username" /></p>
            <p>密码: <input type="password" name="password" /></p>
            <input type="submit" value="提交" class="sub-button" />
        </form>
    </div>

    <script type="text/javascript" src="./js/star.js"></script>
</body>
</html>

下面给出我的样式(以及一个星空的背景效果)

//mainface.css
body{
    text-align:center; 
}
canvas{
	position:fixed;/*设置定位*/
	top:0;
	left:0;
	z-index:-1;/*使画布基于最低层*/
	background:#0e1729;/*画布背景色*/
}

#loginPanel{
    background-color: rgb(255, 255, 255);
    height: auto;
    width: 30%;
    margin:5% auto;

    padding: 2%;

    opacity: 70%;
}

.title{
    font-size: xx-large;

    background-image: -webkit-gradient(linear, 
    0 0, 0 bottom, 
    from(rgb(34, 34, 24)), 
    to(rgb(87, 87, 87)));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent
}

.sub-button{
    color: rgb(253, 253, 253);

    width: 30%;
    height: auto;

    border:1px solid #333;
    box-shadow: 0 1px 2px #8b8b8b inset,0 -1px 0 #3d3d3d inset,0 -2px 3px #8b8b8b inset;
    background: -webkit-linear-gradient(top,#656565,#4c4c4c);
    background: -moz-linear-gradient(top,#656565,#4a4a4a);
    background: linear-gradient(top,#656565,#4a4a4a);    
}
//star.css
html {
	background-image:-webkit-radial-gradient(ellipse farthest-corner at center top,#000d4d 0%,#000105 100%);
	background-image:radial-gradient(ellipse farthest-corner at center top,#000d4d 0%,#000105 100%);
}

以及在static/js目录下的星空效果js:

//star.js
var canvas = mycanvas;
/*获取屏幕宽高。用作适配*/
var w = window.innerWidth;
var h = window.innerHeight;
canvas.width = w;
canvas.height = h;
canvas.backgroundColor = "#000";
var ctx = canvas.getContext('2d');
 
function Build() {
    this.ctx = ctx;
    this.counts = 300; //最大粒子数
    this.maxSize = 4; //初始化最大的大小
    this.halfWidth = w / 2,
        this.halfHeight = h / 2;
    this.arr = []; //用于存储变量
}
Build.prototype.add = function(coor) {
    var grd = this.ctx.createRadialGradient(coor.x, coor.y, coor.size / 2, coor.x, coor.y, coor.size);
    grd.addColorStop(0, "white");
    grd.addColorStop(1, coor.color);
    this.ctx.fillStyle = grd;
    this.ctx.beginPath();
    this.ctx.arc(coor.x, coor.y, coor.size, 0, Math.PI * 2, true);
    this.ctx.transform(1, 0, 0, 1, 0, coor.z);
    this.ctx.closePath();
    this.ctx.fill();
}
Build.prototype.init = function() {
    this.run();
    this.render();
    this.animate();
}
Build.prototype.run = function() {
    var nums = 0;
    while (nums < this.counts) {
        var coor = {
            x: Math.ceil(Math.random() * w),
            y: Math.ceil(Math.random() * h),
            posx: Math.random() * w - this.halfWidth,
            posy: Math.random() * h - this.halfHeight,
            fl: 100,
            speed: Math.random() * 2,
            posz: Math.random() * 250,
            r: Math.ceil(Math.random() * this.maxSize),
            color: "rgba(" + Math.ceil(Math.random() * 255) + "," + Math.ceil(Math.random() * 255) + "," + Math.ceil(Math.random() * 255) + "," + Math.random() + ")"
        };
        this.arr.push(coor);
        nums++;
    }
}
Build.prototype.clear = function() {
    ctx.clearRect(0, 0, w, h);
}
Build.prototype.render = function() {
    this.clear();
    for (var item of this.arr) {
        this.draw(item);
    }
}
Build.prototype.animate = function() {
        var _this = this;
        this.render();
        /*api自带方法*/
        window.requestAnimationFrame(function() {
            _this.animate();
        });
    },
    Build.prototype.draw = function(item) {
        if (item.posz > -item.fl) {
            /*连续修改scale,保持变化,用于控制量子大小,在屏幕上的位置*/
            var scale = item.fl / (item.fl + item.posz);
            /*修改对应数据*/
            item.x = this.halfWidth + item.posx * scale;
            item.y = this.halfHeight + item.posy * scale;
            item.size = item.r * scale;
            item.posz -= item.speed;
        } else {
            /*初始化超出屏幕的量子。达成屏幕量子数量保持衡量的方法*/
            item.posz = Math.random() * 250;
        }
        this.add(item);
    }
var app = new Build();
app.init();
window.addEventListener('resize', function() {
    canvas.width = w = window.innerWidth;
    canvas.height = h = window.innerHeight;
}, false);

最后实现效果:

express基本路由

上面我们的登录表单发送到了router的login路由中,我们应该怎么分发这个路由呢?

我们的主文件server.js:

const express= require("express")
const bodyparser=require("body-parser")
const path=require('path')

//把express做一个实例化
const app=express()

let userRouter=require('./router/userRouter.js')

app.use('/user',userRouter)

app.use('/',express.static(path.join(__dirname,'./static')))

//通过express,开启了一个node的服务器(监听在3000端口)
app.listen(3000,()=>{
    console.log('server start')
})  

下面我们在router文件下,创建一个userRouter.js,来处理登录逻辑

const express= require("express")
//获取路由的实例
const router=express.Router()

router.post('/login',(req,res)=>{
    res.send('user add')
})

//对外打包
module.exports=router

我们启动express项目可以来测试一下:

node server.js

直接访问127.0.0.1:3000/index.html,点击提交按钮,就可以看到页面显示一个:user add,说明我们的逻辑没有问题,成功进入了login处理函数中。

处理登录逻辑

这里我们就需要去访问数据库操作了

首先创建一个数据库,然后创建一个管理员表:

CREATE DATABASE fupan;

CREATE TABLE admin(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32),
PASSWORD VARCHAR(32)
);

ok,我们来插入一个数据来测试

INSERT INTO admin(username,PASSWORD) VALUES('蝙蝠侠','123456');

好了,下面我们用nodejs来访问数据库来验证登录

 

回来代码端,我们先用npm引入mysql库:

npm install mysql

然后创建一个tools目录,创建一个mysql-excute.js,作为一个mysql工具文件。

// 1. 导入 mysql 模块
const mysql = require('mysql')
// 2. 建立与 MySQL 数据库的连接关系
const db = mysql.createPool({
    host: '127.0.0.1', // 数据库的 IP 地址
    user: 'root', // 登录数据库的账号
    password: '964939451', // 登录数据库的密码
    database: 'fupan', // 指定要操作哪个数据库
})

var sqltool = {
    //测试连接
    test: function(name){
        db.query('select 1', (err, results) => {
            // mysql 模块工作期间报错了
            if (err) return false;
                // 能够成功的执行 SQL 语句
            return true;
        })
    },
    //查询全部
    queryAll: function(table,func,errfunc){
        const sqlStr='select * from '+table;
        var resul;
        db.query(sqlStr,(err,results)=>{
            if(err)
                errfunc();
            else
                func(results);
        })
    }
}
//对外打包
module.exports=sqltool

OK,下面,我们再userRouter.js中写登录逻辑:

const express= require("express")
//获取路由的实例
const router=express.Router()

var sqltool = require('../tools/mysql-excute.js');

var verifyLogin=function(username,password,succ,fail,err){
    sqltool.queryAll("admin",(results)=>{
        for(var i=0;i<results.length;i++){
            if(results[i]['username']==username&&results[i]['password']==password){
                succ();
                return null;
            }
        }
        fail();
    },(results)=>{
        err();
    });
}

router.post('/login',(req,res)=>{
    var us=req.body['username'];
    var ps=req.body['password'];
    verifyLogin(us,ps,()=>{
        res.redirect("../idea/main");
    },()=>{
        res.redirect("../index.html");
    },()=>{
        res.redirect("../error.html");
    });
});

//对外打包
module.exports=router

其中,我们的index.html就是原登录界面、error就是出现问题后跳转的页面。

下面我们来创建一个新路由:

const express= require("express")
const bodyparser=require("body-parser")
const path=require('path')

//把express做一个实例化
const app=express()

let userRouter=require('./router/userRouter.js')
let ideaRouter=require('./router/ideaRouter.js')

app.use(bodyparser.urlencoded({extends:false}))

app.use('/user',userRouter)
app.use('/idea',ideaRouter)

app.use('/',express.static(path.join(__dirname,'./static')))

//通过express,开启了一个node的服务器(监听在3000端口)
app.listen(3000,()=>{
    console.log('server start')
})  

在route/ideaRouter.js:

const express= require("express")
//获取路由的实例
const router=express.Router()

var sqltool = require('../tools/mysql-excute.js');

router.get('/main',(req,res)=>{
    
});

//对外打包
module.exports=router

ok,我们将在这里写登录成功的逻辑。

 

 

主界面——所有复盘灵感

主界面计划显示所有的复盘的灵感,第一步还是先来设计数据库:

USE fupan;
CREATE TABLE idea(
id INT PRIMARY KEY AUTO_INCREMENT,
summary VARCHAR(80),
detail TEXT
);

summary是我们主要的经验、detail是关于那个经验的细节,像是这条经验如何而来等等。

在我们的主界面,我们只需要显示summary即可。

 

下面,我们来使用express的模板来方便渲染前端,我这里使用的是jade:

npm install jade

然后我们,在server.js中指定模板:

const express= require("express")
const bodyparser=require("body-parser")
const path=require('path')

//把express做一个实例化
const app=express()

let userRouter=require('./router/userRouter.js')
let ideaRouter=require('./router/ideaRouter.js')

app.use(bodyparser.urlencoded({extends:false}))

// view engine setup
app.set('views', './views');
app.set('view engine', 'jade');
app.engine('jade', require('jade').__express);

app.use('/user',userRouter)
app.use('/idea',ideaRouter)

app.use('/',express.static(path.join(__dirname,'./static')))

//通过express,开启了一个node的服务器(监听在3000端口)
app.listen(3000,()=>{
    console.log('server start')
})  

然后,我们在根目录下再创建一个views目录,再在其中创建一个模板——idea.jade:

doctype html
html
  head
    title= title
    link(rel='stylesheet',type='text/css',href='../css/main.css')
  body
    div(class="main-content")
        div(class="title")
            h1= title
        ul 
            -for(var i=1;i<=idList.length;i++) 
                li
                    a(href="detail?id="+idList[i-1])= ideaList[i-1]

关于jade的语法请看以前的文章或者自行百度。

然后我们来写路由处理——ideaRoute.js

const express= require("express")
//获取路由的实例
const router=express.Router()

var sqltool = require('../tools/mysql-excute.js');

router.get('/main',(req,res)=>{
    var idealist=[],idlist=[];
    sqltool.queryAll("idea",(results)=>{
        for(var i=0;i<results.length;i++){
            idlist.push(results[i]['id']);          
            idealist.push(results[i]['summary']);    
        }
        res.render('idea', {title:"复盘空间",idList:idlist,ideaList:idealist});
    },()=>{
        res.redirect("../error.html");
    })
});

router.get('/detail',(req,res)=>{
    res.send("查看详情~");
});

//对外打包
module.exports=router

这里实现的就是检阅数据库,将复盘idea全部以无序列表的方式显示出来。

当我们点击某一个idea想要查看详情的时候,就会发送get请求给/detail。

 

 

查看复盘灵感的详情细节

我们上面已经写好了处理路由,这里我们在views中创建一个jade模板——detail.jade:

doctype html
html
  head
    title= title
    link(rel='stylesheet',type='text/css',href='../css/main.css')
  body
    div(class="main-content")
        div(class="title")
            h3= idea
        span= detailContent

下面,我们来增加一个SQL工具,来通过id查找:

// 1. 导入 mysql 模块
const mysql = require('mysql')
// 2. 建立与 MySQL 数据库的连接关系
const db = mysql.createPool({
    host: '127.0.0.1', // 数据库的 IP 地址
    user: 'root', // 登录数据库的账号
    password: '964939451', // 登录数据库的密码
    database: 'fupan', // 指定要操作哪个数据库
})

var sqltool = {
    //测试连接
    test: function(name){
        db.query('select 1', (err, results) => {
            // mysql 模块工作期间报错了
            if (err) return false;
                // 能够成功的执行 SQL 语句
            return true;
        })
    },
    //查询全部
    queryAll: function(table,func,errfunc){
        const sqlStr='select * from '+table;
        db.query(sqlStr,(err,results)=>{
            if(err)
                errfunc();
            else
                func(results);
        })
    },
    //通过id查询
    queryById: function(table,id,func,errfunc){
        const sqlStr='select * from '+table+' where id='+id;
        db.query(sqlStr,(err,results)=>{
            if(err)
                errfunc();
            else
                func(results);
        })
    }
}
//对外打包
module.exports=sqltool

好的,下面来写路由处理

router.get('/detail',(req,res)=>{
    sqltool.queryById("idea",req.query['id'],(results)=>{
        res.render('detail', {title:"复盘空间",idea:results[0]['summary'],detailContent:results[0]['detail']});
    },()=>{
        res.redirect("../error.html");
    })
});

OK,通过点击idea查看细节的功能就实现了!

 

功能丰富

下面上面做出了一个基本的模子,下面我们来添加一些功能:

增加idea

首先是,我们希望可以直接在web端添加idea。

首先修改jade,将添加后表单发送到add路由:

doctype html
html
  head
    title= title
    link(rel='stylesheet',type='text/css',href='../css/main.css')
  body
    div(class="main-content")
      div(class="title")
        h1= title
      ul 
        -for(var i=1;i<=idList.length;i++) 
          li
            a(href="detail?id="+idList[i-1])= ideaList[i-1]
      br
      br
      br
      form(action="add",method="POST")
        input(type="text",name="newidea",value="新的人生经验")
        input(type="text",name="newdetail",value="经验出处")
        input(type="submit",value="添加",id="add")

好,下面我们来实现一个新的数据库操作——添加,在sqltool里面接着写:

    addIdea: function(newidea,newdetail,func,errfunc){
        const sql = "insert into idea(summary,detail) values(?,?)" //SQL语句
        const sqlParams = [newidea,newdetail] // 动态参数

        db.query(sql,sqlParams,(err,result)=>{
            if(err) {
                errfunc();
                return
            }
            func();
        })
    }

回到路由处理:

    sqltool.addIdea(req.body['newidea'],req.body['newdetail'],()=>{
        res.redirect("main");
    },()=>{
        res.redirect("../error.html"); 
    });

然后就成功了!

删除idea

修改idea.jade

doctype html
html
  head
    title= title
    link(rel='stylesheet',type='text/css',href='../css/main.css')
    script(src='../js/manipu.js')
  body
    div(class="main-content")
      div(class="title")
        h1= title
      ul 
        -for(var i=1;i<=idList.length;i++) 
          li
            a(href="detail?id="+idList[i-1])= ideaList[i-1]
            input(type="button",value="删除",name=idList[i-1],class="delBtn")
      br
      br
      br
      form(action="add",method="POST")
        input(type="text",name="newidea",value="新的人生经验")
        input(type="text",name="newdetail",value="经验出处")
        input(type="submit",value="添加",id="add")

然后在manipu.js中,编辑:

window.onload=function(){
   
    var delBtnList = document.getElementsByClassName("delBtn");
    for(var i=0;i<delBtnList.length;i++){
        delBtnList[i].onclick=function(){
            if(confirm("确定删除吗?"))
                 window.open("del?id="+this.name,"_self");
        }
    }		
}

这样,我们就将删除交给了路由。

转过来,我们来写一个数据库sqltool的操作:

    //删除
    delById: function(id,func,errfunc){
        const sql = "delete from idea where id=?"
        const sqlParams = [id]

        db.query(sql,sqlParams,(err,result)=>{
            if(err) {
                errfunc();
                return
            }
            func();
        })
    }

最后,把处理的路由写一下:

router.get('/del',(req,res)=>{
    sqltool.delById(req.query['id'],()=>{
        res.redirect("main");
    },()=>{
        res.redirect("../error.html"); 
    });
});

删除的工作,也大功告成。

 


中场休息

项目的主要内容就到这里了,当然,程序的健壮性什么的非常差,这就靠各位自行增补了,例如增加输入的前端拦截等。

下面我们来将项目部署到服务器上。

 


部署项目

折腾累了,我就直接部署到宝塔面板了。

  • 在宝塔上添加新的站点
  • 将项目文件解压到新站点目录
  • 安装PM2,打开进入配置

  • 设置PM2的node版本和依赖
  • 在站点中修改配置文件,添加:
    location / {
	   proxy_pass http://127.0.0.1:3000;
    }

这里的端口3000,是我们的node项目的监听端口,这里是nginx的反向代理。

 

 


最后就大功告成!

项目的github

 

 

 

商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢

是的,我就是计算机界的枭雄!