Springboot3 + MyBatis-Plus + MySql + Uniapp 商品加入购物车功能实现(最新教程附源码)

news2024/11/15 18:48:42

Springboot3 + MyBatis-Plus + MySql + Uniapp 商品加入购物车功能实现(针对上一篇sku)

  • 1、效果展示
  • 2、后端代码
    • 2.1 model
    • 2.2 mapper server serverImpl 参照上一篇自动生成
    • 2.3 controller
  • 3、前端代码
    • 3.1 index.js
    • 3.2 shop-info.vue
    • 3.3 ShopBottomButton.vue
    • 3.4 shop-cart.vue

本文章基于上一篇文章 Springboot3 + MyBatis-Plus + MySql + Uniapp 实现商品规格选择sku(附带自设计数据库,最新保姆级教程)

1、效果展示


在这里插入图片描述

2、后端代码

2.1 model

package com.zhong.model.entity.shop;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.math.BigDecimal;
import java.util.Date;

import com.zhong.model.entity.BaseEntity;
import lombok.Data;

/**
 *
 * @TableName shop_cart
 */
@TableName(value ="shop_cart")
@Data
public class ShopCart extends BaseEntity {

    /**
     * 商品ID
     */
    private Integer goodsId;
    /**
     * 附加信息 “自营”
     */
    private String businessName;

    /**
     * 商品店铺
     */
    private String shopName;

    /**
     * 商品主图
     */
    private String mainImage;

    /**
     * 商品标题
     */
    private String title;

    /**
     * 商品价格
     */
    private BigDecimal price;

    /**
     * 商品数量
     */
    private Integer goodsNum;

    /**
     * 购物车所属用户id
     */
    private Integer userId;

    /**
     * 所选商品规格id
     */
    private Integer goodsSpecsId;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

2.2 mapper server serverImpl 参照上一篇自动生成

ShopCartService

package com.zhong.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zhong.model.entity.shop.ShopCart;
import com.zhong.vo.shop.ShopCartVo;
import com.zhong.vo.shop.ShopSkuVo;

/**
* @author zhong
* @description 针对表【shop_cart】的数据库操作Service
* @createDate 2024-09-19 10:53:15
*/
public interface ShopCartService extends IService<ShopCart> {

    Page<ShopCartVo> pageItem(Page<ShopCart> page);

    ShopSkuVo getSpecsById(Long id);

}

ShopCartServiceImpl

package com.zhong.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.zhong.login.LoginUserHolder;
import com.zhong.mapper.shop.ShopCartMapper;
import com.zhong.mapper.shop.ShopSpecsMapper;
import com.zhong.model.entity.shop.ShopCart;
import com.zhong.model.entity.shop.ShopSku;
import com.zhong.model.entity.shop.ShopSpecs;
import com.zhong.service.ShopCartService;
import com.zhong.service.ShopSkuService;
import com.zhong.vo.shop.ShopCartVo;
import com.zhong.vo.shop.ShopSkuVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zhong
 * @description 针对表【shop_cart】的数据库操作Service实现
 * @createDate 2024-09-19 10:53:15
 */
@Service
public class ShopCartServiceImpl extends ServiceImpl<ShopCartMapper, ShopCart>
        implements ShopCartService {

    @Autowired
    private ShopCartMapper shopCartMapper;
    @Autowired
    private ShopSpecsMapper shopSpecsMapper;
    @Autowired
    private ShopSkuService shopSkuService;

    @Override
    public Page<ShopCartVo> pageItem(Page<ShopCart> page) {
        Long userId = LoginUserHolder.getLoginUser().getUserId();
        LambdaQueryWrapper<ShopCart> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ShopCart::getIsDeleted, 0)
                .eq(ShopCart::getUserId, userId);
        // 获取所有购物车列表
        Page<ShopCart> shopCartPage = shopCartMapper.selectPage(page, queryWrapper);

        // 初始化返回的 ShopCartVo 分页对象
        Page<ShopCartVo> shopCartVoPage = new Page<>(shopCartPage.getCurrent(), shopCartPage.getSize(), shopCartPage.getTotal());

        // 获取 ShopCart 列表
        List<ShopCart> shopCartList = shopCartPage.getRecords();
        List<ShopCartVo> shopCartListVo = new ArrayList<>();

        // 遍历 ShopCart 列表并转换为 ShopCartVo 列表
        for (ShopCart shopCart : shopCartList) {
            ShopCartVo shopCartVo = new ShopCartVo();

            // 使用 BeanUtils 复制属性
            BeanUtils.copyProperties(shopCart, shopCartVo);

            // 获取商品规格信息并设置到 ShopCartVo 中
            Integer specsId = shopCart.getGoodsSpecsId();
            if (specsId != null) {
                ShopSkuVo shopSkuVo = getSpecsById(Long.valueOf(specsId));
                shopCartVo.setSpecs(shopSkuVo);
            }

            // 添加到 ShopCartVo 列表
            shopCartListVo.add(shopCartVo);
        }

        // 将转换后的 ShopCartVo 列表设置到分页对象中
        shopCartVoPage.setRecords(shopCartListVo);
        return shopCartVoPage;
    }

    @Override
    public ShopSkuVo getSpecsById(Long id) {
        ShopSkuVo shopSkuVo = new ShopSkuVo();
        ShopSpecs shopSpecs = shopSpecsMapper.selectById(id);
        // 新建一个规格详情信息 Vo 方便后续添加到 shopSkuVo
        List<ShopSku> skuArrayList = new ArrayList<>();
        if (shopSpecs.getSku1() != null) {
            // 新建一个 shopSku 方便添加到 List<ShopSku>
            ShopSku shopSku = new ShopSku();
            // 根据规格ID获取规格详情
            ShopSku sku = shopSkuService.getById(shopSpecs.getSku1());
            shopSkuVo.setId(shopSpecs.getId());
            shopSku.setAttr(sku.getAttr());
            // 将规格值添加到 shopSku
            shopSku.setAttrValue(sku.getAttrValue());
            skuArrayList.add(shopSku);
        }
        if (shopSpecs.getSku2() != null) {
            // 新建一个 shopSku 方便添加到 List<ShopSku>
            ShopSku shopSku = new ShopSku();
            // 根据规格ID获取规格详情
            ShopSku sku = shopSkuService.getById(shopSpecs.getSku2());
            shopSkuVo.setId(shopSpecs.getId());
            shopSku.setAttr(sku.getAttr());
            // 将规格值添加到 shopSku
            shopSku.setAttrValue(sku.getAttrValue());
            skuArrayList.add(shopSku);
        }
        if (shopSpecs.getSku3() != null) {
            // 新建一个 shopSku 方便添加到 List<ShopSku>
            ShopSku shopSku = new ShopSku();
            // 根据规格ID获取规格详情
            ShopSku sku = shopSkuService.getById(shopSpecs.getSku3());
            shopSkuVo.setId(shopSpecs.getId());
            shopSku.setAttr(sku.getAttr());
            // 将规格值添加到 shopSku
            shopSku.setAttrValue(sku.getAttrValue());
            skuArrayList.add(shopSku);
        }
        if (shopSpecs.getSku4() != null) {
            // 新建一个 shopSku 方便添加到 List<ShopSku>
            ShopSku shopSku = new ShopSku();
            // 根据规格ID获取规格详情
            ShopSku sku = shopSkuService.getById(shopSpecs.getSku4());
            shopSkuVo.setId(shopSpecs.getId());
            shopSku.setAttr(sku.getAttr());
            // 将规格值添加到 shopSku
            shopSku.setAttrValue(sku.getAttrValue());
            skuArrayList.add(shopSku);
        }
        shopSkuVo.setSkus(skuArrayList);
        shopSkuVo.setPrice(shopSpecs.getPrice());
        return shopSkuVo;
    }
}





2.3 controller


package com.zhong.controller.shop;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhong.login.LoginUserHolder;
import com.zhong.model.entity.shop.ShopCart;
import com.zhong.result.Result;
import com.zhong.service.ShopCartService;
import com.zhong.vo.shop.ShopCartVo;
import com.zhong.vo.shop.ShopInfoVo;
import com.zhong.vo.shop.ShopSkuVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @ClassName : ShopCartController
 * @Description :
 * @Author : zhx
 * @Date: 2024-09-19 10:55
 */
@RestController
@RequestMapping("/app/shop/cart")
@Tag(name = "购物车信息")
public class ShopCartController {
    @Autowired
    private ShopCartService service;

    @Operation(summary = "添加到购物车")
    @PostMapping("add")
    public Result getDetailById(@RequestBody ShopCart shopCart) {
        // 判断是否存在
        LambdaQueryWrapper<ShopCart> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ShopCart::getIsDeleted, 0)
                .eq(ShopCart::getGoodsSpecsId, shopCart.getGoodsSpecsId());
        ShopCart dbShopCart = service.getOne(queryWrapper);
        if(dbShopCart != null) {
            dbShopCart.setPrice(dbShopCart.getPrice().add(shopCart.getPrice()));
            dbShopCart.setGoodsNum(dbShopCart.getGoodsNum() + shopCart.getGoodsNum());
            service.saveOrUpdate(dbShopCart);
        }else {
            Long userId = LoginUserHolder.getLoginUser().getUserId();
            shopCart.setUserId(Math.toIntExact(userId));
            shopCart.setIsDeleted((byte) 0);
            service.saveOrUpdate(shopCart);
        }
        return Result.ok();
    }

    @Operation(summary = "分页获取购物车商品信息")
    @GetMapping("listItem")
    public Result<Page<ShopCartVo>> listItem(@RequestParam long current, @RequestParam long size) {
        Page<ShopCart> shopCartPage = new Page<>(current, size);
        Page<ShopCartVo> page = service.pageItem(shopCartPage);
        return Result.ok(page);
    }

    @Operation(summary = "根据id获取商品规格")
    @GetMapping("getSpecsById")
    public Result<ShopSkuVo> getSpecsById(@RequestParam Long id) {
        ShopSkuVo shopSkuVo = service.getSpecsById(id);
        return Result.ok(shopSkuVo);
    }
}

3、前端代码

3.1 index.js

import http from '@/utils/request.js';

export const getAllShopApi = (params) => {
	return http.get(`/app/shop/listItem?current=${params.current}&size=${params.size}`)
}

export const getShopByIdApi = (id) => {
	return http.get(`/app/shop/getDetailById?id=${id}`)
}

// 添加到购物车
export const addShopToCartApi = (params) => {
	return http.post(`/app/shop/cart/add`, params)
}

// 分页获取购物车
export const getAllShopCartApi = (params) => {
	return http.get(`/app/shop/cart/listItem?current=${params.current}&size=${params.size}`)
}

3.2 shop-info.vue

<template>
	<view>
		<template v-for="(item, index) in [data]" :key="index">
			<view class="">
				<up-swiper :list="item.shopImgSwiper" circular :autoplay="true" bgColor="#ffffff" height="360rpx"
					imgMode="auto"></up-swiper>
			</view>

			<view class="connect card card-shadow">
				<view class="price">
					<view class="">
						<up-text mode="price" :text="item.newPrice" color="red" size="24"></up-text>
					</view>
					<view class="">
						<text>已售{{item.saleNumber}}</text>
					</view>
				</view>
				<!-- 标题 -->
				<view class="title">
					<up-row customStyle="margin-bottom: 10px">
						<up-col span="2" v-if="item.businessName">
							<view class="" style="display: flex;">
								<up-tag :text="item.businessName" size="mini" type="error"></up-tag>
							</view>
						</up-col>
						<up-col :span="item.businessName?10 :12">
							<text>{{item.title}}</text>
						</up-col>
					</up-row>
				</view>
				<!-- 发货 -->
				<view class="logistics flex" style=" position: relative;">
					<up-icon name="car"></up-icon>
					<view class="" style="width: 20rpx;"></view>
					<view class="font-lite-size">
						<text>承诺24小时内发货,晚发必赔</text>
					</view>
					<view class="" style="position: absolute;right: 10rpx;">
						<up-icon name="arrow-right"></up-icon>
					</view>
				</view>

				<!-- 破损 -->
				<view class="pock flex" style=" position: relative;">
					<up-icon name="car"></up-icon>
					<view class="" style="width: 20rpx;"></view>
					<view class="font-lite-size">
						<text>破损包退 | 退货运费险 | 极速退款 | 7天无理由退换</text>
					</view>
					<view class="" style="position: absolute;right: 10rpx;">
						<up-icon name="arrow-right" size="16"></up-icon>
					</view>
				</view>
			</view>

			<!-- 评价 -->
			<view class="card card-shadow">
				<ShopCommentVue></ShopCommentVue>
			</view>

			<!-- 店铺信息 -->
			<view class="card card-shadow">
				<StoreInformationVue></StoreInformationVue>
			</view>
			<!-- 商品详情图片 -->
			<view class="bb-info card card-shadow" v-if="data.shopImgInfo.length> 0">
				<ShopInfoImageListVue :imgList="data.shopImgInfo"></ShopInfoImageListVue>
			</view>
			<!-- 提示 -->
			<view class="tips card card-shadow">
				<ShopTipsVue></ShopTipsVue>
			</view>
			<!-- 底部tabbar安全距离 -->
			<view class="" style="height: 140rpx;">

			</view>
		</template>
		<!-- 加入购物车等操作 -->
		<view class="bottom">
			<ShopBottomButtonVue :data="data"></ShopBottomButtonVue>
		</view>
	</view>
</template>

<script setup>
	import {
		reactive,
		ref,
		onMounted
	} from 'vue';

	import ShopCommentVue from '@/pages/components/Home/ShopComment.vue';
	import StoreInformationVue from '@/pages/components/Home/StoreInformation.vue';
	import ShopInfoImageListVue from '@/pages/components/Home/ShopInfoImageList.vue';
	import ShopTipsVue from '@/pages/components/Home/ShopTips.vue';
	import ShopBottomButtonVue from '@/pages/components/Home/ShopBottomButton.vue';
	import {
		onLoad
	} from "@dcloudio/uni-app"
	import {
		getShopByIdApi
	} from "@/pages/api/shop/index.js"
	const shopId = ref();
	const data = ref();
	onLoad((options) => {
		shopId.value = options.id;
	})
	onMounted(async () => {
		console.log(shopId.value);
		let res = await getShopByIdApi(shopId.value);
		data.value = res;
		console.log(res);
	})
	// 父组件中的价格数据
	const price = ref(null);
	// 处理子组件传来的价格更新
	const handlePriceUpdate = (newPrice) => {
	  price.value = newPrice;
	};
</script>

<style lang="less" scoped>
	.card-shadow {
		border-radius: 20rpx;
		box-shadow: 10rpx 10rpx 10rpx 10rpx rgba(0.2, 0.1, 0.2, 0.2);
	}

	.card {
		margin: 20rpx;
		padding: 20rpx;
		background-color: #FFF;
		border-radius: 20rpx;
	}

	.font-lite-size {
		font-size: 26rpx;
	}

	.flex {
		display: flex;
		align-items: center;
	}

	.title {
		margin-top: 20rpx;
	}

	.pock {
		margin: 20rpx 0;
	}

	.price {
		padding-right: 20rpx;
		display: flex;
		justify-content: space-between;
		align-items: center;
	}
</style>

3.3 ShopBottomButton.vue

<template>
	<view class="mains">
		<view class="connect">
			<view class="letf-connect">
				<up-icon name="gift" size="40rpx"></up-icon>
				<text style="font-size: 26rpx;">店铺</text>
			</view>
			<view class="letf-connect">
				<up-icon name="kefu-ermai" size="40rpx"></up-icon>
				<text style="font-size: 26rpx;">客服</text>
			</view>
			<view class="letf-connect" @click="toShopCart">
				<up-icon name="shopping-cart" size="40rpx"></up-icon>
				<text style="font-size: 26rpx;">购物车</text>
			</view>

			<view class="" style="display: flex;flex: 1;padding-left: 20rpx;">
				<up-button text="加入购物车" type="warning" @click="addCartButtonFun"></up-button>
				<up-button text="立即购买" type="success" @click="nowBuyFun"></up-button>
			</view>
		</view>

		<!-- 弹出层选择商品规格 -->
		<up-popup :show="show" mode="bottom" :round="10" @close="close" @open="open">
			<view>
				<view class="top">
					<up-image :src="props.data.mainImage" width="200rpx" height="300rpx" radius="10"></up-image>
					<view style="padding-left: 40rpx;">
						<text style="flex: 1;overflow: hidden;">{{props.data.title}}</text>
						<view style="padding: 20rpx 0;" v-if="calculatedPrice">
							<up-text mode="price" :text="calculatedPrice" color="red" size="20"></up-text>
						</view>
						<view style="padding: 20rpx 0;" v-else>
							<up-text mode="price" :text="props.data.newPrice * shopNum" color="red" size="20"></up-text>
						</view>
						<view style="display: flex;padding-top: 20rpx;">
							<up-number-box v-model="shopNum" min="1"></up-number-box>
						</view>
					</view>
				</view>

				<!-- 渲染规格 -->
				<view class="">
					<template v-for="(item,index) in resSkuGroup">
						<view style="padding-left: 20rpx;">{{item.key}}</view>
						<view style="display: flex;">
							<template v-for="(tag,i) in item.value" :key="i">
								<view class="" style="display: flex;padding:20rpx;">
									<up-tag :text="tag.info" :plain="!tag.isCheck" :color="tag.isCheck?'#FFF':'#000'"
										:borderColor="tag.isCheck?'#FFF':'#000'" type="error"
										@click="changeTagIsCheckFun(tag,index)"></up-tag>
								</view>
							</template>
						</view>
					</template>
				</view>

				<view class="" style="padding: 20rpx;" v-if="isBuy">
					<up-button text="立即购买" shape="circle" type="error" @click="BuyShopFun"></up-button>
				</view>
				<view class="" style="padding: 20rpx;" v-else>
					<up-button text="加入购物车" shape="circle" type="error" @click="addCartFun"></up-button>
				</view>
			</view>
		</up-popup>
	</view>
</template>

<script setup>
	import {
		computed,
		onMounted,
		reactive,
		defineEmits,
		watch,
		ref
	} from 'vue';
	import {
		addShopToCartApi
	} from "@/pages/api/shop/index.js";
	// 创建响应式数据
	const props = defineProps({
		data: Object
	});
	const show = ref(false);
	const resData = ref();
	const resSkuData = ref();
	const resSkuGroup = ref();
	const resDataFun = async () => {
		resSkuGroup.value = await props.data.skuGroup;
		resSkuData.value = await props.data.shopSpecs;
		console.log(props.data.shopSpecs);
		console.log(resSkuData.value);
	}
	const changeTagIsCheckFun = (item, index) => {
		resSkuGroup.value[index].value.map(x => {
			if (x.info == item.info) {
				x.isCheck = true;
			} else {
				x.isCheck = false;
			}
		})
		console.log(resSkuGroup.value);
	}

	// 通过 computed 计算选中的属性值
	const checkedAttributes = computed(() => {
		return resSkuGroup.value.map(option => ({
			attr: option.key,
			attrValue: option.value.find(item => item.isCheck)?.info || null
		}));
	});
	// 商品数量
	const shopNum = ref(1);

	// 根据选中的属性值匹配 SKU,返回匹配的 SKU 对象
	const matchingSku = computed(() => {
		return resSkuData.value.find(sku => {
			return sku.skus.every(skuAttr => {
				return checkedAttributes.value.some(attr =>
					attr.attr === skuAttr.attr && attr.attrValue === skuAttr.attrValue
				);
			});
		});
	});

	// 计算匹配 SKU 的价格和 ID
	const calculatedPrice = computed(() => {
		return matchingSku.value ? matchingSku.value.price * shopNum.value : null;
	});

	const matchingSkuId = computed(() => {
		return matchingSku.value ? matchingSku.value.id : null;
	});

	// 区分是加入购物车还是立即购买
	const isBuy = ref(false);

	// 加入购物车
	const addCartButtonFun = () => {
		show.value = true;
		isBuy.value = false;
	}
	// 立即购买 
	const nowBuyFun = () => {
		show.value = true;
		isBuy.value = true;
	}
	// 立即购买操作 
	const BuyShopFun = () => {

	}



	onMounted(() => {
		console.log(props.data);
		resDataFun();
	})
	// 定义方法  
	const open = () => {
		// 打开逻辑,比如设置 show 为 true  
		show.value = true;
		// console.log('open');  
	}

	const close = () => {
		// 关闭逻辑,设置 show 为 false  
		show.value = false;
		// console.log('close');  
	}
	const toShopCart = () => {
		uni.navigateTo({
			url: "/pages/src/home/shop-cart/shop-cart"
		})
	}
	// 添加到购物车
	const addCartFun = async () => {
		let res = {
			businessName: props.data.businessName,
			shopName: props.data.shopName,
			mainImage: props.data.mainImage,
			title: props.data.title,
			price: calculatedPrice.value,
			goodsId: props.data.id,
			goodsNum: shopNum.value,
			goodsSpecsId: matchingSkuId.value
		}
		console.log(res);
		await addShopToCartApi(res);
		close();
		uni.showToast({
			title: "添加成功",
			icon: 'success'
		})
	}
</script>

<style lang="scss" scoped>
	.top {
		display: flex;
		padding: 40rpx;
	}

	.mains {
		position: fixed;
		bottom: 0;
		left: 0;
		width: 100%;
		/* 占据全宽 */
		height: 120rpx;
		/* Tabbar 高度 */
		background-color: #FFF;
		border-top: 2rpx solid #7d7e80;
	}

	.connect {
		display: flex;
		justify-content: space-around;
		padding: 20rpx;
		align-items: center;
	}

	.letf-connect {
		padding: 0 10rpx;
		display: flex;
		flex-direction: column;
		align-items: center;
	}
</style>

3.4 shop-cart.vue

<template>
	<view class="">

		<view class="" v-if="state.cartItems.length == 0">
			<up-empty mode="car" icon="http://cdn.uviewui.com/uview/empty/car.png">
			</up-empty>
		</view>

		<view class="card" v-else>
			<template v-for="(info, j) in state.cartItems" :key="j">
				<view class="cart-data card-shadow">
					<view class="" style="display: flex;">
						{{info.shopName}}<up-icon name="arrow-right"></up-icon>
					</view>
					<template v-for="(item, index) in info.items" :key="index">
						<view class="" style="display: flex;padding: 20rpx 0;align-items: center;">
							<view>
								<up-checkbox :customStyle="{marginBottom: '8px'}" usedAlone
									v-model:checked="item.isChoose" @change="toggleItemChoose(item.shopName, item.id)">
								</up-checkbox>
							</view>
							<view class="cart-image">
								<up-image :src="item.mainImage" mode="widthFix" height="200rpx" width="220rpx"
									radius="10"></up-image>
							</view>
							<view>
								<view class="cart-right">
									<view style="margin-bottom: 10rpx;font-size: 30rpx;">{{item.title}}</view>
									<view class="" v-if="item.specs" style="display: flex;">
										<template v-for="(sku, i) in item.specs.skus">
											<view
												style="margin-bottom: 20rpx;font-size: 26rpx;color: #7d7e80;margin-right: 10rpx;">
												{{sku.attr}}/{{sku.attrValue}}
											</view>
										</template>
									</view>

									<view class="" style="display: flex;align-items: center;">
										<up-text mode="price" :text="item.price"></up-text>
										<view class="" style="width: 10rpx;"></view>
										<up-number-box v-model="item.goodsNum"
											@change="val => changeItemQuantity(item,item.id, val.value)"
											min="1"></up-number-box>
									</view>
								</view>
							</view>
						</view>
					</template>
				</view>
			</template>
		</view>
		<view class="" style="height: 160rpx;">

		</view>
		<view class="foot card">
			<view class="card-connect">
				<up-checkbox :customStyle="{marginBottom: '8px'}" usedAlone v-model:checked="state.allChose"
					@change="toggleAllChose">
				</up-checkbox>
				<view class="" style="display: flex; align-items: center;">
					<view style="font-size: 28rpx;">全选</view>
					<view style="padding-left: 20rpx;font-size: 24rpx;">已选{{selectedItemsCount}},合计</view>
					<view class="" style="display: flex;flex: 1;">
						<up-text mode="price" :text="totalSelectedPrice" color="red" size="18"></up-text>
					</view>
				</view>
				<view class="" style="width: 20rpx;position: relative;">

				</view>
				<view class="" style="position: absolute;right: 40rpx;">
					<view class="" style="display: flex;">
						<up-button type="error" text="去结算" shape="circle" style="width: 150rpx;"
							@click="toSubmitOrder"></up-button>
					</view>
				</view>
				<up-toast ref="uToastRef"></up-toast>
			</view>
		</view>
	</view>
</template>

<script setup>
	import {
		ref,
		computed,
		onMounted,
		watch
	} from 'vue';
	import {
		useCartStore
	} from '@/pages/store/cart/cart.js'

	import {
		getAllShopCartApi
	} from "@/pages/api/shop/index.js"

	import {
		storeToRefs
	} from "pinia";
	// 使用 Pinia store
	const cartStore = useCartStore();
	// 获取状态和操作
	const {
		state,
		selectedItemsCount,
		totalSelectedPrice,
		selectedItems
	} = storeToRefs(cartStore);

	const {
		toggleItemChoose,
		changeItemQuantity,
		toggleAllChose
	} = cartStore;

	const current = ref(1);
	const size = ref(10);

	onMounted(async () => {
		let res = {
			current: current.value,
			size: size.value
		}
		// 恢复购物车数据
		const response = await getAllShopCartApi(res)
		console.log(response);
		const groupedItems = [];
		response.records.forEach(item => {
			// 查找是否已经存在相同店铺名的对象
			const shop = groupedItems.find(shop => shop.shopName === item.shopName);
			if (shop) {
				// 如果存在,直接将商品添加到该店铺的商品列表中
				shop.items.push(item);
			} else {
				// 如果不存在,创建一个新的店铺对象,并将商品添加进去
				groupedItems.push({
					shopName: item.shopName,
					items: [item]
				});
			}
		});

		console.log(groupedItems);
		cartStore.setCartItems(groupedItems);
	});
	// 创建响应式数据  
	const show = ref(false);
	// 方法
	const uToastRef = ref(null)
	const showToast = (params) => {
		uToastRef.value.show(params);
	}
	const toSubmitOrder = () => {
		if (selectedItems.value.length > 0) {
			uni.navigateTo({
				url: "/pages/src/home/submit-order/submit-order"
			})
		} else {
			showToast({
				type: 'default',
				title: '默认主题',
				message: "您还没有选择商品哦",
			});
		}
	}
</script>

<style lang="scss" scoped>
	.foot {
		position: fixed;
		bottom: 0;
		left: 0;
		width: 90%;
		/* 占据全宽 */
		height: 100rpx;
		/* Tabbar 高度 */
		background-color: #FFF;
		display: flex;
		align-items: center;

		.card-connect {
			display: flex;
			align-items: center;
			justify-content: space-between;
		}
	}

	.card {
		margin: 20rpx;
		padding: 20rpx;
		background-color: #FFF;
		border-radius: 20rpx;
	}

	.card-shadow {
		border-radius: 20rpx;
		box-shadow: 10rpx 10rpx 10rpx 10rpx rgba(0.2, 0.1, 0.2, 0.2);
	}

	.cart-data {
		margin-bottom: 40rpx;
		padding: 20rpx;
		display: flex;
		flex-wrap: wrap;
		align-items: center;

		.cart-image {
			flex: 1;
		}

		.cart-right {
			display: flex;
			flex-direction: column;
			padding-left: 20rpx;
		}
	}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2154632.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

掌上高考爬虫逆向分析

目标网站 aHR0cHM6Ly93d3cuZ2Fva2FvLmNuL3NjaG9vbC9zZWFyY2g/cmVjb21zY2hwcm9wPSVFNSU4QyVCQiVFOCU4RCVBRg 一、抓包分析 二、逆向分析 搜索定位加密参数 本地生成代码 var CryptoJS require(crypto-js) var crypto require(crypto);f "D23ABC#56"function v(t…

机器学习之实战篇——图像压缩(K-means聚类算法)

机器学习之实战篇——图像压缩(K-means聚类算法&#xff09; 0. 文章传送1.实验任务2.实验思想3.实验过程 0. 文章传送 机器学习之监督学习&#xff08;一&#xff09;线性回归、多项式回归、算法优化[巨详细笔记] 机器学习之监督学习&#xff08;二&#xff09;二元逻辑回归 …

Unity自我实现响应式属性

其实只是写着玩,响应式编程建议使用UniRx插件(一套成熟的响应式编程解决方案),我写的主要是借鉴一下这个思想,实现的也不够优雅,不过逻辑也算严密可以正常使用.你可以查看我写的理解响应式属性的思想. 借鉴UniRx的ReactiveProperty类,且UniRx不仅有响应式属性. using System; …

光伏板缺陷红外检测数据集

光伏板缺陷红外检测数据集 包含以下4个数据文件&#xff1a; /train&#xff1a;训练集 /valid&#xff1a;验证集 /test&#xff1a;测试集 README.txt&#xff1a;数据说明 【数据说明】检测目标以Pascal VOC格式进行标注&#xff0c;对每个图像进行以下预处理&#xff0c;统…

【Linux笔记】如何将内容从一个文件复制到另一个文件

比如&#xff1a;将文件tmp_file.txt中的部分数据&#xff0c;复制到file01.txt中去 tmp_file.txt文中内容&#xff1a; file01.txt为空文档 一、使用vi编辑器 I、文件中直接使用:e 目标文件进行切换文件复制 1、打开被复制文件 vi tmp_file.txt 2、进入一般命令模式 默认情况为…

2024年华为杯-研赛更新时间轴-资料分享

本次 助攻CDF题 问题一二三问均已完成更新&#xff0c;更新计划轴如图所示 由于赛题之间存在紧密的联系&#xff0c;单独发布问题一二&#xff0c;有可能与明天最终论文不相符&#xff0c;会根据后面问题对前面几问进行调整。个人建议&#xff0c;等明天上午的完整论文即可 题 …

ACT训练调参技巧

ACT Tuning Tips 这里是针对斯坦福Aloha机械臂远程训练调参技巧的中文解释&#xff0c;初学者可能会对此感到陌生&#xff0c;不过不用担心&#xff0c;多尝试&#xff0c;多实验。 - Chunk size is the most important param to tune when applying ACT to a new environment…

【YOLO目标检测学生课堂行为数据集】共4266张、已标注txt格式、有训练好的yolov5的模型

目录 说明图片示例 说明 数据集格式&#xff1a;YOLO格式 图片数量&#xff1a;4266 标注数量(txt文件个数)&#xff1a;4266 标注类别数&#xff1a;3 标注类别名称&#xff1a;hand、read、write 数据集下载&#xff1a;学生课堂行为数据集 图片示例 数据集图片&#…

HTML5中新增元素介绍

引入了许多新元素&#xff0c;以增强网页的语义和功能。这些新元素大致可以按以下几类进行分类和介绍。 下面是对各标签的详解&#xff0c;section、header、footer、nav、article、aside、figure、code、dialog、meter、time、progress、video、audio、details、atagrid、menu…

AIGC7: 高通骁龙AIPC开发者沙龙过程记录A

图中是一座高耸的宫殿。 就像AI的出现&#xff0c;慢慢初现端倪&#xff0c;头角峥嵘。 背景 一直以来都比较关注AI的发展&#xff0c;有幸再一次参加异常AI的盛会。 从我的角度看。 高通是一家生产芯片的公司&#xff0c;国内的小米&#xff0c;荣耀&#xff0c;Oppo , Vi…

Qt_窗口界面QMainWindow的介绍

目录 1、菜单栏QMenuBar 1.1 使用QMainWindow的准备工作 1.2 在ui文件中设计窗口 1.3 在代码中设计窗口 1.4 实现点击菜单项的反馈 1.5 菜单中设置快捷键 1.6 菜单中添加子菜单 1.7 菜单项中添加分割线和图标 1.8 关于菜单栏创建方式的讨论 2、工具栏QToolBar …

[产品管理-32]:NPDP新产品开发 - 30 - 文化、团队与领导力 - 领导力与团队的可持续发展

目录 一、团队领导的领导力 1.1 领导力 1、领导力的定义 2、领导力的重要性 3、领导力的构成要素 4、如何提升领导力 1.2 情商 二、虚拟团队 1、团队定义与特征 2、团队优势 3、团队挑战与应对策略 三、可持续发展 四、团队管理和领导力中的度量指标 4.1 激励创新…

unix中的进程标识以及使用场景

一、前言 本文将介绍unix系统中的进程标识以及使用场景。进程标识和用户标识类似&#xff0c;只不过其指代的对象是一个进程。我们常把进程标识称为进程ID&#xff0c;本文将讨论如下内容&#xff1a; 1.什么是进程标识&#xff1f; 2.特殊的进程标识 3.如果获取以及使用进程标…

深度学习02-pytorch-09(pytorch完结篇)-基本使用介绍-线性回归案例

使用PyTorch的基本流程&#xff1a;数据准备&#xff1a;通过make_regression生成回归数据&#xff0c;使用 TensorDataset 和 DataLoader 来封装数据。 模型定义&#xff1a;使用 nn.Module 或内置层&#xff08;如 nn.Linear&#xff09;来定义模型结构。 损失函数和优化器…

【全网最全】2024年华为杯研赛D题成品论文获取入口(后续会更新)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片&#xff0c;那是获取资料的入口&#xff01; 点击链接加入【2024华为杯研赛资料汇总】&#xff1a;https://qm.qq.com/q/XzdIsvbiM0https://qm.qq.com/q/XzdIsvbiM0 你是否在寻找数学建模比赛的突破点…

【他山之石】优化 JavaScript 的乐趣与价值(下)

前言 继本文的 上篇 发表之后&#xff0c;没想到反响还挺好&#xff0c;看来大家在 JS 优化的问题上越来越注重“与国际接轨”了。一起来看本文的下篇&#xff0c;也是干货满满。 文章目录 6. Avoid large objectsWhat the eff should I do about this? 7. Use eval8. Use str…

多元形式助力商业价值最大化,王鹤棣商业影响力遥遥领先

明星商业代言层出不穷&#xff0c;但在个人影响力的升级玩法上&#xff0c;当代青年偶像王鹤棣以其独特的个人魅力和卓越的商业头脑&#xff0c;正逐步搭建起一个以个人形象为核心&#xff0c;与各大品牌相互成就的立体商业模型。通过一系列创新的商务合作模式&#xff0c;王鹤…

[Java并发编程] synchronized(含与ReentrantLock的区别)

文章目录 1. synchronized与ReentrantLock的区别2. synchronized的作用3. synchronized的使用3.1 修饰实例方法&#xff0c;作用于当前实例&#xff0c;进入同步代码前需要先获取实例的锁3.2 修饰静态方法&#xff0c;作用于类的Class对象&#xff0c;进入修饰的静态方法前需要…

React组件如何暴露自身的方法

一、研究背景 最近遇到一个如何暴露React组件自身方法的问题。在某些时候&#xff0c;我们需要调用某个组件内部的方法以实现某个功能&#xff0c;因此我们需要了解如何暴露组件内部API的方法。 二、实践过程 本文主要介绍React组件暴露子组件API的方法&#xff0c;以下是实…

2024年研赛-华为杯数模竞赛C题论文首发+论文讲解+代码分享

2024年华为杯-研赛分享资料&#xff08;论文分享部分代码&#xff09;&#xff08;已更新部分代码&#xff09;&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1HGIYjV3lqzUc_3H0vg5H8w 提取码&#xff1a;sxjm 题 目&#xff1a; _基于数据驱动下磁性元件的磁芯损耗建模…