前言
我想做一个简单的备忘录网站。
基础环境与代码
基础环境
环境搭建
首先是用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的反向代理。
最后就大功告成!
商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢