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

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

  • 1、效果展示
  • 2、数据库设计
    • 2.1 商品表
    • 2.2 商品价格和规格中间表
    • 2.3 商品规格表
  • 3、后端代码
    • 3.1 model
    • 3.2 vo
    • 3.3 mapper、server、serverImp
    • 3.4 controller
  • 4、前端代码
    • 4.1 request.js
    • 4.2 index.js
    • 4.3 shop-info.vue
    • 4.4 ShopBottomButton.vue

1、效果展示


在这里插入图片描述

2、数据库设计

2.1 商品表


/*Navicat Premium Data TransferSource Server         : Test1Source Server Type    : MySQLSource Server Version : 80200Source Host           : localhost:3306Source Schema         : yunshangshequTarget Server Type    : MySQLTarget Server Version : 80200File Encoding         : 65001Date: 21/09/2024 12:59:26
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for shop
-- ----------------------------
DROP TABLE IF EXISTS `shop`;
CREATE TABLE `shop`  (`id` int NOT NULL AUTO_INCREMENT COMMENT '商品ID',`business_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '标签/自营?',`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品简介',`sale_number` int NULL DEFAULT NULL COMMENT '已售数量',`old_price` decimal(10, 2) NULL DEFAULT NULL COMMENT '旧价格',`new_price` decimal(10, 2) NULL DEFAULT NULL COMMENT '现售价',`in_price` decimal(10, 2) NULL DEFAULT NULL COMMENT '进货价',`inventory_num` int NULL DEFAULT NULL COMMENT '库存',`main_image` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品主图',`shop_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品分类',`shop_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '店铺名称',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',`is_deleted` tinyint NULL DEFAULT NULL COMMENT '逻辑删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of shop
-- ----------------------------
INSERT INTO `shop` VALUES (1, '自营', '新货【三只松鼠_量贩碧根果500g】特大健康坚果零食奶油味长寿果', 138, 39.90, 29.90, 19.90, 200, 'https://gw.alicdn.com/imgextra/i3/2218288872763/O1CN01rN6Cn91WHVIflhWLg_!!2218288872763.jpg', '1', '三只松鼠旗舰店', NULL, NULL, 0);
INSERT INTO `shop` VALUES (2, '', '新货【三只松鼠_量贩碧根果1500g】特大健康坚果零食奶油味长寿果', 28, 59.90, 49.90, 39.90, 2000, 'https://gw.alicdn.com/imgextra/i3/2218288872763/O1CN01rN6Cn91WHVIflhWLg_!!2218288872763.jpg', '2', '三只耗子', NULL, NULL, 0);SET FOREIGN_KEY_CHECKS = 1;

2.2 商品价格和规格中间表

/*Navicat Premium Data TransferSource Server         : Test1Source Server Type    : MySQLSource Server Version : 80200Source Host           : localhost:3306Source Schema         : yunshangshequTarget Server Type    : MySQLTarget Server Version : 80200File Encoding         : 65001Date: 21/09/2024 12:59:16
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for shop_specs
-- ----------------------------
DROP TABLE IF EXISTS `shop_specs`;
CREATE TABLE `shop_specs`  (`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',`goods_id` int NULL DEFAULT NULL COMMENT '规格对应的商品id',`price` decimal(10, 2) NULL DEFAULT NULL COMMENT '对应规格的价格',`sku1` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '对应的规格',`sku2` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '对应的规格',`sku3` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '对应的规格',`sku4` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '对应的规格',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',`is_deleted` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '逻辑删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of shop_specs
-- ----------------------------
INSERT INTO `shop_specs` VALUES (1, 1, 39.90, '1', '2', NULL, NULL, NULL, NULL, '0');
INSERT INTO `shop_specs` VALUES (2, 1, 29.90, '1', '4', NULL, NULL, NULL, NULL, '0');
INSERT INTO `shop_specs` VALUES (3, 1, 59.90, '3', '2', NULL, NULL, NULL, NULL, '0');
INSERT INTO `shop_specs` VALUES (4, 1, 49.00, '3', '4', NULL, NULL, NULL, NULL, '0');SET FOREIGN_KEY_CHECKS = 1;

2.3 商品规格表

/*Navicat Premium Data TransferSource Server         : Test1Source Server Type    : MySQLSource Server Version : 80200Source Host           : localhost:3306Source Schema         : yunshangshequTarget Server Type    : MySQLTarget Server Version : 80200File Encoding         : 65001Date: 21/09/2024 12:59:04
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for shop_sku
-- ----------------------------
DROP TABLE IF EXISTS `shop_sku`;
CREATE TABLE `shop_sku`  (`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',`attr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '属性名',`attr_value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '属性值',`create_time` datetime NULL DEFAULT NULL,`update_time` datetime NULL DEFAULT NULL,`is_deleted` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of shop_sku
-- ----------------------------
INSERT INTO `shop_sku` VALUES (1, '重量', '1kg', NULL, NULL, '0');
INSERT INTO `shop_sku` VALUES (2, '大小', '大果', NULL, NULL, '0');
INSERT INTO `shop_sku` VALUES (3, '重量', '2kg', NULL, NULL, '0');
INSERT INTO `shop_sku` VALUES (4, '大小', '中果', NULL, NULL, '0');SET FOREIGN_KEY_CHECKS = 1;

3、后端代码

3.1 model


BaseEntity 所有表字段的基本组成字段

package com.zhong.model.entity;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;import java.io.Serializable;
import java.util.Date;@Data
public class BaseEntity implements Serializable {@Schema(description = "主键")@TableId(value = "id", type = IdType.AUTO)private Long id;@Schema(description = "创建时间")@TableField(value = "create_time", fill = FieldFill.INSERT)@JsonIgnoreprivate Date createTime;@Schema(description = "更新时间")@TableField(value = "update_time", fill = FieldFill.UPDATE)@JsonIgnoreprivate Date updateTime;@Schema(description = "逻辑删除")@TableField("is_deleted")@JsonIgnoreprivate Byte isDeleted;}

shop 表

package com.zhong.model.entity.shop;import java.io.Serializable;
import java.math.BigDecimal;import com.zhong.model.entity.BaseEntity;
import lombok.Data;/*** * @TableName shop 商品信息*/
@Data
public class Shop extends BaseEntity {/*** 标签/自营?*/private String businessName;/*** 商品简介*/private String title;/*** 已售数量*/private Integer saleNumber;/*** 旧价格*/private BigDecimal oldPrice;/*** 现售价*/private BigDecimal newPrice;/*** 进货价*/private BigDecimal inPrice;/*** 库存*/private Integer inventoryNum;/*** 商品主图*/private String mainImage;/*** 商品分类*/private String shopType;/*** 店铺名称*/private String shopName;private static final long serialVersionUID = 1L;
}

shop_img_swiper 表 轮播图

package com.zhong.model.entity.shop;import java.io.Serializable;import com.zhong.model.entity.BaseEntity;
import lombok.Data;/*** * @TableName shop_img_swiper*/
@Data
public class ShopImgSwiper extends BaseEntity {/*** 图片地址*/private String url;/*** 商品id*/private Integer fatherId;private static final long serialVersionUID = 1L;
}

shop_img_info表 商品详情图片

package com.zhong.model.entity.shop;import java.io.Serializable;import com.zhong.model.entity.BaseEntity;
import lombok.Data;/*** * @TableName shop_img_info*/
@Data
public class ShopImgInfo extends BaseEntity {/*** 图片地址*/private String url;/*** 商品id*/private Integer fatherId;private static final long serialVersionUID = 1L;
}

shop_specs 表 商品规格与商品中间表

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.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;import com.zhong.model.entity.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;/*** * @TableName shop_specs*/
@TableName(value ="shop_specs")
@Data
public class ShopSpecs extends BaseEntity {@Schema(description = "主键")@TableId(value = "id", type = IdType.AUTO)private Long id;/*** 规格对应的商品id*/private Integer goodsId;/*** 对应规格的价格*/private BigDecimal price;/*** 对应的规格*/private String sku1;/*** 对应的规格*/private String sku2;/*** 对应的规格*/private String sku3;/*** 对应的规格*/private String sku4;@TableField(exist = false)private static final long serialVersionUID = 1L;
}

shop_sku 商品规格

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.io.Serializable;
import java.util.Date;import com.fasterxml.jackson.annotation.JsonIgnore;
import com.zhong.model.entity.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;/*** * @TableName shop_sku*/
@TableName(value ="shop_sku")
@Data
public class ShopSku extends BaseEntity {@Schema(description = "主键")@TableId(value = "id", type = IdType.AUTO)@JsonIgnoreprivate Long id;/*** 属性名*/private String attr;/*** 属性值*/private String attrValue;@TableField(exist = false)private static final long serialVersionUID = 1L;
}

3.2 vo


ShopVo

package com.zhong.vo.shop;import com.zhong.model.entity.shop.Shop;
import com.zhong.model.entity.shop.ShopSpecs;
import lombok.Data;import java.util.List;/*** @ClassName : ShopVo* @Description :* @Author : zhx* @Date: 2024-09-15 18:55*/
@Data
public class ShopVo extends Shop {private List<String> shopImgSwiper;private List<String> shopImgInfo;private List<ShopSpecs> shopSpecs;
}

ShopInfoVo

package com.zhong.vo.shop;import com.zhong.model.entity.shop.Shop;
import com.zhong.model.entity.shop.ShopSku;
import com.zhong.model.entity.shop.ShopSpecs;
import lombok.Data;import java.util.List;
import java.util.Map;
import java.util.Set;/*** @ClassName : ShopVo* @Description :* @Author : zhx* @Date: 2024-09-15 18:55*/
@Data
public class ShopInfoVo extends Shop {private List<String> shopImgSwiper;private List<String> shopImgInfo;// 所有规格信息 方便渲染private List<Map<String, Object>> skuGroup;// 规格信息对应价格private List<ShopSkuVo> shopSpecs;
}

ShopSkuVo

package com.zhong.vo.shop;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.zhong.model.entity.shop.ShopSku;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;import java.math.BigDecimal;
import java.util.List;/*** @ClassName : ShopSkuVo* @Description :* @Author : zhx* @Date: 2024-09-20 10:46*/
@Data
public class ShopSkuVo {@Schema(description = "主键")@TableId(value = "id", type = IdType.AUTO)private Long id;private BigDecimal price;private List<ShopSku> skus;
}

SkuGroupVo

package com.zhong.vo.shop;import lombok.Data;import java.util.List;/*** @ClassName : skuGroupVo* @Description :* @Author : zhx* @Date: 2024-09-20 13:49*/
@Data
public class SkuGroupVo {private String skuKey;private List<String> skuValue;
}

3.3 mapper、server、serverImp

依赖mybatis自动生成,生成后将文件剪贴到对应位置即可。model 也可以自动生成 只不过要稍加修改

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.4 controller


ShopController

package com.zhong.controller.shop;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhong.model.entity.shop.Shop;
import com.zhong.result.Result;
import com.zhong.service.ShopService;
import com.zhong.vo.shop.ShopInfoVo;
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 : ShopController* @Description :* @Author : zhx* @Date: 2024-09-15 18:23*/@RestController
@RequestMapping("/app/shop/")
@Tag(name = "商品信息")
public class ShopController {@Autowiredprivate ShopService service;@Operation(summary = "分页获取商品信息")@GetMapping("listItem")public Result<Page<Shop>> listItem(@RequestParam long current, @RequestParam long size) {LambdaQueryWrapper<Shop> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(Shop::getIsDeleted, "0");Page<Shop> shopPage = new Page<>(current, size);Page<Shop> page = service.page(shopPage, queryWrapper);return Result.ok(page);}@Operation(summary = "根据id获取商品详细信息")@GetMapping("getDetailById")public Result<ShopInfoVo> getDetailById(@RequestParam Long id) {ShopInfoVo shopInfoVo = service.getDetailById(id);return Result.ok(shopInfoVo);}
}

ShopService

package com.zhong.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.zhong.model.entity.shop.Shop;
import com.zhong.vo.shop.ShopInfoVo;
import com.zhong.vo.shop.ShopVo;/**
* @author zhong
* @description 针对表【shop】的数据库操作Service
* @createDate 2024-09-15 18:18:13
*/
public interface ShopService extends IService<Shop> {ShopInfoVo getDetailById(Long id);
}

ShopServiceImpl

package com.zhong.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhong.mapper.shop.ShopMapper;
import com.zhong.model.entity.shop.Shop;
import com.zhong.model.entity.shop.ShopSku;
import com.zhong.model.entity.shop.ShopSpecs;
import com.zhong.service.ShopService;
import com.zhong.service.ShopSkuService;
import com.zhong.vo.shop.ShopInfoVo;
import com.zhong.vo.shop.ShopSkuVo;
import com.zhong.vo.shop.ShopVo;
import com.zhong.vo.shop.SkuGroupVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.*;
import java.util.stream.Collectors;/*** @author zhong* @description 针对表【shop】的数据库操作Service实现* @createDate 2024-09-15 18:18:13*/
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop>implements ShopService {@Autowiredprivate ShopMapper shopMapper;@Autowiredprivate ShopSkuService service;@Overridepublic ShopInfoVo getDetailById(Long id) {// 返回的数据voShopInfoVo shopInfoVo = new ShopInfoVo();// 根据商品ID查询信息ShopVo detailById = shopMapper.getDetailById(id);// 获取查询到的商品信息的规格列表List<ShopSpecs> shopSpecs = detailById.getShopSpecs();// 新建一个vo对象保存 规格信息对应价格和列表List<ShopSkuVo> shopSkuLists = new ArrayList<>();
//        // 新建一个顶层数组 group vo 方便添加
//        List<List<SkuGroupVo>> skuGroupVos = new ArrayList<>();// 遍历每一个规格信息shopSpecs.forEach(x -> {
//            // 新建一个 二层数组 group vo 方便添加
//            List<SkuGroupVo> skuGroupList = new ArrayList<>();
//            // 新建一个 三层数组 group vo 方便添加
//            SkuGroupVo skuGroup = new SkuGroupVo();// 新建一个规格信息 Vo 方便后续添加到 listVoShopSkuVo shopSkuVo = new ShopSkuVo();// 新建一个规格详情信息 Vo 方便后续添加到 shopSkuVoList<ShopSku> skuArrayList = new ArrayList<>();// 获取 skuGroupVo 的 keyList<String> skuGroupKey = new ArrayList<>();// 获取 skuGroupVo 的 valueList<String> skuGroupValue = new ArrayList<>();// 判断是否含有第一种规格if (x.getSku1() != null) {// 新建一个 shopSku 方便添加到 List<ShopSku>ShopSku shopSku = new ShopSku();// 根据规格ID获取规格详情ShopSku sku = service.getById(x.getSku1());// 将规格ID详情添加到 shopSkuVoshopSkuVo.setId(x.getId());// 将规格价格详情添加到 shopSkuVoshopSkuVo.setPrice(x.getPrice());// 将规格标题添加到 shopSkushopSku.setAttr(sku.getAttr());skuGroupKey.add(sku.getAttr());skuGroupValue.add(sku.getAttrValue());// 将规格值添加到 shopSkushopSku.setAttrValue(sku.getAttrValue());// 添加到规格详情List  List<ShopSku>skuArrayList.add(shopSku);}// 判断是否含有第二种规格if (x.getSku2() != null) {// 新建一个 shopSku 方便添加到 List<ShopSku>ShopSku shopSku = new ShopSku();// 根据规格ID获取规格详情ShopSku sku = service.getById(x.getSku2());// 将规格ID详情添加到 shopSkuVoshopSkuVo.setId(x.getId());// 将规格价格详情添加到 shopSkuVoshopSkuVo.setPrice(x.getPrice());// 将规格标题添加到 shopSkushopSku.setAttr(sku.getAttr());skuGroupKey.add(sku.getAttr());skuGroupValue.add(sku.getAttrValue());// 将规格值添加到 shopSkushopSku.setAttrValue(sku.getAttrValue());// 添加到规格详情List  List<ShopSku>skuArrayList.add(shopSku);}// 判断是否含有第三种规格if (x.getSku3() != null) {// 新建一个 shopSku 方便添加到 List<ShopSku>ShopSku shopSku = new ShopSku();// 根据规格ID获取规格详情ShopSku sku = service.getById(x.getSku3());// 将规格ID详情添加到 shopSkuVoshopSkuVo.setId(x.getId());// 将规格价格详情添加到 shopSkuVoshopSkuVo.setPrice(x.getPrice());// 将规格标题添加到 shopSkushopSku.setAttr(sku.getAttr());skuGroupKey.add(sku.getAttr());skuGroupValue.add(sku.getAttrValue());// 将规格值添加到 shopSkushopSku.setAttrValue(sku.getAttrValue());// 添加到规格详情List  List<ShopSku>skuArrayList.add(shopSku);}// 判断是否含有第四种规格if (x.getSku4() != null) {// 新建一个 shopSku 方便添加到 List<ShopSku>ShopSku shopSku = new ShopSku();// 根据规格ID获取规格详情ShopSku sku = service.getById(x.getSku4());// 将规格ID详情添加到 shopSkuVoshopSkuVo.setId(x.getId());// 将规格价格详情添加到 shopSkuVoshopSkuVo.setPrice(x.getPrice());// 将规格标题添加到 shopSkushopSku.setAttr(sku.getAttr());skuGroupKey.add(sku.getAttr());skuGroupValue.add(sku.getAttrValue());// 将规格值添加到 shopSkushopSku.setAttrValue(sku.getAttrValue());// 添加到规格详情List  List<ShopSku>skuArrayList.add(shopSku);}shopSkuVo.setSkus(skuArrayList);shopSkuLists.add(shopSkuVo);});BeanUtils.copyProperties(detailById, shopInfoVo);shopInfoVo.setShopSpecs(shopSkuLists);List<Map<String, Object>> skuGroup = computeSkuGroup(shopSkuLists);shopInfoVo.setSkuGroup(skuGroup);return shopInfoVo;}public List<Map<String, Object>> computeSkuGroup(List<ShopSkuVo> shopSpecs) {// 用于存放规格结果Map<String, Set<String>> resultMap = new LinkedHashMap<>();// 遍历每个商品的skus,提取属性和属性值for (ShopSkuVo item : shopSpecs) {for (ShopSku sku : item.getSkus()) {String attr = sku.getAttr();String attrValue = sku.getAttrValue();// 如果resultMap中不存在该属性,则初始化一个空的SetresultMap.putIfAbsent(attr, new LinkedHashSet<>());// 将属性值加入对应的SetresultMap.get(attr).add(attrValue);}}// 将结果格式化为所需的输出格式List<Map<String, Object>> finalResult = new ArrayList<>();for (Map.Entry<String, Set<String>> entry : resultMap.entrySet()) {Map<String, Object> temp = new HashMap<>();temp.put("key", entry.getKey());// 将Set转换为List并排序List<String> sortedValues = new ArrayList<>(entry.getValue());Collections.sort(sortedValues); // 对属性值进行排序// 创建一个包含info和isCheck的结构,假设第一个元素被选中List<Map<String, Object>> attrValueItems = new ArrayList<>();for (int i = 0; i < sortedValues.size(); i++) {Map<String, Object> valueMap = new HashMap<>();valueMap.put("info", sortedValues.get(i));valueMap.put("isCheck", i == 0); // 默认第一个值为true,其他为falseattrValueItems.add(valueMap);}temp.put("value", attrValueItems);finalResult.add(temp);}// 输出结果System.out.println(finalResult);return finalResult;}
}

ShopMapper

package com.zhong.mapper.shop;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zhong.model.entity.shop.Shop;
import com.zhong.vo.shop.ShopVo;
import org.apache.ibatis.annotations.Mapper;/**
* @author zhong
* @description 针对表【shop】的数据库操作Mapper
* @createDate 2024-09-15 18:18:13
* @Entity generator.domain.Shop
*/
@Mapper
public interface ShopMapper extends BaseMapper<Shop> {ShopVo getDetailById(Long id);
}> ShopMapper.xml```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zhong.mapper.shop.ShopMapper"><resultMap id="ShopVoMap" type="com.zhong.vo.shop.ShopVo" autoMapping="true"><id column="id" property="id"/><collection property="shopImgSwiper" ofType="string"><result column="siu"/></collection><collection property="shopImgInfo" ofType="string" autoMapping="true"><result column="ssu"/></collection><collection property="shopSpecs" ofType="com.zhong.model.entity.shop.ShopSpecs" autoMapping="true"><id column="sssid" property="id"/></collection></resultMap><select id="getDetailById" resultMap="ShopVoMap">select sp.id,sp.business_name,sp.title,sp.sale_number,sp.old_price,sp.new_price,sp.in_price,sp.inventory_num,sp.main_image,sp.shop_type,sp.shop_name,si.url siu,ss.url ssu,sss.id sssid,sss.sku1,sss.sku2,sss.sku3,sss.sku4,sss.pricefrom shop spleft join shop_img_info si on sp.id = si.father_id and si.is_deleted = 0left join shop_img_swiper ss on sp.id = ss.father_id and ss.is_deleted = 0left join shop_specs sss on sp.id = sss.goods_id and sss.is_deleted = 0where sp.is_deleted = 0and sp.id = #{id}</select>
</mapper>

4、前端代码

4.1 request.js

import axios from 'axios'// 创建 axios 实例
const service = axios.create({// baseURL: import.meta.env.PROD ? import.meta.env.VITE_APP_BASE_URL : '/',  // 根据环境设置 baseURLbaseURL: '/api', // 根据环境设置 baseURL// timeout: ResultEnum.TIMEOUT,timeout: 10000,
})/*** @description: 请求拦截器*/
service.interceptors.request.use((config) => {// const userStore = useUserStore()// const token = userStore.token// if (token) {//   config.headers['access-token'] = token// }config.headers['access-token'] ="eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MzUwMzcyNjUsInN1YiI6IkxPR0lOX1VTRVIiLCJ1c2VySWQiOjgsInVzZXJuYW1lIjoiMTMwMzY0OTc1NjIifQ.BJudKwWdt48h4eQaYRte2aXZWDYFRG7SHopRYSMr7D4"return config},(error) => {// 使用 uni.showToast 替换 ElMessage.erroruni.showToast({title: error.message,icon: 'none',duration: 2000})return Promise.reject(error)}
)/*** @description: 响应拦截器*/
service.interceptors.response.use((response) => {const {data} = response// console.log(data.code);// // * 登陆失效// if (ResultEnum.EXPIRE.includes(data.code)) {// 	RESEETSTORE()// 	uni.showToast({// 		title: data.message || ResultEnum.ERRMESSAGE,// 		icon: 'none',// 		duration: 2000// 	})// 	router.replace(LOGIN_URL)// 	return Promise.reject(data)// }if (data.code && data.code !== 200) {uni.showToast({title: data.message || ResultEnum.ERRMESSAGE,icon: 'none',duration: 2000})return Promise.reject(data)}return data.data},(error) => {// 处理 HTTP 网络错误let message = ''const status = error.response?.statusswitch (status) {case 400:message = "请求错误";break;case 401:message = "未授权,请登录";break;case 403:message = "拒绝访问";break;case 404:message = `请求地址出错: ${error.response?.config?.url}`;break;case 408:message = "请求超时";break;case 500:message = "服务器内部错误";break;case 501:message = "服务未实现";break;case 502:message = "网关错误";break;case 503:message = "服务不可用";break;case 504:message = "网关超时";break;case 505:message = "HTTP版本不受支持";break;default:message = "网络连接故障";}// 使用 uni.showToast 提示错误信息uni.showToast({title: message,icon: 'none',duration: 2000})return Promise.reject(error)}
)/*** @description: 导出封装的请求方法*/
const http = {get(url, params) {return service.get(url, params)},post(url, data) {return service.post(url, data)},put(url, data) {return service.put(url, data)},delete(url, data) {return service.delete(url, {data})}
}export default http

4.2 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}`)
}

4.3 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>

4.4 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="show=true"></up-button><up-button text="立即购买" type="success"></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;"><up-text mode="price" :text="calculatedPrice" 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;"><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';// 创建响应式数据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,计算出价格const calculatedPrice = computed(() => {if (resSkuData.value) {// 找到与选中属性完全匹配的 SKUconst matchingSku = resSkuData.value.find(sku => {return sku.skus.every(skuAttr => {return checkedAttributes.value.some(attr => attr.attr === skuAttr.attr && attr.attrValue === skuAttr.attrValue);});});// 返回匹配的价格,没有匹配的返回 nullreturn matchingSku ? matchingSku.price * shopNum.value: null;}});// // 定义 emit// const emit = defineEmits(['updatePrice']);// // 监听计算的价格变化并向父组件传值// watch(calculatedPrice, (newPrice) => {// 	emit('updatePrice', newPrice);// });const toCartData = reactive({num: 1,})const props = defineProps({data: Object});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 = () => {close();}
</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>

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

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

相关文章

前端-javaScript:jquery补充

jquery绑定事件的方式 1.直接使用事件函数 &("div").click(function(){alert(1)}) 2.用统一的on函数绑定事件 on(事件类型&#xff0c;事件函数) $("div").on("click",function(){alert(2)}) 事件类型以参数的类型传递 --->可以同时绑…

go webapi上传文件 部属到linux

go厉害的地方&#xff0c;linux服务器上无需安装任务依赖就可以运行&#xff0c;大赞&#xff01; 一、编译 #在Goland中cmd中执行 go env -w GOARCHamd64 go env -w GOOSlinux go build main.go # 切换回来 否则无法运行 go env -w GOOSwindows go run main.go 拷贝到linux服…

C++——关联式容器(4):set和map

在接触了诸如二叉搜索树、AVL树、红黑树的树形结构之后&#xff0c;我们对树的结构有了大致的了解&#xff0c;现在引入真正的关联式容器。 首先&#xff0c;先明确了关联式容器的概念。我们之前所接触到的如vector、list等容器&#xff0c;我们知道他们实际上都是线性的数据结…

使用pe工具制作ubuntu备份系统和还原系统

使用pe工具制作ubuntu备份系统和还原系统 备份系统还原系统修复磁盘教程修复引导教程为什么使用pe工具 1,因为我个人觉得这个工具实现起来比systemback软件操作起来报错少些,而且装的快,其他系统同理 实验准备 1,一个电脑,一个pe启动U盘 备份系统 插入U盘,开机进入pe系…

[JavaEE] UDP协议

目录 再谈端口号 一、端口号的划分 二、UDP协议 三、UDP的特点 再谈端口号 一、端口号的划分 0-1023&#xff1a;知名端口号&#xff0c;端口号固定&#xff0c;其中包括HTTP&#xff0c;FTP&#xff0c;SSH等广为使用的应用层协议。 1024-65535&#xff1a;操作系统动态分…

数据结构|二叉搜索树

&#x1f36c; mooridy-CSDN博客 &#x1f36c;数据结构专栏&#xff08;更新中&#xff09; 目录 1. ⼆叉搜索树的概念 2. ⼆叉搜索树的性能分析 3.⼆叉搜索树key和key/value key搜索场景 key/value搜索场景 4. 二叉搜索树的代码实现 4.1 ⼆叉搜索树的插⼊ 4.2 ⼆叉搜索…

java----LinkedHashMap

.由键决定:有序、不重复、无索引 .这里的有序指的是保证存储和去除的元素顺序一致 原理:底层数据结构依然是哈希表,只是每个键值对元素又额外多了一个双链表的机制记录存储的顺序。 内容来自:集合进阶-09-LinkedHashMap_哔哩哔哩_bilibili

ChatGPT 在国内使用的方法

AI如今很强大&#xff0c;聊聊天、写论文、搞翻译、写代码、写文案、审合同等等&#xff0c;ChatGPT 真是无所不能~ 作为一款出色的大语言模型&#xff0c;ChatGPT 实现了人类般的对话交流&#xff0c;最主要是能根据上下文进行互动。 接下来&#xff0c;我将介绍 ChatGPT 在国…

hackmyvm靶场--zon

环境 攻击机kali 靶机 未知 主机探测 因为在同一个局域网内使用ARP协议探测存活主机 靶机为192.168.56.128 端口探测 常见的80和22端口 那么一定是寻找web漏洞拿shell了 后台扫描 后台扫描常用dirsearch和gobuster,有时候小字典可能不太行&#xff0c;可以尝试换个大点…

JAVA——数据流、序列化流

目录 一、DataOutputStream(数据输出流&#xff09; 二、DataInputStream(数据输入流&#xff09; 三、序列化流 1.1 ObjectOutputStream(对象字节输出流&#xff09; 1.2 OutputStream(对象字节输入流&#xff09; 四、补充 一、DataOutputStream(数据输出流&#xff09; …

Flutter 获取手机连接的Wifi信息

测试版本 Flutter&#xff1a;3.7.6Dart:2.19.3 network_info_plus: 4.0.1 前言 我在做设备配网的时候&#xff0c;需要选择配网的WiFi。用下拉选择框展示WiFi列表。现在有个需求&#xff1a;默认展示的设备为手机连接的wifi。要实现这个需求只要能够获取到手机连接的wifi信息…

直接插入排序(C语言实现)

目录 1.直接插入排序介绍 2.实现思路 3.动图展示 4.代码实现 &#xff08;升序&#xff09; 单趟排序实现 单趟排序代码 直接插入排序函数 5.代码测试 6.时空复杂度分析 时间复杂度O(N^2) 空间复杂度O(1) 1.直接插入排序介绍 插入排序&#xff0c;又叫直接插入排序。…

(十七)MATLAB读取Gazebo话题信息

在仿真实验过程中&#xff0c;我们有时需要实时读取ROS及Gazebo话题&#xff0c;目前互联网上关于读取ROS的话题资料较多&#xff0c;读取Gazebo话题的参考资料较少&#xff0c;本文将以Ubuntu下固定翼仿真为例&#xff0c;展示如果通过MATLAB的插件GazeboPlugin读取Gazebo话题…

MoFA: 迈向AIOS

再一次向朋友们致以中秋的祝福&#xff01; MoFA (Modular Framework for Agents)是一个独特的模块化AI智能体框架。MoFA以组合&#xff08;Composition)的逻辑和编程&#xff08;Programmable&#xff09;的方法构建AI智能体。开发者通过模版的继承、编程、定制智能体&#xf…

C++:多态(协变,override,final,纯虚函数抽象类,原理)

目录 编译时多态 函数重载 模板 运行时多态 多态的实现 实现多态的条件 协变 析构函数的重写 override 关键字 final 关键字 重载、重写、隐藏对比 纯虚函数和抽象类 多态的原理 多态是什么&#xff1f; 多态就是有多种形态 多态有两种&#xff0c;分别是编译时…

【前端学习】作用域实际问题学习记录

在复习apply&#xff0c;call&#xff0c;bind的时候遇到了一个作用域问题。 let name noName let age 18function getMyname() {console.log(my name is this.name, and I am this.age years old); }getMyname() }在全局使用let定义变量name和age之后&#xff0c;运行g…

★ C++进阶篇 ★ 二叉搜索树

Ciallo&#xff5e;(∠・ω< )⌒☆ ~ 今天&#xff0c;我将继续和大家一起学习C进阶篇第三章----二叉搜索树 ~ ❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️ 澄岚主页&#xff1a;椎名澄嵐-CSDN博客 C基础篇专栏&#xff1a;★ C基础篇 ★_椎名澄嵐的博客-CSD…

从零开始学习TinyWebServer

写在前面 项目参考&#xff1a;https://github.com/qinguoyi/TinyWebServer 写作框架/图参考&#xff1a;https://blog.csdn.net/qq_52313711/article/details/136356042?spm1001.2014.3001.5502 原本计划是&#xff0c;先将项目代码大概看一遍&#xff0c;然后再着手实现一下…

SOCKS5代理为何比HTTP代理更快?

在代理类型的选择上&#xff0c;SOCKS5代理经常被认为比HTTP代理更快&#xff0c;这是因为它们在工作原理和功能实现上存在较大的差异。让我们来探讨一下&#xff0c;为什么SOCKS5代理的速度通常比HTTP代理要快。 1. 协议的差异 SOCKS5代理&#xff1a;它是一个通用的代理协议…

【yolo破损纸板-包装盒-快递袋缺陷检测】

yolo破损纸板-包装盒-快递袋缺陷检测 破损纸质包装盒检测方盒型快递包裹检测 破损纸质包装盒检测 数据集合模型 可视化 方盒型快递包裹检测 数据集和模型 train: ../train/images val: ../valid/images test: ../test/images nc: 1 names: - box_packet可视化