电子商城项目总结

技术栈
1.vue
2.express
3.mongodb


项目完成步骤

1.vuecli初始化前端项目模板

指令:vue init [template-name] [project-name]

eg:vue init webpack vuecliTest

2.express初始化后端项目模板

  • npm init 创建一个npm的package.json配置文件
  • npm i express –save 安装express并将其保存到依赖列表中
  • npm i express-generator -g 使用全局方式安装 Express 应用生成器
  • express [appName] 在安装了 express 的文件夹中快速生成 Express 应用

3.托管代码

  • 在github上新建一个仓库(不要初始化)
  • 创建本地仓库,先git init,然后git add . ,最后git commit -m “message”
  • 远程连接,先git remote add origin git@server-name:path/repo-name.git
    (eg:git remote add origin git@github.com:Winnie-bear/Canvas-learn.git)
    然后 git push -u origin master
    最后 git push origin master 推送最新修改
    

4.数据库

  • 在后端项目文件夹中新建一个models文件夹,用来装所需的Schema文件

  • 在routes文件下新建一级路由的文件,文件当中可以编写二级路由的代码,同时还需要修改app.js中的代码
    例如:在routes文件夹下新建goods.js文件,则/goods是一级路由

    1
    2
    var goodsRouter=require('./routes/goods');
    app.use('/goods',goodsRouter);
  • 连接数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const url = "mongodb://mallAdmin:winniebear98813@127.0.0.1:27017/mall?authSource=mall"
    //连接数据库
    mongoose.connect(url,{ useNewUrlParser: true });

    //连接不上的时候
    mongoose.connection.on('disconnected',()=>{
    console.log('MongoDB connected fail');
    })

    //连接错误的时候
    mongoose.connection.on('error',err=>{
    console.log(err)
    })

    //连接打开的时候
    mongoose.connection.once('open',()=>{
    console.log('MongoDB Connected successfully!')
    })

    数据库连接成功后方可操作数据库,注意要引入models文件夹下所需的Schema文件


前端代码简析

1.通用组件部分(components文件下)

  • 模态框

    语法点:slot、props、$emit

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
      //核心代码,通过slot进行插值
    <div class="md-modal modal-msg md-modal-transition" v-bind:class="{'md-show':mdShow}">
    <div class="md-modal-inner">
    <div class="md-top">
    <button class="md-close" @click="closeModal">Close</button>
    </div>
    <div class="md-content">
    <div class="confirm-tips">
    <!-- 提示信息插槽 -->
    <slot name="message"></slot>
    </div>
    <div class="btn-wrap">
    <!-- 按钮插槽 -->
    <slot name="btnGroup"></slot>
    </div>
    </div>
    </div>
    </div>
    //父子组件之间的传值和触发父组件上的事件
    <script>
    export default{
    //接收父组件的传值
    props:["mdShow"],
    data(){
    return{
    msg:'hello vue'
    }
    },
    methods:{
    closeModal(){
    //触发父组件的close事件
    this.$emit("close");
    }
    }
    }
    </script>
  • 面包屑导航

    知识点:slot

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
      <template>
    <section class="bread">
    <div class="bread-wrap">
    <nav class="">
    <a href="/">
    Home
    </a>
    <!-- 插槽,每个页面显示的当前页不一样 -->
    <slot></slot>
    </nav>
    </div>
    </section>
    </template>
  • 页首

    知识点:vuex
    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import Vuex from 'vuex'
    Vue.use(Vuex);
    const store =new Vuex.Store({
    state:{
    nickName:'',
    cartCount:0
    },
    //更改vuex的store中的状态,s不能掉
    mutations:{
    updateUserInfo(state,nickName){
    state.nickName=nickName;
    },
    updateCartCount(state,cartCount){
    state.cartCount+=cartCount;
    },
    initCartCount(state,cartCount){
    state.cartCount=cartCount;
    }
    }
    });

    NavHeader.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import {mapState} from 'vuex'
    computed: {
    //映射this.nickName为this.$store.state.nickName,...扩展运算符,将一个数组转为用逗号分隔的参数序列
    ...mapState(['nickName','cartCount']);
    },
    getCartCount(){
    axios.get('/users/getCartCount').then((response)=>{
    let res=response.data;
    if(res.status=='0'){
    //触发mutations中注册的方法
    this.$store.commit('initCartCount',parseInt(res.result));
    }
    })
    }
  • 页尾

    (分别对应Modal.vue、 NavBread.vue、 NavHeader.vue、NavFooter.vue)

2.显示页面部分(在views文件夹下)

  • 产品列表页面
  • 购物车页面

    知识点:computed 计算属性
    关键点:

    • computed 会搜集并记录依赖
    • 依赖发生了变化才会重新计算 computed ,由于 computed 是有缓存的,所以当依赖变化之后,第一次访问 computed 属性的时候,才会计算新的值
    • 记录的响应式属性都在当前实例范畴内
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      computed:{
      checkAllFlag:function(){
      return this.checkedCount==this.cartList.length;
      },
      checkedCount:function(){
      var i=0;
      this.cartList.forEach((item)=>{
      if(item.checked=='1') i++;
      });
      return i;
      },
      totalPrice:function(){
      var sum=0;
      this.cartList.forEach((item)=>{
      //被选中的商品才算入总价
      if(item.checked=='1'){
      sum+=parseFloat(item.salePrice)*parseInt(item.productNum);
      }
      });
      return sum;
      }
      },
  • 选择地址页面

  • 订单确认页面

    知识点:编程式路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    payMent(){
    //$route为当前的router对象,可以获取name、path、query、params等
    var addressId=this.$route.query.addressId;
    //通过接口将支付以后产生的订单保存到数据库
    axios.post('/users/payMent',{
    addressId:addressId,
    orderTotal:this.orderTotal
    }).then((response)=>{
    //跳转到下一个页面需要携带数据
    let res=response.data;
    if(res.status=='0')
    {
    //$router为VueRouter实例,全局路由对象,任何页面都可以调用 push(), go()等方法
    this.$router.push({
    path:'/orderSuccess',
    query:{
    orderId:res.result.orderId,
    }
    });
    }
    })
    }
  • 订单成功页面

    知识点:动态路由

    1
    2
    3
    4
    5
    6
    <div class="btn-l-wrap">
    <router-link class="btn btn--m" to="/cart">Cart List</router-link>
    </div>
    <div class="btn-r-wrap">
    <router-link class="btn btn--m" to="/">Goods List</router-link>
    </div>

    (分别对应GoodsList.vue、 Cart.vue、 Address.vue、OrderConfirm.vue、OrderSuccess.vue)


后端代码简析

1./goods显示商品接口

  • 查询商品列表数据

    router.get(‘/list’,function(req,res,next){ … });

  • 添加到购物车

    router.post(‘/addCart’,function(req,res,next){ … });

2./users用户登录及后续操作接口

  • 登陆接口
  • 登出接口
  • 登录校验接口
  • 获取购物车接口
  • 购物车删除接口
  • 修改购物车接口
  • 全选接口
  • 获取地址列表接口
  • 设置默认地址接口
  • 删除地址接口
  • 支付产生订单接口
  • 根据订单Id查询订单信息接口
  • 获取购物车的商品数量接口
    3.登录拦截
    /server/app.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //登录拦截
    app.use(function(req,res,next){
    //用户登录以后,每次请求都会带上cookie
    if(req.cookies.userId){
    next();
    }else{
    //设置未登录状态时的白名单,req.originalUrl包含参数,req.path不包含参数
    if(req.originalUrl=='/users/login'||req.originalUrl=='/users/logout'||req.path=='/goods/list')
    {
    next();
    }else{
    res.json({
    status:'10001',
    msg:'当前未登录',
    result:''
    });
    }
    }
    })

数据库代码简析

定义所需的schema文件即可,每一个Schema都会映射到MongoDB的一个collection上。Schema定义了collection里documents的模板(或者说是框架)。最后要把Schema编译成model,才可以操作数据库。

1.productSchema

2.userSchema


小心脚下有坑!

1.进入mongo终端之前要mongod配置服务器

2.mongoimport之前要退出mongo的环境

3.mongodb设置了用户认证以后,mongoexport和mongoimport时要进行认证,需要输入三条认证信息
mongoimport -u winnie -p=winniebear98813 –authenticationDatabase=admin -d mall -c users –file /usr/local/mallData/dumall-users

4.express中app.js定义一级路由,路由文件里面定义二级路由

5.开启mongodb本地服务的时候要用系统管理员的身份运行cmd

6.本地运行项目最好把数据库建在本地,远程访问数据库需要修改远程数据库的配置文件

7.角色授权分两种,一种是直接在当前库中创建用户并授予相关权限。如admin库中创建admin用户。另一种情况是
将在admin中创建的用户授予操作其他库的权限,相关授权命令如下:

#授予角色:db.grantRolesToUser( “userName” , [ { role: “”, db: “” } ])

8.用接口返回数据库查找结果,输入url时,查询参数一定要带上,否则会报toString undefined

9.修改服务器代码时,一定要重新启动。前端访问后端接口时,后端接口一定要开启

10.保存用户信息,一是通过接口校验,二是通过vuex

11.nginx测试的时候,一定要开启服务器的端口才能访问!!!!!

12.部署后端代码的时候要有package.json 并且可以通过vi bin/www检查是否修改成功,然后cnpm i 安装依赖,xshell必须在后端代码的目录下才可以安装package.json中的依赖
lsof -i :加数字 查询端口占用情况

13.修改mongodb的配置文件后要 mongod –config重新设置一遍

14.通过域名文件夹形式访问前端代码,必须在dist/static/index.html修改assetsPublicPath的路径

15.使用mongoose远程连接,需要修改mongodb.conf中的bind_ip

16.windows系统,先mongo连接数据库,然后use admin进入admin数据库,db.auth认证数据库

17.collection无法导入文件的时候,可能是没有readWrite权限


看这里!

代码详情请见:
电子商城


写在最后的话

第一次写项目总结,多有不足,请多见谅!