1、购物车列表功能实现
点击加入购物车或者点击购物车图标后进入购物车页面,在购物车页面中首先渲染cartList的列表。首先加载init()方法,在init方法中发起get请求,请求cartList数据(不需要传入参数)。在后端先解析出用户cookie中的userId,查找文档,如果找到了就把cartList返回去。
前端实现代码
data(){
return{
cartList:[],
}
},
mounted(){
this.init();
},
methods:{
init(){
axios.get('/users/cartList').then((response)=>{
let res=response.data;
this.cartList=res.result;
})
}
},
components:{
NavHeader,
NavFooter,
NavBread,
Modal
}
}
后端实现代码
//查询当前用户的购物车数据
router.get('/cartList',(req,res,next)=>{
let userId=req.cookies.userId;
User.findOne({userId:userId},(err,doc)=>{
if(err){
res.json({
status:'1',
msg:err.message,
result:''
})
}else{
res.json({
status:'0',
msg:'',
result:doc.cartList
})
}
})
})
2、商品删除功能
点击购物车页面的删除按钮,弹出询问是否删除的模态框,并用变量productId记录商品id,点击模态框上的确认删除后,向后端发起post请求,传入参数商品id,后端返回响应后关闭模态框并重新调用init方法重新渲染购物车列表
前端代码:
data(){
return{
cartList:[],
mdDel:false,
productId:''
}
},
mounted(){
this.init();
},
methods:{
init(){
axios.get('/users/cartList').then((response)=>{
let res=response.data;
this.cartList=res.result;
})
},
delPrd(id){
this.mdDel=true;
this.productId=id;
},
prdDel(){
axios.post('/users/delprd',{
productId:this.productId
}).then((response)=>{
let res=response.data;
if(res.status=='0'){
this.mdDel=false;
this.init();
}
})
}
},
components:{
NavHeader,
NavFooter,
NavBread,
Modal
}
}
后端代码:
router.post('/delprd',(req,res,next)=>{
let userId=req.cookies.userId;
let pdId=req.body.productId;
User.update({userId:userId},{$pull:{'cartList':{'productId':pdId}}},(err,doc)=>{
if(err){
res.json({
status:'1',
msg:err.message,
result:''
})
}else{
res.json({
status:'0',
msg:'删除成功',
result:'suc'
})
}
})
})
3、商品修改功能
点击+-需要和前边的是否选中需要修改商品数据。
首先加减是在+-的按钮上调用editCart方法,并传入商品item和+-的flag(add和minus)如果是+,就给item的productNum++,反之--。如果flag不是+-,就是check了,那此时将item的选中状态变为相反数。全部改变完之后将数据传到后端进行同步处理。通过axios的post请求,传入参数商品id,商品数量,和商品的选中状态。请求之后如果成功了就好,没成功的话要把数量变回原值。并弹出提示信息。后端更新子文档有不同的方法,可以先查找到子文档在更新,也可以直接更新子文档。后者比较简单。
前端更新后的代码:
data(){
return{
cartList:[],
mdDel:false,
productId:''
}
},
mounted(){
this.init();
},
methods:{
init(){
axios.get('/users/cartList').then((response)=>{
let res=response.data;
this.cartList=res.result;
})
},
delPrd(id){
this.mdDel=true;
this.productId=id;
},
prdDel(){
axios.post('/users/delprd',{
productId:this.productId
}).then((response)=>{
let res=response.data;
if(res.status=='0'){
this.mdDel=false;
this.init();
}
})
},
editCart(item,flag){
let oldNum=item.productNum;
if(flag=='add'){
item.productNum++;
}else if(flag=='minus'){
if(item.productNum<=1){
return;
}else{
item.productNum--;
}
}else{
item.checked*=-1;
}
axios.post('/users/changeNum',{
pdId:item.productId,
num:item.productNum,
checked:item.checked
}).then((response)=>{
let res=response.data;
if(res.status!='0'){
item.productNum=oldNum;
alert('更改商品数量失败')
}
})
},
},
components:{
NavHeader,
NavFooter,
NavBread,
Modal
}
}
后端代码:
router.post('/changeNum',(req,res,next)=>{
let userId=req.cookies.userId;
let pdId=req.body.pdId;
let num=req.body.num;
let checked=req.body.checked;
//先查找再更新
// User.findOne({userId:userId},(err,doc)=>{
// if(err){
// res.json({
// status:'1',
// msg:err.message,
// result:''
// })
// }else{
// doc.cartList.forEach((item)=>{
// if(item.productId==pdId){
// item.productNum=num;
// }
// });
// User.update({userId:userId},{
// $set: {cartList:doc.cartList}
// },(err)=>{
// if(err){
// res.json({
// status:'1',
// msg:err.message,
// result:''
// })
// }else{
// res.json({
// status:'0',
// msg:'',
// result:doc.cartList
// })
// }
// })
// }
// })
//直接更新子文档
User.update({userId:userId,"cartList.productId":pdId},{
$set: {"cartList.$.productNum":num},
$set: {"cartList.$.checked":checked}
},(err)=>{//更新子文档
if(err){
res.json({
status:'1',
msg:err.message,
result:''
})
}else{
res.json({
status:'0',
msg:'更新成功',
result:''
})
}
})
})
4、购物车全选
点击全选按钮时,将是否选中的状态通过flag传给后端,如果后端更新成功,前端就将遍历商品将checked设置为相应状态。在后端,首先接收是否需要全选isAll,然后通过userId找到用户的cartLIst,遍历列表,将整个cartList的checked设置为相应的状态。改完之后保存数据
data(){
return{
cartList:[],
mdDel:false,
productId:''
}
},
mounted(){
this.init();
},
methods:{
init(){
axios.get('/users/cartList').then((response)=>{
let res=response.data;
this.cartList=res.result;
})
},
delPrd(id){
this.mdDel=true;
this.productId=id;
},
prdDel(){
axios.post('/users/delprd',{
productId:this.productId
}).then((response)=>{
let res=response.data;
if(res.status=='0'){
this.mdDel=false;
this.init();
}
})
},
editCart(item,flag){
let oldNum=item.productNum;
if(flag=='add'){
item.productNum++;
}else if(flag=='minus'){
if(item.productNum<=1){
return;
}else{
item.productNum--;
}
}else{
item.checked*=-1;
}
axios.post('/users/changeNum',{
pdId:item.productId,
num:item.productNum,
checked:item.checked
}).then((response)=>{
let res=response.data;
if(res.status!='0'){
item.productNum=oldNum;
alert('更改商品数量失败')
}
})
},
selectAll(){
// this.isAll=!this.isAll;//在computed实时计算了不能在这再赋值了,
var flag=!this.isAll
axios.post('/users/selectAll',{
isAll:flag
}).then((response)=>{
if(response.data.status=='0'){
this.cartList.forEach((item)=>{
if(flag){
item.checked=1;
}else{
item.checked=-1;
}
})
}
})
},
},
components:{
NavHeader,
NavFooter,
NavBread,
Modal
}
}
后端代码:
router.post('/selectAll',(req,res,next)=>{
let userId=req.cookies.userId;
let isAll=req.body.isAll;
User.findOne({userId:userId},(err,doc)=>{
if(err){
res.json({
status:'1',
msg:err.message,
result:''
})
}else{
doc.cartList.forEach((item)=>{
if(isAll){
item.checked=1;
}else{
item.checked=-1;
}
})
doc.save((err1,doc1)=>{
if(err1){
res.json({
status:'1',
msg:err.message,
result:''
})
}else{
res.json({
status:'0',
msg:'',
result:'suc'
})
}
})
}
})
})
5、商品实时计算功能
computed中添加的实际上是data,返回一个实时变化的值,这对计算商品总价钱很方便,首先将isAll变量用动态的商品属性checked的个数来计算,如果checked的商品等于cartList里的商品长度,isAll就是全选的状态,如果不是就不是全选的状态。其次,总价钱也实时计算,遍历整个列表,计算商品的个数*价钱。
computed:{//实时计算,相当于watch方法
isAll(){
let count=0;
this.cartList.forEach((item)=>{
if(item.checked==1){
count++;
}
})
return count===this.cartList.length;
},
totalMoney(){
let totalMoney=0;
this.cartList.forEach((item)=>{
if(item.checked==1){
totalMoney+=parseFloat(item.salePrice)*parseInt(item.productNum);
}
})
return totalMoney;
}
},
6、filter格式化
在组件内部定义filters,写过滤方法,在页面中需要格式化的地方调用
{{ totalMoney | currency('$')}}
import {currency} from './../util/currency'
filters:{
currency:currency
},
在main.js中定义全局过滤器。
import {currency} from './util/currency'
Vue.filter("currency",currency);
currency代码:
const digitsRE = /(\d{3})(?=\d)/g
export function currency (value, currency, decimals) {
value = parseFloat(value)
if (!isFinite(value) || (!value && value !== 0)) return ''
currency = currency != null ? currency : '$'
decimals = decimals != null ? decimals : 2
var stringified = Math.abs(value).toFixed(decimals)
var _int = decimals
? stringified.slice(0, -1 - decimals)
: stringified
var i = _int.length % 3
var head = i > 0
? (_int.slice(0, i) + (_int.length > 3 ? ',' : ''))
: ''
var _float = decimals
? stringified.slice(-1 - decimals)
: ''
var sign = value < 0 ? '-' : ''
return sign + currency + head +
_int.slice(i).replace(digitsRE, '$1,') +
_float
}