https://github.com/unknowSir/nodejs-crud.git
一个页面数据的crud 具有分页 模糊查询 图片上传七牛 前端预览
前端 ajax+formData+html5-require原生验证+FileReader前端图片预览+bootstrap+bootstrap-bootstrap paginator+art-template
前言,就是想要做一个crud的demo,把上面的技术都走一走(主要是要走通nodejs+express+orm+xtemplate+FormData+ajax+FileReader)
/*
Navicat MySQL Data Transfer
Source Server : localhost_3306
Source Server Version : 50617
Source Host : localhost:3306
Source Database : cso2017
Target Server Type : MYSQL
Target Server Version : 50617
File Encoding : 65001
Date: 2017-12-22 10:31:12
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `media`
-- ----------------------------
DROP TABLE IF EXISTS `media`;
CREATE TABLE `media` (
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
`title` varchar(50) DEFAULT '',
`src` text,
`des` text,
`createTime` varchar(50) DEFAULT '',
`type` int(11) DEFAULT '-1' COMMENT '媒体类型\r\n-1 默认\r\n0 轮播图\r\n1 优秀标兵\r\n2 状态\r\n3 动态',
`isDelete` tinyint(1) unsigned zerofill DEFAULT '0' COMMENT '0 false 1 true',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of media
-- ----------------------------
INSERT INTO `media` VALUES ('1', '1', 'uploads/151375821335921420-106.jpg', '2', '1513758213360', '-1', '1');
INSERT INTO `media` VALUES ('2', '22', 'p1935eslc.bkt.clouddn.com1513772101394222.png', '22', '1513772102349', '1', '1');
INSERT INTO `media` VALUES ('3', '33', 'http://p1935eslc.bkt.clouddn.com/1513772179071222.png', '33', '1513772181066', '-1', '1');
INSERT INTO `media` VALUES ('4', '11', 'http://p1935eslc.bkt.clouddn.com/1513772206802222.png', '11', '1513772209600', '0', '1');
INSERT INTO `media` VALUES ('5', '11', '', '11', '1513772267496', '-1', '1');
INSERT INTO `media` VALUES ('6', '13', 'http://p1935eslc.bkt.clouddn.com/1513773929540222.png', '1', '1513772305577', '-1', '0');
INSERT INTO `media` VALUES ('7', '33', '', '33', '1513773221744', '-1', '1');
INSERT INTO `media` VALUES ('8', '44', '', '44', '1513773292398', '1', '1');
INSERT INTO `media` VALUES ('9', '44', 'http://p1935eslc.bkt.clouddn.com/1513773307440340372-106.jpg', '44', '1513773317109', '0', '0');
INSERT INTO `media` VALUES ('10', '55', 'http://p1935eslc.bkt.clouddn.com/1513773371196350902-106.jpg', '55', '1513773398180', '-1', '1');
INSERT INTO `media` VALUES ('11', '55', 'http://p1935eslc.bkt.clouddn.com/1513773517413222.png', '55', '1513773520467', '-1', '1');
INSERT INTO `media` VALUES ('12', '555', 'http://p1935eslc.bkt.clouddn.com/1513773586654222.png', '555', '1513773588739', '-1', '0');
INSERT INTO `media` VALUES ('13', '66', 'http://p1935eslc.bkt.clouddn.com/1513773621490222.png', '66', '1513773624133', '-1', '0');
INSERT INTO `media` VALUES ('14', '777', 'http://p1935eslc.bkt.clouddn.com/1513773632543222.png', '777', '1513773638194', '2', '0');
INSERT INTO `media` VALUES ('15', '8', 'http://p1935eslc.bkt.clouddn.com/151377400387837094-106.jpg', '8', '1513773982037', '-1', '0');
INSERT INTO `media` VALUES ('16', '9', '', '9', '1513907210481', '-1', '1');
INSERT INTO `media` VALUES ('17', '11', '', '11', '1513907333323', '-1', '1');
INSERT INTO `media` VALUES ('18', '12', '', '12', '1513907424628', '1', '1');
INSERT INTO `media` VALUES ('19', '33', '', '33', '1513907485859', '-1', '1');
INSERT INTO `media` VALUES ('20', '44', '', '44', '1513907597748', '-1', '1');
INSERT INTO `media` VALUES ('21', '55', '', '555', '1513907798788', '-1', '1');
INSERT INTO `media` VALUES ('22', '66', 'http://p1935eslc.bkt.clouddn.com/151390780567141077-106.jpg', '66', '1513907807289', '-1', '0');
INSERT INTO `media` VALUES ('23', '444', 'http://p1935eslc.bkt.clouddn.com/1513907892102337602-106.jpg', '444', '1513907895662', '1', '0');
INSERT INTO `media` VALUES ('24', '111', 'http://p1935eslc.bkt.clouddn.com/1513908485005337030-106.jpg', '1111', '1513908495119', '-1', '0');
/*
Navicat MySQL Data Transfer
Source Server : localhost_3306
Source Server Version : 50617
Source Host : localhost:3306
Source Database : cso2017
Target Server Type : MYSQL
Target Server Version : 50617
File Encoding : 65001
Date: 2017-12-19 22:39:54
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `media`
-- ----------------------------
DROP TABLE IF EXISTS `media`;
CREATE TABLE `media` (
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
`title` varchar(50) DEFAULT '',
`src` text,
`des` text,
`createTime` varchar(50) DEFAULT '',
`type` int(11) DEFAULT '-1' COMMENT '媒体类型\r\n-1 默认\r\n0 轮播图\r\n1 优秀标兵\r\n2 状态\r\n3 动态',
`isDelete` tinyint(1) unsigned zerofill DEFAULT '0' COMMENT '0 false 1 true',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of media
-- ----------------------------
INSERT INTO `media` VALUES ('1', '11', '1111', '111111', '11111111', '-1', '1');
INSERT INTO `media` VALUES ('2', '2', '22', '222', '22222', '-1', '1');
INSERT INTO `media` VALUES ('3', '1', 'uploads/37094-106.jpg', '3333', '1513678617873', '0', '0');
INSERT INTO `media` VALUES ('4', '2', 'uploads/295727-106.jpg', '333', '1513678633962', '1', '0');
INSERT INTO `media` VALUES ('5', '3', 'uploads/21420-106.jpg', '3333', '1513678676016', '-1', '0');
INSERT INTO `media` VALUES ('6', '4', 'uploads/37094-106.jpg', '111111', '1513678755337', '-1', '0');
INSERT INTO `media` VALUES ('7', '5', 'uploads/41077-106.jpg', '444', '1513678769503', '-1', '0');
INSERT INTO `media` VALUES ('8', '6', 'uploads/37094-106.jpg', '777', '1513679508958', '-1', '0');
INSERT INTO `media` VALUES ('9', '7', 'uploads/21420-106.jpg', '888', '1513679540648', '-1', '0');
INSERT INTO `media` VALUES ('10', '888', 'uploads/350947-106.jpg', '8888', '1513691658007', '2', '0');
INSERT INTO `media` VALUES ('11', '99', 'uploads/127678-106.jpg', '999', '1513691885585', '-1', '0');
INSERT INTO `media` VALUES ('12', '10', 'uploads/129643-106.jpg', '10', '1513691925412', '-1', '0');
INSERT INTO `media` VALUES ('13', '11', 'uploads/268223-106.jpg', '11', '1513691939124', '-1', '0');
INSERT INTO `media` VALUES ('14', '12', 'uploads/37094-106.jpg', '12', '1513692236017', '-1', '0');
这里选择的搭配是nodejs+express 4.x + orm +xtemplate
通过如下命令安装:
npm install express-generator -g
例如,下面的示例就是在当前工作目录下创建一个命名为 myapp 的应用。
express myapp
然后安装所有依赖包:
cd myapp
npm install
Windows 平台使用如下命令:
> set DEBUG=myapp & npm start
然后在浏览器中打开 http://localhost:3000/
网址就可以看到这个应用了。
还要安装其他依赖
orm mysql 对象关系模型框架
npm i orm --save
安装 xtemplate
npm i xtemplate --save
// 引入orm模块
var orm = require('orm');
// 设置静态文件目录
app.use(express.static('public'));
// 修改视图引擎jade改为xtemplate- 不喜欢jade怪异的模样
app.set('view engine', 'xtpl');
// 修改上传文件的大小 默认上传太小了 超过1MB都会报错 所以提前修改 报错信息提示:payload 提示太大
app.use(bodyParser.json({limit:'50mb'}));
app.use(bodyParser.urlencoded({limit:'50mb',extended:true}));
// 定义数据模型 连接mysql 将模型绑定到 req对象上
app.use(orm.express("mysql://root@localhost/cso2017", {
define: function (db, models, next) {
var media= db.define("media", {
// 字段都是和mysql中对应的
id: { type: 'serial', key: true },
title: { type: 'text' },
src: { type: 'text' },
des: { type: 'text' },
createTime: { type: 'text' },
type: { type: 'number' },
isDelete: { type: 'number' }
});
models.media=media;
next();
}
}));
// 用来指定路由处理的
app.use('/', index);
app.use('/users', users);
var express = require('express');
var qiniu = require('qiniu');
var router = express.Router();
// 我的七牛的accessKey-此处修改为自己的即可
var accessKey = 'xxxxxxx';
// 我的七牛的 secretKey - 此处修改为自己的即可
var secretKey = 'xxxxx';
var options = {
// 空间名字 buket
scope: "xxxx",
};
var putPolicy = new qiniu.rs.PutPolicy(options);
var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
// 上传凭证
var uploadToken = putPolicy.uploadToken(mac);
router.get("/token", function (req, res, next) {
console.log(uploadToken);
res.send({
"uptoken": uploadToken
});
})
module.exports = router;
var express = require('express');
var router = express.Router();
// 中间件 某则 req中获取不到上传参数(因为express 4.x 改变了 3.x获取参数的形式)
var multipart = require('connect-multiparty');
var multipartMiddleware = multipart();
var fs = require('fs');
var orm = require('orm');
router.get('/', function (req, res, next) {
res.render('user', { title: '我来拉' });
});
// 获取分页数据
router.get("/getList", function (req, res, next) {
// 获取分页数据
var page = +req.query.page;
var rows = +req.query.rows;
var title = req.query.title;
// 注意要传入的类型是数字
// limit 限制一共取几条 rows
// offset 往后偏移几条 下一页的条数
// mysql查询参数
var queryObj = { isDelete: "0", title: orm.like('%' + title + '%') };
req.models.media.find(queryObj).all().limit(rows).offset((page - 1) * rows).run(function (error0, medias) {
if (error0) res.send(error0);
// 计算分页后的总数据
req.models.media.count(queryObj, function (error1, count) {
if (error1) res.send(error1);
// medias.total = count;
var obj = {
data: medias,
total: count,
page: page,
rows: rows
};
res.send(obj);
});
})
});
// 删除
router.get("/del", function (req, res, next) {
var id = req.query.id;
req.models.media.get(id, function (err, oldData) {
if (err) throw err;
oldData.isDelete = 1;
oldData.save(function (err1) {
if (err1) throw err1;
res.send({ status: 0, msg: "删除成功" });
})
})
})
/* post 添加 */
router.post("/add", multipartMiddleware, function (req, res, next) {
var file = req.files.src;
var formBody = req.body;
formBody.src = formBody.src;
formBody.createTime = Date.now();
formBody.isDelete = 0;
req.models.media.create(formBody, function (err) {
if (err) throw err;
// 正常执行
res.send({ status: 0, msg: "新增成功" });
})
})
// 编辑
router.post("/edit", multipartMiddleware, function (req, res, next) {
/*
1 有修改了图片文件的 重新保存和提交
2 没有修改图片的 只修改字段即可
3 改用七牛云 直接存储字段即可
*/
var formBody = req.body;
req.models.media.get(formBody.id, function (err, oldData) {
oldData.title = formBody.title;
oldData.des = formBody.des;
oldData.type = formBody.type;
oldData.src = formBody.src;
// 保存数据 同步保存数据
oldData.save(function (err) {
if (err) throw err;
res.send({ status: 0, msg: "编F辑成功" });
});
});
})
/**
*
* @param {*要保存的文件} file
* @param {*文件保存的全路径} path abc/
* @param {*回调函数} callback 参数 imgPath 图片全路径
*/
function saveFile(file, path, callback) {
fs.readFile(file.path, function (err, data) {
if (err) {
console.log(err);
} else {
// 存储文件
var imgPath = "uploads/" + path + Date.now() + file.originalFilename;
fs.writeFile("public/" + imgPath, data, function (err1) {
if (err1) {
console.log(err1);
} else {
// 存入数据库
callback(imgPath);
}
});
}
});
}
module.exports = router;
$(function () {
// 往art-template中注册方法
template.helper("toJson", function (str) {
return JSON.stringify(str);
});
// 将数字转为 对应的多媒体类型
template.helper("toType", function (num) {
var str = "默认";
switch (num) {
case -1:
break;
case 0:
str = "轮播图";
break;
case 1:
str = "优秀标兵";
break;
case 2:
str = "状态";
break;
case 3:
str = "动态";
break;
default:
break;
}
return str;
})
})
<!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>
<!-- 注意 因为在app.js中设置了 app.use(express.static('public')); 所以路径忽略public文件夹,直接到子层即可 -->
<link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="css/user.css">
</head>
<body>
<!-- 遮罩层 -->
<div class="mask"></div>
<div class="container">
<div>
<button class="btn btn-default" id="add">添加</button>
<button class="btn btn-default" id="reflash">刷新</button>
<div class="input-group">
<input type="text" class="form-control searchInp" placeholder="Search for...">
<span class="input-group-btn">
<button class="btn btn-default searchBtn" type="button">搜索</button>
</span>
</div>
</div>
<div>
<table class="table table-bordered"></table>
</div>
<div>
<nav aria-label="Page navigation">
<ul class="pagination"></ul>
</nav>
</div>
</div>
<!-- 模态框 -->
<div id="myModal" class="modal fade" tabindex="-1" role="dialog">
<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">×</span>
</button>
<h4 class="modal-title">操作数据</h4>
</div>
<div class="modal-body">
<form id="ff" class="form-horizontal" enctype="multipart/form-data">
<input type="hidden" name="id">
<div class="form-group">
<label for="title" class="col-sm-2 control-label">请输入标题</label>
<div class="col-sm-10">
<input required type="text" class="form-control" name="title" id="title" placeholder="Email">
</div>
</div>
<div class="form-group">
<label for="fileInp" class="col-sm-2 control-label">上传文件</label>
<div class="col-sm-10 " id="upParent">
<input required type="file" class="form-control" id="fileInp" placeholder="Email">
<input type="hidden" name="src" id="hiddenSrc">
</div>
</div>
<div class="form-group">
<div class="col-sm-10 col-sm-offset-2">
<input type="image" width="100" src="images/default.png" name="srcImage" id="src">
</div>
</div>
<div class="form-group">
<label for="type" class="col-sm-2 control-label">类型</label>
<div class="col-sm-10">
<select name="type" class="form-control" id="type">
<option value="-1">默认</option>
<option value="0">轮播图</option>
<option value="1">优秀标兵</option>
<option value="2">状态</option>
<option value="3">动态</option>
</select>
</div>
</div>
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">描述</label>
<div class="col-sm-10">
<textarea placeholder="请输入详细描述" required name="des" id="des" class="form-control"></textarea>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" id="canBtn" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" form="ff" id="subBtn" class="btn btn-primary">提交</button>
</div>
</div>
</div>
</div>
<!-- 删除模态框 -->
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog">
<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">×</span>
</button>
<h4 class="modal-title">警告</h4>
</div>
<div class="modal-body">
你确定要删除吗
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger delSubBtn ">删除</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.modal -->
<!-- art-template 模板引擎语法 -->
<script type="text/html" id="tblTpl">
{{%
<tr>
<td>序号</td>
<td>标题</td>
<td>路径</td>
<td>描述</td>
<td>创建时间</td>
<td>类型</td>
<td>操作</td>
</tr>
{{each data as value i}}
<tr data-value="{{toJson(value)}}" >
<td>{{(page-1)*rows+ i+1 }}</td>
<td>{{ value.title }}</td>
<td><img src="{{value.src}}" alt=""></td>
<td>{{ value.des }}</td>
<td>{{ value.createTime }}</td>
<td>{{ toType(value.type) }}</td>
<td><button class="btn btn-warning dataEditBtn " data-id="{{value.id}}" >编辑</button> <button data-id="{{value.id}}" class="btn btn-danger dataDelBtn" >删除</button> </td>
</tr>
{{/each}}
%}}
</script>
<script src="lib/jquery.min.js"></script>
<script src="lib/bootstrap/js/bootstrap.min.js"></script>
<!-- 分页插件 -->
<script src="lib/bootstrap-paginator.min.js"></script>
<script src="lib/template.js"></script>
<script src="js/common.js"></script>
<script src="js/user.js"></script>
</body>
</html>
table img {
width: 100%;
max-width: 100px;
}
.mask {
position: fixed;
width: 100%;
height: 100%;
opacity: .5;
background-color: #000;
z-index: 100000;
}
.mask::after {
display: block;
content: "";
position: absolute;
width: 10px;
height: 10px;
border: 1px solid #fff;
background-color: blue;
border-radius: 50%;
left: 50%;
top: 50%;
margin-left: -50px;
margin-top: -50px;
transform-origin: 45px 45px;
animation: ani_loading .5s infinite linear;
}
@keyframes ani_loading {
0% {}
100% {
transform: rotate(360deg);
}
}
$(function () {
// 查询参数
var QueryPage = {
page: 1,
rows: 5,
title: ""
};
// 操作类型
// 0 新增 1 编辑 2 删除 默认为0 新增
var ManagerType = 0;
// 总页数
var TotalPages = 1;
// 要删除数据的id
var DelID = -1;
getList(setPage);
function getList(callback) {
QueryPage.title = $.trim($(".searchInp").val());
$.get("users/getList", QueryPage, function (result) {
// 计算总页数
TotalPages = Math.ceil(result.total / QueryPage.rows);
var html = template("tblTpl", result);
$("table").html(html);
callback && callback();
})
}
function setPage(params) {
// bs 分页插件设置
var options = {
bootstrapMajorVersion: 3,
currentPage: QueryPage.page,//当前页面
numberOfPages: QueryPage.rows,//一页显示几个按钮(在ul里面生成5个li)
totalPages: TotalPages,//总页数
onPageClicked: function (event, originalEvent, type, page) {
QueryPage.page = page;
getList(setPage);
}
}
$(".pagination").bootstrapPaginator(options);
}
// 搜索
$(".searchBtn").click(function (params) {
QueryPage.page = 1;
getList(setPage);
// 点击搜索后 设置分页插件自动回到第一页
$(".pagination").bootstrapPaginator("showFirst");
})
// 选择图片后,在前端即时预览
$("#fileInp").change(function (e) {
// console.log(e);
var file = e.target.files[0];
var fr = new FileReader();
fr.readAsDataURL(file);
fr.onload = function () {
$("#src").attr("src", fr.result);
}
})
// 编辑
$("table ").on("click", ".dataEditBtn", function (e) {
/*
0 存入当前操作提示 编辑 ManagerType=1
1 弹出对话框
2 填充表单
3 去除 文件上传按钮 required属性
*/
ManagerType = 1;
$("#myModal").modal("show");
// 获取被操作的行上的元数据 data-value
//parentsUntil 查找当前元素的所有的父辈元素,直到遇到匹配的那个元素为止。 包含了下面所有找到的父辈元素,但不包括那个选择器匹配到的元素。
var obj = $(e.target).parents("tr").data("value");
// 2 填充数据
$("input[name='title']").val(obj.title);
$("input[name='id']").val(obj.id);
// $("input[name='src']").val(obj.src);
$("[name='des']").val(obj.des);
$("[name='type']").val(obj.type);
// 图片按钮
$("input[name='srcImage']").attr("src", obj.src);
// 3
// $("input[type='src']").removeAttr("required");
// 可能存在编辑文件时 没重新提交图片 所以去除验证
$("#fileInp").removeAttr("required");
})
// 新增
$("#add").click(function (e) {
/*
1 操作类型
2 弹出对话框
3 清空表单
4 添加文件上传按钮 required属性
*/
ManagerType = 0;
$("#myModal").modal("show");
$("input[name='src']").attr("required", "");
$("#ff")[0].reset();
// 清除图片路径的隐藏于
$("input[name='src']").val("");
// 清除图片按钮
$("input[type='image']").attr("src", "");
$("input[type='image']").val("");
})
// 提交
$("#ff").submit(function (e) {
/*
1 发送了3个ajax a 请求上传图片的token b 上传到七牛 获取图片链接 c 将数据插入到后台
*/
e.preventDefault();
// formData.append("src", $("input[name='src']").val());
// 判断操作类型 0 为新增 1 为编辑 2 为删除
if (ManagerType == 0) {
// 获取最新的token
$.get("/qiniu/token", function (token) {
var file = $("#fileInp")[0].files[0];
// 构造formdata 上传到七牛使用
var f = new FormData();
f.append("key", Date.now() + file.name);
f.append("token", token.uptoken);
f.append("file", file);
$.ajax({
url: "http://upload.qiniup.com/",
type: "POST",
data: f,
cache: false,
// 不用jq处理
processData: false,
contentType: false,
success: function (r) {
// console.log(r);
// 外链名字
var namepace = "http://p1935eslc.bkt.clouddn.com/";
// 图片的完整名字
var imgPath = namepace + r.key;
// 构造参数,添加到后台
$("input[name='src']").val(imgPath);
var formData = new FormData($("#ff")[0]);
$.ajax({
url: "/users/add",
type: "POST",
data: formData,
cache: false,
// 不用jq处理
processData: false,
contentType: false,
success: function (result) {
// console.log(e);
// 关闭对话框 刷新数据列表
$("#myModal").modal("hide");
QueryPage = {
page: 1,
rows: 5
};
getList(setPage);
},
error: function (e) {
// console.log(e);
}
});
}
});
});
} else if (ManagerType == 1) {
// 编辑
var formData = new FormData($("#ff")[0]);
$.ajax({
url: "/users/edit",
type: "POST",
data: formData,
cache: false,
// 不用jq处理
processData: false,
contentType: false,
success: function (result) {
// console.log(e);
// 关闭对话框 刷新数据列表
$("#myModal").modal("hide");
QueryPage = {
page: 1,
rows: 5
};
getList(setPage);
},
error: function (e) {
// console.log(e);
}
})
}
});
// 删除
$("table").on("click", ".dataDelBtn", function (e) {
var id = $(e.target).parents("tr").data("value").id;
DelID = id;
$("#deleteModal").modal("show");
});
// 提交删除
$(".delSubBtn").click(function (e) {
/*
1 发送删除请求
2 关闭对话框
3 刷新数据
*/
$.get("users/del?id=" + DelID, function (result) {
$("#deleteModal").modal("hide");
if (result.status == 0) {
// 成功
QueryPage.page = 1;
getList(setPage);
}
})
})
})
// 显示进度条
$(window).ajaxStart(function () {
$(".mask").show();
})
// 隐藏进度条
$(window).ajaxStop(function () {
$(".mask").hide();
})
整个demo download下后,需要修改的地方有
数据库连接代码 在app.js中
七牛的密钥等,在router/qiniu.js 中
环境: nodejs 8.3.0 express 4.x 其他 都是最新版本.
如有错误,联系方式 不懂先生 yeah126139163@163.com