vuex + koa + mysql实现购物车功能(一)

双十一刚过去的你是否是又要开始准备双十二了呢?🤔谈到购物就离不开购物车,那购物车功能如何实现呢?就小米商城购物车,咱们来谈一谈
前端

  效果图以下(基本是咱们见过的购物车的效果): vue

动手前思考

  1.数据库须要建两个表: 全部商品表(cart), 购物车商品表(cartselected)node

  2.cart表: id(主键,自增),img,title,price,recommendmysql

  cartselected表:id(与cart表id关联),img,title,price,recommend,num,checked(是否选中状态,只有0和1两个值)ios

  3.axios请求在actions中进行,请求回来的数据放入state中,相关计算在getters中进行sql

  4.axios须要屡次用到,能够进行封装vuex

  5.商品栏展现cart表数据,购物车栏展现cartselected表数据数据库

准备工具

  • vscode
  • navicat
  • mysql
数据库表结构以下:

  接下来让咱们一个一个功能分别实现吧!😀

实现功能

  1.搭建后台服务、链接mysql

// 配置数据库链接
const config = {
  database: {
    DATEBASE: 'xiaomi',
    USERNAME: 'root',
    PASSWORD: '******',
    PORT: '3306',
    HOST: 'localhost'
  }
}
// 建立链接池
var pool = mysql.createPool({
  host: config.database.HOST,
  user: config.database.USERNAME,
  password: config.database.PASSWORD,
  database: config.database.DATEBASE,
  port: config.database.PORT
})
// 统一链接数据库的方法
let allServies = {
  query: function (sql, values) {
    return new Promise((resolve, reject) => {
      pool.getConnection(function (err, connection) {
        if (err) {
          reject(err)
        } else {
          connection.query(sql, values, (err, rows) => {
            if (err) {
              reject(err)
            } else {
              resolve(rows)
            }
            connection.release() // 释放链接池
          })
        }
      })
    })
  }
}
复制代码

  链接成功后能够用postman,或者直接浏览器测试一下(这里就不测试啦)axios

  2.细讲每个功能实现(按照sql语句 -> 后端接口 -> vuex -> 具体component方式)

  • 随机查询
  •   场景:当购物车为空时,页面显示20件商品,当购物车有商品时,则显示10件商品后端

    let findallgoods = function (num) { // 随机查询num数量的商品信息
      let _sql = `select * from cart order by rand() limit ${num} ;`
      return allServies.query(_sql)
    }
    复制代码

    后端路由:

    router.post('/allcart', async (ctx, next) => {
      let _num = ctx.request.body.num
      await userService.findallgoods(_num).then((res) => {
        ctx.body = {
          data: res
        }
      })
    })
    复制代码

    前端访问

    data () {
        return {
          allcart: [] // 返问后拿到的数据
        }
      },
    methods: {
        requestGoods (nums) { // 请求接口返回数据
          axios({
          url: 'http://localhost:3000/users/allcart',
          method: 'post',
          data: {
            num: nums
          }
        }).then(res => {
          this.allcart = res.data.data
        }).catch(err => {
          console.log(err)
        })
      }
    },
    watch: { // 监听cart长度从而判断请求几条数据(10条仍是20条)
        cart (newval, old) {
          if (newval.length == 0 && old.length != 0) {
            this.requestGoods(20)
          } else {
            this.requestGoods(10)
          }
        }
      },
    复制代码

  • 加入购物车及移出购物车
  • // 从购物车中查找某一件商品
    let findcart = function (id) {
      let _sql = `select * from cartselected where id="${id}";`
      return allServies.query(_sql)
    }
    // 查找购物车全部商品
    let findallcart = function () {
      let _sql = `select * from cartselected;`
      return allServies.query(_sql)
    }
    // 插入购物车(id, title, price, recommend, img, num, checked)
    let insertgoods = function (value) {
      let _sql = `insert into cartselected set id=?,title=?,price=?,recommend=?,img=?,num=?,checked=?`
      return allServies.query(_sql, value)
    }
    // 根据id删除购物车某一件商品
    let deletegoods = function (value) {
      let _sql = `delete from cartselected where id=?`
      return allServies.query(_sql, value)
    }
    复制代码
    // 购物车全部商品
    router.post('/allcarts', async (ctx, next) => {
      await userService.findallcart().then((res) => {
        ctx.body = {
          data: res
        }
      })
    })
    // 加入购物车
    router.post('/insertcart', async (ctx, next) => {
      let _id = ctx.request.body.id
      let _title = ctx.request.body.title
      let _price = ctx.request.body.price
      let _recommend = ctx.request.body.recommend
      let _img = ctx.request.body.img
      let _num = ctx.request.body.num
      let _checked = ctx.request.body.checked
      if (!_id) {
        return
      } else {
        let cart = {
          id: _id,
          title: _title,
          price: _price,
          recommend: _recommend,
          img: _img,
          num: _num,
          checked: _checked
        }
        await userService.findcart(cart.id).then(async (res) => {
          if (res.length) {
            try {
              throw Error('购物车中已存在')
            } catch (error) {
              console.log(error)
            }
            ctx.body = {
              code: '800003',
              data: 'err',
              mess: '购物车已存在该商品'
            }
          } else {
            await userService.insertgoods([cart.id, cart.title, cart.price, cart.recommend, cart.img, cart.num, cart.checked]).then(async (res) => {
              let r = ''
              if (res.affectedRows !== 0) {
                await userService.findcartgoods(cart.id).then((res1) => {
                  ctx.body = {
                    code: '800000',
                    data: res1,
                    mess: '增长购物车成功'
                  }
                })
              } else {
                r = 'error'
                ctx.body = {
                  code: '800004',
                  data: r,
                  mess: '增长购物车失败'
                }
              }
            })
          }
        })
      }
    })
    // 删除购物车某一件商品
    router.post('/deletegood', async (ctx, next) => {
      let _id = ctx.request.body.id
      await userService.deletegoods(_id).then(res => {
        ctx.body = {
          code: '800000',
          data: res,
          mess: '删除成功'
        }
      }).catch(err => {
        ctx.body = {
          code: '800002',
          data: err
        }
      })
    })
    复制代码

    vuex 的actions:

    getcart ({commit}, status) { // 获取购物车表的数据
        axios({
          url: 'http://localhost:3000/users/allcarts',
          method: 'post',
          data: {}
        }).then(res => {
          commit('setcart', res.data.data) // commit方法给mutations,给state的cart赋值
        }).catch(err => {
          console.log(err)
        })
      },
      addcart ({dispatch}, {id, title, price, recommend, img, num, checked}) { // 加入购物车
        axios({
          url: 'http://localhost:3000/users/insertcart',
          method: 'post',
          data: {
            id: id,
            title: title,
            price: price,
            recommend: recommend,
            img: img,
            num: num,
            checked: checked
          }
        }).then(res => {
          if (res.data.code === '800000') {
            dispatch('getcart'); // 再次请求购物车数据
          } else {
            console.log('增长购物车失败')
          }
        }).catch(err => {
          console.log(err)
        })
      },
      deletecart ({dispatch}, id) { // 删除购物车某件商品
        axios({
          url: 'http://localhost:3000/users/deletegood',
          method: 'post',
          data: {
            id: id
          }
        }).then (res => {
          dispatch('getcart')
        })
      }
    复制代码

    component:

    addcart (id, title, price, recommend, img, num, checked) { // 点击加入购物车
          this.$store.dispatch('addcart', {id, title, price, recommend, img, num, checked})
        },
    deletecart (id) { // 删除购物车的商品
          this.$store.dispatch('deletecart', id)
        }
    复制代码

  • 增长减小数量
  • // 购物车数量增长
    let _sql = `update cartselected set num=num+1 where id="${id}"` // 购物车数量减小 let _sql = `update cartselected set num=num-1 where id="${id}" and num >= 2` // num > 2是由于数量不能减小到0 复制代码

    后端接口

    // 增长购物车某个商品数量
    router.post('/addcartnum', async (ctx, next) => {})
    // 减小购物车某个商品数量
    router.post('/reducecartnum', async (ctx, next) => {})
    复制代码

    vuex:

    addcartnum ({dispatch}, params) {}) // 增长数量
    reducecartnum ({dispatch}, params) {}) // 减小数量
    复制代码

    component:

    add (id) { // 增长商品数量
      this.$store.dispatch('addcartnum', id)
    },
    reduce (id) { // 减小商品数量
          this.$store.dispatch('reducecartnum', id)
        }
    复制代码

  • 全选与单选
  • // 设置全不选
    let allfalse = function () {
      let _sql = `update cartselected set checked=0`
      return allServies.query(_sql)
    }
    // 设置全选
    let alltrue = function () {
      let _sql = `update cartselected set checked=1`
      return allServies.query(_sql)
    }
    // 根据id切换该商品选中仍是不选中
    let singleselect = function (id) {
      let _sql = `update cartselected set checked=(case when checked=0 then 1 else checked=0 end) where id="${id}"`
      return allServies.query(_sql)
    }
    复制代码

    后端接口:

    // 设置全选
    router.post('/alltrue', async (ctx, next) => {})
    // 设置全不选
    router.post('/allfalse', async (ctx, next) => {})
    // 根据id切换该商品选中仍是不选中
    router.post('/singleselect', async (ctx, next) => {})
    复制代码

    vuex:

    allfalse ({dispatch}, status) {})
    alltrue ({dispatch}, status) {})
    singleselect ({dispatch}, status) {})
    复制代码

    component

    data () {
        return {
            allcheked: false //是否全选
        }
    },
    methods: {
        allselect () { // 全选
         if (this.allcheked) {
           this.$store.dispatch('allfalse')
         } else {
           this.$store.dispatch('alltrue')
         }
        },
        singleselected (id) { // 单选
          this.$store.dispatch('singleselect', id)
        }
    },
    computed: {
        ...mapGetters([
          'cart'
        ])
    },
    watch: {
        cart: {
          handler(newValue, oldValue) {
            for (let i = 0; i < newValue.length; i++) {
              console.log(newValue[i].checked)
              if (newValue[i].checked == 0) {
                this.allcheked = false
                return 
              }
              this.allcheked = true
            }
          },
          deep: true
        }
      }
    复制代码

  • 相关计算
  • getters

    const getters = {
      cart: state => state.cart,
      littletotalPrice (state) { // 小计
        let money = []
        if (state.cart.length != 0) {
          state.cart.forEach((item) => {
            console.log(item)
             let price = item.price.substring(0, item.price.indexOf('元'))
             money.push(price * item.num)
          })
          return money
        } else {
          return []
        }
      },
      totalPrice (state) { // 总计
        let selected = state.cart.filter(function(elem) {
          return elem.checked == 1
        })
        let totalprice = 0
        for (let i = 0; i < selected.length; i++) {
          let price1 = selected[i].price.substring(0, selected[i].price.indexOf('元'))
          let price2 = price1 * selected[i].num
          totalprice += price2
        }
        return totalprice
      },
      selectednum (state) { // 选中的数量
        let selected = state.cart.filter(function(elem) {
          return elem.checked == 1
        })
        return selected.length
      },
      totalnum (state) { // 商品数
        let sum = 0
        for (let i = 0; i < state.cart.length; i++) {
          sum = sum + state.cart[i].num
        }
        return sum
      }
    }
    复制代码

    这样购物车功能及基本实现了(内心难免哀叹一声:好像都是在搬砖😪,有没有更简单的方法呢?)

    总结

    每个方法都要请求一次甚至更屡次后端接口,对后端形成巨大压力,因为对数据的操做大部分靠sql语句驱动,对于sql语句不熟练的同窗就不太友好了,所以须要更好的方法来解决。

    后续

    下次咱们将改善咱们购物车功能的实现,接口值访问一次,功能全在vuex中实现

    相关文章
    相关标签/搜索