uniapp 学习笔记二十八 购物车新增商品数据和逻辑完善

uniapp 学习笔记二十八 购物车新增商品数据和逻辑完善

cart.vue

<template>
	<view>
		<view class="flex padding" v-for="(item,index) in cartList" :key="item.id+'-'+item.idx">
			<view class="flex align-center">
				<text @click="handleCheck(index)" :class="['iconfont','icon-youxiajiaogouxuan','margin-right',{'yellow':item.isCheck}]"></text>
				<image class="poster margin-right" :src="item.img" mode=""></image>
			</view>
			<view class="flex justify-between info">
				<view class="">
					{{item.name}}
					<view class="margin-tb-xs">	{{item.french}}	</view>
					Y{{item.list[item.idx].price}}
				</view>
				<view class="flex flex-direction align-end">
					<view @click="handleEdit(index)" class="edit margin-bottom-xs">
						<text class="iconfont icon-bianjishuru"></text>
					</view>
                    {{item.list[item.idx].spec}} 
                    X 
                    {{item.num}}
				</view>
			</view>
		</view>
        <!-- 弹窗 -->
        <u-overlay :show="show" @click="show=false">
            <view class="bg-fff margin cover-cont" @click.stop>
                <view class="padding">
                    <view class="flex justify-between info">
                        <image class="poster margin-right" src="" mode=""></image>
                    	<view class="">
                    		{{cartList[cartIdx].name}}
                    		<view class="margin-tb-xs">	{{cartList[cartIdx].french}}	</view>
                    		Y{{checkedCartInfo.price}}
                    	</view>
                    </view>
                    <view class="flex justify-between padding-tb u-border-bottom">
                        <view class="">
                            规格选择
                        </view>
                        <view class="drop">
                            <view @click="dropShow=true">
                                {{checkedCartInfo.spec}} - {{checkedCartInfo.edible}}
                                <text class="iconfont icon-xiangxiazhankai"></text>
                            </view>
                            <view v-if="dropShow" class="drop-list bg-fff">
                                <view 
                                    v-for="(item,index) in cartList[cartIdx].list" 
                                    class="padding-sm"
                                    :key="index" 
                                    @click="handleDropList(index)"
                                >
                                    {{item.spec}} - {{item.edible}}
                                </view>
                            </view>
                        </view>
                    </view>
                    <view class="flex justify-between align-center padding-tb u-border-bottom">
                        <view class="">
                            数量选择
                        </view>
                        <u-number-box button-size="36" @change="handleNum"></u-number-box>
                    </view>
                </view>
                <view class="flex margin-top">
                    <button @click="show=false" type="default" class="cu-btn lg bg-brown">取消</button>
                    <button @click="handleOk" type="default" class="cu-btn lg bg-yellow">确认</button>
                </view>
            </view>
        </u-overlay>
		<view class="fixed bg-fff flex">
			<view class="flex flex-sub padding align-center">
				<text 
                @click="handleAllCheck(allInfo.allCheck)" 
                :class="['iconfont', 'icon-youxiajiaogouxuan', 'margin-right-xs',{'yellow':allInfo.allCheck}]"></text>
				全选
				<view class="margin-left">
					共计:{{allInfo.allPrice}}
				</view>
			</view>
			<view class="bg-yellow padding text-center color-fff">
				立即结算
			</view>
		</view>
	</view>
</template>

<script>
    import {mapState,mapMutations,mapGetters} from 'vuex'
	export default {
		data() {
			return {
				show:false,
				dropShow:false,
                cartIdx:0, // 主商品序号
                num:1,//当前弹窗商品数量
                dropIdx:0 // 子商品序号
			}
		},
        computed: {
            ...mapState({
                cartList:state=>state.cart.cartList 
            }),
            ...mapGetters({
                allInfo:'cart/allInfo'
            }),
            checkedCartInfo(){ // 过滤下拉选中的商品对象
                let {cartIdx,cartList,dropIdx} = this
                return cartList[cartIdx].list[dropIdx]
            }
        },
        methods:{
            ...mapMutations({
                handleCheck:'cart/cartCheckMut',
                handleAllCheck:'cart/cartAllCheckMut'
            }),
            handleEdit(idx){
                // 弹窗控制
                this.cartIdx = idx;
                this.show=true;
                this.dropIdx = this.cartList[idx].idx;
            },
            handleDropList(dropIdx){ //子商品下拉列表
                this.dropShow = false;
                this.dropIdx = dropIdx;
            },
            handleOk(){ //弹窗确定            
                this.show = false;
                let {cartIdx,dropIdx,num} = this;
                console.log(cartIdx,dropIdx,num);
                this.$store.commit('cart/cartListCheckMut',{cartIdx,dropIdx,num});
            },
            handleNum({value}){
                this.num = value;
            }
            
        }
	}
</script>

<style lang="scss">
page{
	padding-bottom: 100upx;
}
.poster{
	width: 180upx;
	height: 180upx;
	background-color: #d8d8d8;
}
.info{
	width: 60%;
	.edit{
		width: 80upx;
		height: 80upx;
		text-align: center;
		line-height: 80upx;
		background-color: #e6e6e6;
		border-radius: 50%;
	}
}
.icon-youxiajiaogouxuan{
	width: 50upx;
	height: 50upx;
	text-align: center;
	line-height: 50upx;
	background-color: #e6e6e6;
	border-radius: 20%;
}
.fixed {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    box-shadow: 0 0 10upx 2upx rgba(0, 0, 0, 0.2);
}

.yellow{
	background-color: yellow;
}
.cu-btn.lg{
    width: 50%;
}
.cover-cont{
    position: absolute;
    top: 50%;
    left: 0;
    width: 690upx;
    transform: translateY(-50%);
    border-radius: 10upx;
}
.drop{
    position: relative;
    .drop-list{
        width:300upx;
        position: absolute;
        top: 60upx;
        right: 0;
        box-shadow: 0 0 10upx 2upx rgba(0, 0, 0, 0.2);
        z-index: 10;
        view:hover{
            background-color: #e6e6e6;
        }
    }
}

</style>

cart,js

import Vue from 'vue'
export default {
    namespaced:true,
    state(){
        return {
            cartList:[{
                id:"10090",
                twoId:10089,
                name:"拿破仑草莓恋爱",
                french:"Napoleon aux fraises",
                price:"208.00",
                isCheck:false,
                img:"/static/logo.png",
                list:[
                    {id:10090,sku:"n0201",ahead:'提前5小时预定',edible:"2-3人食用",spec:"1磅",price:"218.00"},
                    {id:10091,sku:"n0202",ahead:'提前5小时预定',edible:"4-7人食用",spec:"2磅",price:"318.00"},
                    {id:10092,sku:"n0203",ahead:'提前5小时预定',edible:"8-12人食用",spec:"3磅",price:"458.00"},
                    {id:10093,sku:"n0204",ahead:'提前5小时预定',edible:"12-20人食用",spec:"5磅",price:"750.00"}
                ],
                num:1,//商品数量
                idx:0 //标记选中的子商品信息
            },{
                id:"10090",
                twoId:10089,
                name:"拿破仑草莓",
                french:"Napoleon aux fraises",
                price:"218.00",
                tid:11,
                tname:'限定',
                isCheck:false,
                img:"/static/logo.png",
                list:[
                    {id:10090,sku:"n0201",ahead:'提前5小时预定',edible:"2-3人食用",spec:"1磅",price:"218.00"},
                    {id:10091,sku:"n0202",ahead:'提前5小时预定',edible:"4-7人食用",spec:"2磅",price:"318.00"},
                    {id:10092,sku:"n0203",ahead:'提前5小时预定',edible:"8-12人食用",spec:"3磅",price:"458.00"},
                    {id:10093,sku:"n0204",ahead:'提前5小时预定',edible:"12-20人食用",spec:"5磅",price:"750.00"}
                ],
                num:1, //商品数量
                idx:0 //标记选中的子商品信息
            }
            ]
        }
    },
    getters:{
        /**
         * 统计信息
         */
        allInfo(state){ 
            let allCheck = true
            let allPrice = 0
            state.cartList.forEach(item=>{
                if(!item.isCheck){
                    allCheck = false
                }
                if(item.isCheck){
                    allPrice += item.list[item.idx].price * item.num
                }
            })
            return {allCheck,allPrice}
        }
    },
    mutations:{
        cartCheckMut(state,idx){ // 单选
            state.cartList[idx].isCheck = !state.cartList[idx].isCheck
        },
        cartAllCheckMut(state,bool){ // 全选 bool 是原本的全选状态
            state.cartList.forEach(item=>{
                item.isCheck = !bool
            })
        },
        cartListCheckMut(state,{cartIdx,dropIdx,num}){ // 子商品下拉确认处理
            state.cartList[cartIdx].idx = dropIdx
            state.cartList[cartIdx].num = num
        },
        cartAddMut(state,goodObj){ // 新增商品
            let {cartList} = state
            let len = cartList.length
            for(let i=0;i<len;i++){
                // 购物车中已经有相同商品
                let {id,idx} = goodObj
                if(cartList[i].id==id&&cartList[i].idx==idx){
                    state.cartList[i].num++
                    return
                }
            }
            
            //  非响应式数据挂载
            // goodObj.isCheck = false
            // goodObj.num = 1
            // goodObj.idx = 0
            
            // 响应式数据挂载
            Vue.set(goodObj,'isCheck',true)
            Vue.set(goodObj,'num',1)
            // Vue.set(goodObj,'idx',0)
            state.cartList.push(goodObj)
        }
    }
}

detail.vue

<template>
    <view>
        <swiper class="banner" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000">
        	<swiper-item>
        		<view class="swiper-item">
					<image :src="detail.img" mode="widthFix"></image>
				</view>
        	</swiper-item>
        </swiper>
		<view class="flex justify-around">
			<view 
				v-for="(item,index) in detail.list" 
				:key="index" 
				:class="['text-center padding-sm tab',{active:tabIdx}]" 
				@tap="tabIdx = index"
			>
				<view class="">{{item.spec}}</view>
				<view class="">{{item.weight}}</view>
				<view class="">{{item.edible}}</view>
			</view>
		</view>
		
		<view class="padding fs-28 u-border-bottom">
			¥{{detail.list[tabIdx].price}}
		</view>
		
		<view class="padding fs-28 u-border-bottom">
			{{detail.list[tabIdx].ahead}}
			<text class="margin-lr">{{detail.list[tabIdx].size}}</text>
			{{detail.list[tabIdx].edible}}
		</view>
	
		<view class="padding fs-28 u-border-bottom">
			{{detail.list[tabIdx].fittings}}
		</view>
		
		<view class="flex justify-around fixed padding-tb-sm">
			<button @click="handleAdd({...detail,idx:tabIdx})" class="cu-btn bg-brown lg">加入购物车</button>
			<button class="cu-btn bg-yellow lg">立即购买</button>
		</view>
    </view>
</template>

<script>
    import {mapMutations} from 'vuex'
    export default {
        data() {
            return {
                tabIdx:0,
				detail:null
            }
        },
        methods: {
            ...mapMutations({
                'handleAdd':'cart/cartAddMut'
            })
        },
        onLoad() {
            uni.getStorage({
            	key:'detail',
				success:(res)=>{
					// console.log(res.data);
					this.detail = res.data
				}
            })
        },
        onShow() {
            console.log('onShow 生命周期 执行');
        },
        onHide() {
            console.log('onHide 生命周期 执行');
        },
        onLaunch() {
            console.log('onLaunch 生命周期 执行');
        }
    }
</script>

<style>
.banner{
	height: 600upx;
	.swiper-item{
		height: 600upx;
	}
}
.fixed {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    box-shadow: 0 0 10upx 2upx rgba(0, 0, 0, 0.2);
}
</style>

cake.vue

<template>
    <view>
        <nav-custom></nav-custom>
        <view class="cont">
            <good-item v-for="(item,index) in glist" :gdata="item"></good-item>
        </view>
        <view class="fixed flex justify-center bg-fff padding-sm">
            <view v-for="(item,index) in tabArr" :key="index" @tap="handleTab(item)"
                class="flex justify-around align-center">
                <view class="">{{item.name}}</view>
                <u-line v-if="index<tabArr.length-1" direction="col" length="15" margin="30upx"></u-line>
            </view>
        </view>
        <u-popup :show="show" mode="left" @close="handleClose">
            <view class="pop-cont">
                <view v-for="(item,index) in cfylist" class="padding-sm u-border-bottom">
                    {{item.bname}}
                    <view v-if="index==0">
                        <view 
                            @tap="listShow=!listShow" 
                            :class="['padding-tb-sm',{'u-border-bottom':!listShow}]"
                        >
                            口味筛选
                        </view>
                        <u-cell-group v-if="listShow">
                            <u-cell 
                                v-for="(itm,idx) in item.list" 
                                :title="itm.tname" 
                                isLink
                                @click="handleList(itm,1)" 
                            ></u-cell>
                        </u-cell-group>
                        <view @tap="sceneShow=!sceneShow" class="padding-tb-sm u-border-top">
                            场景筛选
                        </view>
                        <u-cell-group v-if="sceneShow">
                            <u-cell 
                            v-for="(itm,idx) in item.scene" 
                            :title="itm.tname" 
                            isLink                            
                            @click="handleList(itm,2)" 
                        ></u-cell>
                        </u-cell-group>
                    </view>
                </view>
            </view>
        </u-popup>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                // condition:{ // 商品列表查询条件对象
                //     bcid:1,
                // },
                cfylist:[],
                sceneShow:false,
                listShow:false,
                show: false,
                glist: [],
                page: 0,
                tabArr: [{
                        name: '分类',
                        bcid: '',
                        target: ''
                    },
                    {
                        name: '蛋糕',
                        bcid: '1',
                        target: '/pages/cake'
                    },
                    {
                        name: '面包',
                        bcid: '11',
                        target: '/pages/bread'
                    },
                    {
                        name: '小食',
                        bcid: '6',
                        target: '/pages/food'
                    },
                    {
                        name: '购物车',
                        bcid: '',
                        target: '/pages/cart/cart'
                    }
                ]
            }
        },
        computed:{
            num(){
                return this.$store.state.count.num
            },
            condition(){
                return this.$store.state.condition.cond
            }
        },
        methods: {
            /* handleDetail(idx) {
                console.log(idx);
                uni.navigateTo({
                    url: '../detail/detail?idx=' + idx
                })
            }, */
            loadData() {
                let skip = this.page * 8
                let wh = JSON.stringify(this.condition)
                let url = `/1.1/classes/goods?where=${wh}&limit=8&skip=${skip}`;
                this.$get(url).then(res => {
                    uni.stopPullDownRefresh()
                    let {
                        results
                    } = res
                    if (results.length) {
                        this.page++
                        this.glist = [
                            ...this.glist,
                            ...res.results
                        ]
                        return
                    }
                    uni.showToast({
                        title: '这回真没了...',
                        icon: 'none'
                    })
                })
            },
            handleTab(item) {
                let {
                    bcid,
                    target
                } = item
                if (bcid) { // 商品列表数据更新
                    // this.glist = []
                    // this.page = 0
                    // this.condition.bcid = Number(bcid)
                    this.$store.commit('changeCondition',{
                        bcid:Number(bcid)
                    })
                    this.reloadData()
                }
                if (!bcid && !target) { // 侧边分类
                    this.show = true
                }
                if (!bcid && target) {
                    uni.navigateTo({
                        url:target
                    })
                }
            },
            handleClose(){
                this.show = false
            },
            handleList({bid,tid},type){
                // 口味场景筛选
                // console.log(typeof bid);
                let obj = {bcid:bid}
                type === 1 ? obj.fid=tid : obj.sid=tid
                this.$store.commit('changeCondition',obj)
                this.reloadData()
            },
            reloadData(){ //刷新页面数据
                this.glist = []
                this.page = 0
                this.loadData()
            }
        },
        onLoad() {
            this.loadData()            
            this.$get('/1.1/classes/classify').then(res=>{
                console.log(res)
                this.cfylist = res.results
            })
        },
        onReachBottom() {
            console.log('触底了');
            this.loadData()
        },
        onPullDownRefresh() {
            this.glist = []
            this.page = 0
            this.loadData()
        }
    }
</script>

<style lang="scss">
page {
    padding-top: 100upx;
    padding-bottom: 120upx;
}

.cont {
    display: flex;
    flex-wrap: wrap;
    padding: 15upx;
    justify-content: space-between;
}

.fixed {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    box-shadow: 0 0 10upx 2upx rgba(0, 0, 0, 0.2);
}
.cu-bar{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    z-index: 2;
}
.pop-cont{
    width: 400upx;
    margin-top: 200upx;
}


</style>

goods-item.vue

<template>
    <view class="cake-item">
        <image @tap="handleDetail" class="poster" :src="gdata.img" mode=""></image>
        <view class="info-cont">
            <view class="info flex align-center justify-between">
                <view class="">
                    <view class="fs-28">
                        {{gdata.name}}
                    </view>
                    <view class="fs-16">
                        {{gdata.french}}
                    </view>
                </view>
                <view @click.stop="handleCartAdd" class="cart-btn margin-right-sm">
                    <text class="iconfont icon-caigou"></text>
                </view>
                
            </view>
            <view class="fs-18">
                <text class="fs-14">Y</text>
                {{gdata.price}}
            </view>
            
        </view>
    </view>
    
</template>

<script>
    export default {
        name:"goods-item",
        props:['gdata'],
        data() {
            return {
                
            };
        },
		methods:{
			handleDetail() {
				uni.setStorage({
					key:'detail',
					data:this.gdata,
					success: () => {
						uni.navigateTo({
							url: '/pages/detail/detail'
						})
					}
				})
			    
			},
            handleCartAdd(){
                this.$store.commit('cart/cartAddMut',{
                    ...this.gdata,
                    idx:0
                })
            }
		}
    }
</script>

<style lang="scss">
.cake-item{
    width: 350upx;
    .poster{
        height: 350upx;
        background-color: #f5f5f5;
    }
    .fs-28{
        font-size: 28upx;
        margin-top: 24upx;
    }
    .fs-16{
        font-size: 16upx;
        margin: 14upx 0;
    }
    .fs-18{
        font-size: 18upx;
        margin-bottom: 22upx;
    }
    .cart-btn{
        width: 60upx;
        height: 60upx;
        border-radius: 50%;
        background-color: #ffe32a;
        text-align: center;
        line-height: 60upx;
        /* 在按钮处于活动状态时添加转换 */
        .cart-btn.active {
            transform: scale(0.98);
            box-shadow: 3px 2px 22px 1px rgba(0, 0, 0, 0.24);
        }
    }
}

</style>

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值