mysql增删改查购物车_Vue 原生实现商城购物车增删改查

业务分析:

1 .整个页面分为两种状态:

正常状态:选中商品显示总价,可结算

编辑状态:选中商品增删改查,可删除

页面状态直接影响底部栏的变化

2. 商品,店铺,全选两种状态都可三级联动

3. 编辑状态只有一个店铺能编辑,其他不可编辑

4. 编辑状态对数量操作:增加,减少,输入操作三种改变数量的方式

5. 整体而言,删除商品的情况:正常状态滑动商品单个删除,编辑状态下单个或多个删除及整个店铺删除三种

6.点击删除按钮显示确认对话框,根据用户点击结果发送请求或者直接关闭对话框

7.对话框关闭后从编辑状态切换到正常状态

页面两种状态图示:

准备工作:

1.为所有数据请求接口封装一个入口fetch

export default function fetch({key,data=null,type="post"}){

return new Promise((resolve,reject)=>{

axios[type](URL[key],data).then(res=>{ // URL[key]是封装的完整的地址。 if(res.status === 200){}

if(res.status === 202){}

...

resolve(res)

}).catch(err=>{

reject(err)

})

})

}

2.对同一个场景下所有的操作进行一个封装:

购物车有多种状态,封装cartService对购物车业务状态统一处理

class Cart{

getAllList(){

return _fetch({key:"cartList"}).then(res=>{

return res.data.cartList;

})

}

update(id,number){ // 只要是改变数量的全部放在update return _fetch({key:"cartUpdate",data:{id,number}});

}

remove(id){

return _fetch({key:"cartRemove",data:{id}});

}

removeMore(){

return _fetch({key:"cartRemoveMore",data:{ids}})

}

}

let _cart = new Cart;

export default _cart;

步骤:获取数据进行加工:

获取数据Mock模板:

{

"status": 200,

"message": "",

"cartList|3": [

{

"shopTitle": [ 店铺名

"寻找田野",

"猫咪森林",

"老爹果园"

],

"shopId|+1": 4, 店铺id

"goodsList|1-3": [ 商品列表

{

"id|+1": 6, 商品id

"img": "@image(90x90,@color)", 商品图

"number": 1, 商品数量

"price|50-90.1-2": 1, 商品价格

"sku": [ 商品规格

"全网通,玫瑰金,3+32G",

"全网通,香槟金,3+32G"

],

"title": "VIVO-Y66 全网通/移动版 3+32GB" 商品描述

}

]

}

]

}

从后台获取的数据,先对数据进行加工处理,然后再赋值给data里相应的属性

_cart.getAllList().then(lists=>{

lists.forEach(shop=>{

// checked 响应 页面正常时商品和店铺的icon状态 shop.checked = false ;

// removeChecked 响应 页面编辑时商品和店铺的icon状态 shop.removeCheckd = false;

shop.goodsLIist.forEach(good=>{

good.checked = false;

good.removeChecked = false;

})

})

})

2.页面两种状态通过正在编辑的editingShop的值来判断,editingShop通过点击编辑店铺按钮控制

html:

编辑与完成按钮:

@click="selectEditingShop(shop)"

-----v-show 控制一次只能编辑一个店铺------

v-show="editingShop ? editingShop.shopId === shop.shopId : true"

>

{{editingShop ? "完成":"编辑"}}

我的商品的状态应有所响应:

v-for="(good,goodIndex) in shop.goodsList"

-----editing 让商品样式处理可编辑 -----

:class="{editing : editingShop.editingShopIndex ==== shopIndex : false}"

---------------------------------------

@touchstart($event,good)

@touchend($event,shop,shopIndex,good,goodIndex)

:ref="'goods-'+shopIndex+'-'+goodIndex"

>

底部栏跟着切换

-----isEdit 让底部栏隐藏结算,显示删除----

-----isCheck 当价格大于0,让结算变红,文字变红-----

:class="{isEdit: editingShop, isCheck:totalPrice>0}"

>

// jsselectEditingShop(shop){

this.editingShop = this.editingShop ? null : shop;

}

computed:{

editingShopIndex(){

if(this.editingShop){

return this.cartList.findIndex(shop=>{

return this.editingShop.shopId === shop.shopId

})

}

}

}

3.根据页面状态的三级联动

自下而上:商品 > 店铺 > 全选

1.good:

:class="{checked: editingShop ? good.removeChecked : good.checked}"

---------v-show 只能编辑一个店铺----------

v-show="editingShop ? editingShop.shopId === shop.shopId : true"

@click="selectGood(shop,good)"

>

selectGood(shop,good){

let attr = this.editingShop ? "removeChecked" : "checked";

good[attr] = !good[attr];

// 对店铺 shop[attr] = shop.goodList.every(good=>{

return good[attr];

})

}

2.shop:

:class="{checked: editingShop ? shop.removeChecked : shop.checked}"

---------v-show 只能编辑一个店铺----------

v-show="editingShop ? editingShop.shopId === shop.shopId : true"

@click="selectShop(shop)"

>

selectshop(shop){

let attr = this.editingShop ? "removeChecked" : "checked";

shop[attr] = !shop[attr];

//对商品 shop.goodsList.forEach(good=>{

good[attr] = shop[attr]

})

}

//对全选computd:{

// 正常下的 allChecked:{

get(){

if(this.cartList && this.cartList.length){

return this.cartList.every(shop=>{

return shop.checked;

})

}

},

set(val){}

},

// 编辑下的 allRemoveChecked: {

get(){

if(this.cartList && this.cartList.length){

return this.editingShop.removeChecked;

}

},

set(){}

}

}

3.全选:

:class="{checked:editingShop ? allRemoveChecked : allChecked}"

@click="selectAll"

>全选

selectAll(){

let attr = this.editingShop ? "allRemoveChecked" : "allChecked";

this[attr] = !this[attr];

}

//对商品和店铺computed:{

allChecked:{

get(){...},

set(val){

if (this.cartList && this.cartList.length) {

this.cartList.forEach(shop=>{

shop.checked = val;

shop.goodsList.forEach(good => {

good.checked = val;

})

})

}

}

},

allRemoveChecked:{

get(){...},

set(val){

this.editingShop.removeChecked = val;

this.editingShop.goodsList.forEach(good=>{

good.removeChecked = val;

})

}

}

}

4.此时页面选择情况大致完成,那么我就要动态生成 正常状态选择的列表,或编辑状态的列表,还有总价,是否可以支付

computed:{

selectList(){

if (this.cartList && this.cartList.length) {

let arr = [];

this.cartList.forEach(shop => {

shop.goodsList.forEach(good => {

if(good.checked){

arr.push(good);

}

})

})

return arr;

}

},

removeList(){

if (this.editingShop) {

let arr = [];

this.editingShop.goodsList.forEach(good => {

if(good.removeChecked){

arr.push(good);

}

})

return arr;

}

},

totalPrice(){

let price = 0;

if(this.cartList && this.cartList.length){

this.cartList.forEach(shop=>{

shop.goodsList.forEach(good=>{

if(good.checked) price += good.number*good.price

})

})

}

return price;

},

canpay(){

return (this.totalPrice>0 && !this.editingShop) ? true:false;

}

}

5.更新数量:

:class="disabled:good.number === 1"

@click="updateNumber(good,-1)"

>-

@change="updateNumber(good,good.number)">

@click="updateNumber(good,1)"

>+

updateNumber(good,n){

switch (n){

case 1:

_cart.update(good.id,good.number + 1 ).then(()=>{

// 请求成功再渲染页面 good.number++;

})

break;

case -1:

if(good.number <= 1){return}

_cart.update(good.id,good.number - 1).then(()=>{

good.number--;

})

break;

default:

_cart.update(good.id.n);

}

}

6.删除商品:

1.正常状态点击删除

@click="deleteGood(shop,shopIndex,good,goodIndex,good.id)"

>删除

2.编辑下的删除

:class="{'btn-red':removeList&&removeList.length > 0}"

@click="deleteGood"

>删除

3.滑动删除

...

:ref="'goods-'+shopIndex+'-'+goodIndex"

@touchstart=touchStart($event,good)

@touchend=touchEnd($event,shop,shopIndex,good,goodIndex)

>

js:

deleteGood(shop,shopIndex,good,goodIndex,id){

if(arguments.length){

// 表示只删除一个 this.removeData = {shop,shopIndex,good,goodIndex,id}

}

// 显示对话框 this.isShowPopup = true;

}

确认删除:

confirmRemove(){

if(this.removeData.id){

// 单个 let {shop,shopIndex,good,goodIndex,id} = this.removeData;

_cart.remove(id).then(()=>{

// 成功后渲染 shop.goodsList.splice(goodIndex,1);

// 如果该店铺下无商品 if(!shop.goodsList.length){

this.cartList.splice(shopIndex,1)

}

// 回归正常 this.recoverStatus();

})

}else{

let ids = [];

this.removeList.forEach(good=>{

ids.push(good.id);

})

_cart.removeMore(ids).then(()=>{

if(ids.length === this.editinigShop.goodsList.length){

//全删 this.cartList.splice(this.)

}else{

this.cartList.forEach((shop,shopIndex)=>{

if(shopIndex === this.editingShopIndex){

for(let i = 0;i

if(ids.indexof(shop.goodsLists[i].id) > -1){

shop.goodsList.splice(i,1);

i--;

}

}

}

})

}

this.recoverStatus();

})

}

},

recoverStatus(){

this.removeData = null;

this.editingShop = null;

this.isShowPopup = false;

},

touchStart(e,good){

good.startX = e.changedTouches[0].clientX;

},

touchEnd(e,shop,shopIndex,good,goodIndex){

if(this.editingShop) return; //编辑不可滑动 let endX = e.changedTouches[0].clientX;

let left =0;

if(endX - good.startX > 80){

left = 0;

}else if(endX-good.startX < -80){

left = "-60px";

}

Velocity(this.$refs[`goods-${shopIndex}-${goodIndex}`],{left})

}

}

修复bug:

1.编辑时,之前滑动的商品应回归正常状态

recoverSlide(){

this.cartList.forEach((xshop,shopIndex)=>{

xshop.goodsList.forEach((good,goodIndex)=>{

Velocity(this.$refs[`goods-${shopIndex}-${goodIndex}`],{left:0});

})

})

}

2.滑动删除后,紧接着的下一个商品会继承滑动的效果,因为v-for采用‘就地复用策略’,复用原有DOM结构,解决:为商品添加唯一识别 ,绑定key,注意不能绑定index

...

总结:

1.从后台获取的数据,先对数据进行加工处理,然后再赋值给data里相应的属性。因为是引用,让所有操作都变得对应起来。

2.通过computed 里动态的set 来做响应式处理。这使得data里不需要初始化很多数据,也起到watch的作用。

3.找到突破点,才能让逻辑清晰,有条理。editingShop便是关键。

4.不要闲代码多,乱,先实现需求后优化。。

5.写了好久,我可真蠢,我怀疑我是不是少了哪根筋?????想起来大一计算机基础得了61,我就知道生活不会好过的。。

知识点:

1.$refs 获取子元素/组件列表

ref 是非响应式的,不建议在模板中进行数据绑定,即使用唯一标识绑定

2.v-for 模式使用“就地复用”策略,简单理解就是会复用原有的dom结构,尽量减少dom重排来提高性能 ( 解决方案:还原dom样式 )

3.key 为每个节点提供身份标识,数据改变时会重排,最好绑定唯一标识,如果用index标识可能得不到想要的效果 ( 解决方案:绑定唯一识别key )

4.v-model.number

5.Velocity

6.mockjs

可拓展:

1.分理出失效商品

2.点击结算,判断登录状态,未登录显示对话框跳转登录注册页,登录成功返回购物车页面,已登录状态后生成支付页,支付成功生成查看订单页

Appendix:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值