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中實現

    相關文章
    相關標籤/搜索