电商后台管理系统
准备工作
1.vscoude
2.node
3.mongodb
4.express
node安装及使用
node的安装参考: https://www.cnblogs.com/moluxi/p/13025592.html
mongo安装以及使用
mongo的安装参考:https://www.cnblogs.com/moluxi/p/13027549.html
express相关
express: https://www.expressjs.com.cn/
注
这种语句是在命令行运行的
构建项目
1.首先对自己安装的node版本进行测试,8.0以下版本不能使用项目的相关模块
node -v
如果版本过低,http://nodejs.cn/download/ 下载新的版本(windows:控制面板-程序-node-卸载)
2.使用国内的淘宝镜像源(下载的速度会快些)
测试淘宝的镜像源
cnpm -v
如果报错 不是内部或者外部命令说明没有安装,需要安装一下
npm install -g cnpm --registry=https://registry.npm.taobao.org
3.安装项目脚手架(用express)
https://www.expressjs.com.cn/starter/generator.html
全局安装espress
cnpm i express-generator -g
express --help
创建项目
利用express快速生成项目框架
express myapp --view=ejs //myapp为你的项目名
进入生成的项目目录下
cd myapp
安装所需依赖
cnpm i
运行项目
cnpm run start
express项目目录理解
- myapp
- bin
www --- 设置了服务器的端口号,默认为3000,可以随意更改3000/ 4200/8080/8000/9000
- public---静态的资源目录,存放css,js,网页,图片,可以直接通http://localhost:3000/images/1.jpg
- images
- javascripts
- stylesheets
- routes --- 定义路由 /login
index.js
users.js
- views --- 各个页面,后缀名不再是html
error.ejs
index.ejs
app.js --- 服务器的准备
package.json --- 项目的描述文件,为什么可以使用cnpm run start 启动项目
准备一个后台管理系统的模板(这里用admin-let)
首先复制admin-lte模版中的starter.html的内容
复制到 myapp/views/index.ejs、观察 head部分 查看引入的css的路径,找到需要的css文件夹bootsrtap + dist + font-awesome + Ionicons将这几个文件夹复制到 myapp/public 修改index.ejs的路径,以 / 开头, 此 / 其实可以看作就是public的目录、同理,引入相关的js文件。
抽离页面结构
仔细观察代码可以看出,index.ejs页面结构可以分为四个部分,分别将不同的部分存放在不同的文件夹中
头部 header.ejs
菜单 menu.ejs
底部 footer.ejs
右侧 sideBar.ejs
保留内容部分
将各个部分使用ejs模板语法添加到页面
-
语法:<%%>
-
<% js的语句(for / if ) %>
-
<%- val %> ---- 解析输出
-
<%= val %> ---- 转义输出
添加头部的页面
<%- include('./header.ejs') %>
这样一来我们就能把页面重组回去,在更多类似的页面要使用时比较方便
myapp/views/index.ejs
<div class="wrapper">
<%- include('./header.ejs') %>
<%- include('./menu.ejs') %>
<div class="content-wrapper"></div>
<%- include('./footer.ejs') %>
<%- include('./sideBar.ejs') %>
</div>
设计左侧菜单栏
我们准备在侧边栏设置以下为几种功能的管理 首页 、轮播图管理、 商品管理 、用户管理
购物车管理 、订单管理 、活动管理 、消息管理
<!-- Sidebar Menu -->
<ul class="sidebar-menu" data-widget="tree">
<li class="header">HEADER</li>
<!-- 在这里先定义一些要使用的数据 -->
<% var arr = [
{ title: '首页',icon: '', path: ''},
{ title: '轮播图管理',icon: '', path: ''},
{ title: '商品管理',icon: '', path: ''},
{ title: '用户管理',icon: '', path: ''},
{ title: '购物车管理',icon: '', path: ''},
{ title: '订单管理',icon: '', path: ''},
{ title: '活动管理',icon: '', path: ''},
{ title: '消息管理',icon: '', path: ''}
] %>
<!-- 循环将各个标题的内容添加到页面的li中 -->
<% for(var i = 0; i < arr.length; i++) { %>
<li class="active"><a href="#"><i class="fa fa-link"></i> <span>
<%- arr[i].title %>
</span></a></li>
<% } %>
</ul>
<!-- /.sidebar-menu -->
这个时候我们已经有了很多选项卡可以操作啦,但是还没有任何的功能,由于我们做的是后端的管理系统用ajax局部刷新会对用户不太友好,我们可以直接把每个选项卡写一个页面,使页面跳转更加精致
我们可以复制index页面,并修改文件名和关键字从而得到每个选项卡所要对应的页面
index.ejs banner.ejs product.ejs users.ejs cart.ejs order.ejs activity.ejs message.ejs
创建各个页面对应的路由
有了页面,要想让页面有交互效果,我们就拍给他配置相应的路由在outes文件夹下新建与rviews相对应的js文件,像下面这种形式
routes views
activity.js activity.ejs
banner.js banner.ejs
....
在刚刚建好的这些js文件中配置"/"的当前路由
router.get('/', function(req, res, next) {
// 如果用户访问 / 返回index.ejs页面,并且返回变量title
res.render('index', { title: '1' }); //tiele用来表明是哪个选项卡
});
使各个路由生效
页面有了,路由也有了,下面我们就要让我们的路由生效啦
在app.js中引入路由文件并设置
//根据上下文写到相对应的位置就可以啦
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var productRouter = require('./routes/product'); // +++
var cartRouter = require('./routes/cart'); // +++
var bannerRouter = require('./routes/banner'); // +++
var orderRouter = require('./routes/order'); // +++
var activityRouter = require('./routes/activity'); // +++
var messageRouter = require('./routes/message'); // +++
...
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/activity', activityRouter); // +++
app.use('/banner', bannerRouter); // +++
app.use('/product', productRouter); // +++
app.use('/cart', cartRouter); // +++
app.use('/order', orderRouter); // +++
app.use('/message', messageRouter); // +++
左侧菜单添加 链接
依次添加所有页面相应选项卡的连接,这样当你点击的时候才能跳转到你想要的页面
高亮选中左侧菜单栏
要想让点击的选项卡高亮显示就要告诉他什么时候应该高亮,此时我们可以在页面加载的时候通过路由给页面传来一个数据,来说明当前页面是哪个页面,当点击一个页面的选项卡的时候跳转到该页面之后可以通过传来的值告诉页面此时哪个选项啊改高亮了
大题语法是这样的
res.render('activity', { index: 顺序(从0开始,注意和menu对应) });
在刚刚生成侧边栏nav的循环中通过传来的index值来判断是否高亮,判断是否高亮的代码
<% for(var i = 0; i < arr.length; i++) { %>
<li class="<%- index === i ? 'active' : ''%>"><a href="<%- arr[i].path %>"><i class="fa fa-link"></i> <span>
<%- arr[i].title %>
</span></a></li>
<% } %>
为商品添加数据表格
现在我们开始给页面添加内容,应为是后台管理系统,所以以数据表格的形势在页面显示更加直观,我们可以在我们的模板 - 表格 - 数据表格 中找到总计中意的表格,右键- 审查元素 - 右键 - copy - copy outerhtml,赋值好后粘贴到views/product.ejs (放在| Your Page Content Here |这段注释之后),删除不需要的数据。我们商品管理页面的数据表格就完成了
点击添加产品 进入 添加产品的页面
有了表格之后我们还需要数据才算算得上是管理,所以我们需要一个添加商品的功能
新建页面views/product_add.ejs,和之前一样后页面要请求数据就要设置路由,因为是product的子页面,所以我们只需要配置二级路由就可以啊
routes/product.js
router.get('/', function(req, res, next) {
res.render('product', { index: 2 });
});
// +++++++++++++++
router.get('/add', function(req, res, next) {
res.render('product_add', { index: 2 })
})
点击按钮跳转到添加页面
product.ejs
<a href="/product/add"> <button type="button" class="btn btn-block btn-info btn-lg">添加产品</button> </a>
设计商品属性
要添加数据我们总要知道我们要添加什么样的数据吧,所以现在我们要对产品的属性进行设置
为此我们可以做一个excel表格用来批量导入数据
设计产品表单
<div class="box-header with-border"> <h3 class="box-title">Horizontal Form</h3> </div> <!-- /.box-header --> <!-- form start --> <form class="form-horizontal" action="/product/addAction" method="POST"> <div class="box-body"> <div class="form-group"> <label for="proname" class="col-sm-2 control-label">产品名称</label> <div class="col-sm-10"> <input type="text" class="form-control" name="proname" id="proname" placeholder="产品名称"> </div> </div> <div class="form-group"> <label for="probrand" class="col-sm-2 control-label">产品品牌</label> <div class="col-sm-10"> <input type="text" class="form-control" name="probrand" id="probrand" placeholder="产品品牌"> </div> </div> <div class="form-group"> <label for="brandimg" class="col-sm-2 control-label">logo地址</label> <div class="col-sm-10"> <input type="text" class="form-control" name="brandimg" id="brandimg" placeholder="logo地址"> </div> </div> <div class="form-group"> <label for="porimg" class="col-sm-2 control-label">产品图片</label> <div class="col-sm-10"> <input type="text" class="form-control" name="proimg" id="proimg" placeholder="产品图片"> </div> </div> <div class="form-group"> <label for="price" class="col-sm-2 control-label">价格</label> <div class="col-sm-10"> <input type="text" class="form-control" name="price" id="price" placeholder="价格"> </div> </div> <div class="form-group"> <label for="stock" class="col-sm-2 control-label">库存</label> <div class="col-sm-10"> <input type="text" class="form-control" name='stock' id="stock" placeholder="库存"> </div> </div> <div class="form-group"> <label for="sales" class="col-sm-2 control-label">销量</label> <div class="col-sm-10"> <input type="text" class="form-control" name="sales" id="sales" placeholder="销量"> </div> </div> <div class="form-group"> <label for="detail" class="col-sm-2 control-label">详情</label> <div class="col-sm-10"> <input type="text" class="form-control" name="detail" id="detail" placeholder="详情"> </div> </div> </div> <!-- /.box-body --> <div class="box-footer"> <label class="col-sm-2 control-label"></label> <button type="submit" class="btn btn-info ">提交</button> </div> <!-- /.box-footer --> </form> </div>
获取表单提交的信息
<form class="form-horizontal" action="/product/addAction" method="POST"></form>
设计post提交的表单路由 product.js
router.post('/addAction', (req, res, next) => { // get req.query // post req.body res.send(req.body) })
创建相关数据库
在数据库连接中输入以下指令来创建商品的数据库集合
use sh2001 switched to db sh2001 db.createCollection('products') { "ok" : 1 } db.createCollection('users') { "ok" : 1 } db.createCollection('carts') { "ok" : 1 } db.createCollection('orders') { "ok" : 1 } db.createCollection('banners') { "ok" : 1 } db.createCollection('messages') { "ok" : 1 } db.createCollection('activitys') { "ok" : 1 }
使用mongoose
安装指令cnpm i mongoose -S (要连接mongodb数据库所需的插件)
数据库连接
创建db.js文件(连接数据库的模板)
const mongoose = require('mongoose'); const DB_URL = "mongodb://127.0.0.1:27017/sh2001" // 后面两个不需要记忆,会自动提示 mongoose.connect(DB_URL, { useNewUrlParser: true, useUnifiedTopology: true }); mongoose.connection.on('connected', () => { console.log('数据库链接成功') }) mongoose.connection.on('disconnected', () => { console.log('数据库链接失败') }) mongoose.connection.on('error', () => { console.log('数据库链接异常') }) module.exports = mongoose;
创建数据库的集合
collection/Product.js
const mongoose = require('./../db'); const Schema = mongoose.Schema; const productSchema = new Schema({ proid: { type: String }, proname: { type: String }, probrand: { type: String }, brandimg: { type: String }, proimg: { type: Array }, price: { type: Number }, detail: { type: String }, stock: { type: Number }, sales: { type: Number } }) // 就会自动创建一个 数据库集合products module.exports = mongoose.model('Product', productSchema);
封装增删改查以及分页功能
// 增删改查的封装 module.exports = { //插入 insert(collectionName, insertData) { return new Promise((resolve, reject) => { collectionName.insertMany(insertData, (err) => { if (err) { reject(err) } else { resolve() } }) }) }, /* 删除 */ delete(collectionName, whereData, deleteNum) { const deleteType = deleteNum === 1 ? 'deleteMany' : 'deleteOne'//判断是一条数据还是多条数据 return new Promise((resolve, reject) => { collectionName[deleteType](whereData, (err) => { if (err) { reject(err) } else { resolve() } }) }) }, /* 修改 */ update(collectionName, whereData, updateData, updateNum) { const updateType = updateNum === 1 ? 'updateMany' : 'updateOne'//判断是一条数据还是多条 return new Promise((resolve, reject) => { collectionName[updateType](whereData, updateData, (err) => { if (err) { reject(err) } else { resolve() } }) }) }, /* 查询 */ find(collectionName, whereData, showData) { return new Promise((resolve, reject) => { collectionName.find(whereData, showData, (err, data) => { if (err) { reject(err) } else { resolve(data) } }) }) }, /* 排序 */ sort(collectionName, whereData, showData, sortData) { return new Promise((resolve, reject) => { collectionName.find(whereData, showData).sort(sortData).exec((err, data) => { if (err) { reject(err) } else { resolve(data) } }) }) }, //封装的分页功能,有bug paging (collectionName, whereData, showData, limitnum, count) { return new Promise((resolve, reject) => { collectionName.find(whereData, showData).limit(limitnum).skip(count * limitnum).exec((err, data) => { if (err) { reject(err) } else { resolve(data) } }) }) }, //页码路由 count (collectionName) { return new Promise((resolve, reject) => { return collectionName.count((err, len) => { if (err) throw err resolve(len) }) }) }, /* 排序 */ paging2 (collectionName, whereData, showData,sortData,limitnum, count) { return new Promise((resolve, reject) => { collectionName.find(whereData, showData).sort(sortData).limit(limitnum).skip(count * limitnum).exec((err, data) => { if (err) { reject(err) } else { resolve(data) } }) }) } }
其他管理功能大致和product差不多,在此不一一赘述
登录功能
首先创建登陆功能
在routes/index.js中配置登录的路由
创建登录页面
新建登录页面views/login.ejs(页面样式可以参照,AdminLTE-2.4.10中登录的界面设计)
设计管理员账号数据库
//管理员数据库 const mongoose = require('./../db') const Schema = mongoose.Schema; const adminSchema = new Schema({ adminid: { type: String }, admin: { type: String }, password: { type: String }, role: { type: Number } // 角色 - 权限 }) // 就会自动创建一个 数据库集合products module.exports = mongoose.model('Admin', adminSchema);
密码存储时可以进行加密存储
......