双十一刚过去的你是否是又要开始准备双十二了呢?🤔谈到购物就离不开购物车,那购物车功能如何实现呢?就小米商城购物车,咱们来谈一谈
前端
效果图以下(基本是咱们见过的购物车的效果): 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表数据数据库
// 配置数据库链接
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
场景:当购物车为空时,页面显示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中实现