Commit 1976571ae8cf91f377c06a09edce0cf2dab5dd92

Authored by YunaiV
2 parents 2866b587 af6a0224

Merge remote-tracking branch 'origin/feature/1.8.0-uniapp' into feature/1.8.0-uniapp

# Conflicts:
#	yudao-ui-admin/src/views/mall/product/spu/index.vue
Showing 23 changed files with 1386 additions and 647 deletions
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyViewRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.admin.property.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.ToString;
  7 +
  8 +import java.util.Set;
  9 +
  10 +/**
  11 + * @Description: ProductPropertyViewRespVO
  12 + * @Author: franky
  13 + * @CreateDate: 2022/7/5 21:29
  14 + * @Version: 1.0.0
  15 + */
  16 +@ApiModel("管理后台 - 规格名称详情展示 Request VO")
  17 +@Data
  18 +@ToString(callSuper = true)
  19 +public class ProductPropertyViewRespVO {
  20 +
  21 + @ApiModelProperty(value = "规格名称id", example = "1")
  22 + public Long propertyId;
  23 +
  24 + @ApiModelProperty(value = "规格名称", example = "内存")
  25 + public String name;
  26 +
  27 + @ApiModelProperty(value = "规格属性值集合", example = "[{\"v1\":11,\"v2\":\"64G\"},{\"v1\":10,\"v2\":\"32G\"}]")
  28 + public Set<Tuple2> propertyValues;
  29 +
  30 + @Data
  31 + @ApiModel(value = "规格属性值元组")
  32 + public static class Tuple2 {
  33 + private final long v1;
  34 + private final String v2;
  35 + public Tuple2(Long v1, String v2) {
  36 + this.v1 = v1;
  37 + this.v2 = v2;
  38 + }
  39 +
  40 + }
  41 +
  42 +
  43 +}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuRespVO.java
1 1 package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
2 2  
  3 +import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO;
3 4 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO;
4 5 import io.swagger.annotations.ApiModel;
5 6 import io.swagger.annotations.ApiModelProperty;
... ... @@ -19,7 +20,7 @@ public class SpuRespVO extends ProductSpuBaseVO {
19 20  
20 21 // TODO @franky:注解要完整
21 22  
22   - @ApiModelProperty(value = "主键", required = true)
  23 + @ApiModelProperty(value = "主键", required = true, example = "1")
23 24 private Long id;
24 25  
25 26 @ApiModelProperty(value = "创建时间")
... ... @@ -28,9 +29,13 @@ public class SpuRespVO extends ProductSpuBaseVO {
28 29 /**
29 30 * SKU 数组
30 31 */
31   - List<ProductSkuRespVO> skus;
  32 + @ApiModelProperty(value = "sku 数组", example = "[{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":15},{\"propertyId\":2,\"valueId\":10}],\"price\":12,\"originalPrice\":32,\"costPrice\":22,\"barCode\":\"765670123123\",\"picUrl\":\"http://test.yudao.iocoder.cn/72938088f1ca8438837c3b51394aea43.jpg\",\"status\":0,\"id\":7,\"createTime\":1656683270000},{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":15},{\"propertyId\":2,\"valueId\":11}],\"price\":13,\"originalPrice\":33,\"costPrice\":23,\"barCode\":\"888788770999\",\"picUrl\":\"http://test.yudao.iocoder.cn/6b902c700e5d32e862b6fd9af2e1c0e4.jpg\",\"status\":0,\"id\":8,\"createTime\":1656683270000},{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":16},{\"propertyId\":2,\"valueId\":10}],\"price\":14,\"originalPrice\":34,\"costPrice\":24,\"barCode\":\"9999981212\",\"picUrl\":\"http://test.yudao.iocoder.cn/eddf3c79b1917160d94d05244e1f47da.jpg\",\"status\":0,\"id\":9,\"createTime\":1656683270000},{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":16},{\"propertyId\":2,\"valueId\":11}],\"price\":15,\"originalPrice\":35,\"costPrice\":25,\"barCode\":\"4444121212\",\"picUrl\":\"http://test.yudao.iocoder.cn/88ac3eb068ea9cfac4726879b2776ccf.jpg\",\"status\":0,\"id\":10,\"createTime\":1656683270000}]")
  33 + private List<ProductSkuRespVO> skus;
32 34  
33 35 @ApiModelProperty(value = "分类id数组,一直递归到一级父节点", example = "[1,2,4]")
34   - LinkedList<Long> categoryIds;
  36 + private LinkedList<Long> categoryIds;
  37 +
  38 + @ApiModelProperty(value = "规格属性修改和详情展示组合", example = "[{\"propertyId\":2,\"name\":\"内存\",\"propertyValues\":[{\"v1\":11,\"v2\":\"64G\"},{\"v1\":10,\"v2\":\"32G\"}]},{\"propertyId\":3,\"name\":\"尺寸\",\"propertyValues\":[{\"v1\":16,\"v2\":\"6.1\"},{\"v1\":15,\"v2\":\"5.7\"}]}]")
  39 + private List<ProductPropertyViewRespVO> productPropertyViews;
35 40  
36 41 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/AppCategoryController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.category;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.product.controller.app.category.vo.AppCategoryListRespVO;
  5 +import cn.iocoder.yudao.module.product.convert.category.CategoryConvert;
  6 +import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
  7 +import cn.iocoder.yudao.module.product.service.category.CategoryService;
  8 +import io.swagger.annotations.Api;
  9 +import io.swagger.annotations.ApiOperation;
  10 +import org.springframework.validation.annotation.Validated;
  11 +import org.springframework.web.bind.annotation.GetMapping;
  12 +import org.springframework.web.bind.annotation.RequestMapping;
  13 +import org.springframework.web.bind.annotation.RestController;
  14 +
  15 +import javax.annotation.Resource;
  16 +import java.util.Comparator;
  17 +import java.util.List;
  18 +
  19 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  20 +
  21 +@Api(tags = "用户 APP - 商品分类")
  22 +@RestController
  23 +@RequestMapping("/product/category")
  24 +@Validated
  25 +public class AppCategoryController {
  26 +
  27 + @Resource
  28 + private CategoryService categoryService;
  29 +
  30 + @GetMapping("/list")
  31 + @ApiOperation("获得商品分类列表")
  32 + public CommonResult<List<AppCategoryListRespVO>> listByQuery() {
  33 + List<CategoryDO> list = categoryService.getCategoryList();
  34 + list.sort(Comparator.comparing(CategoryDO::getSort));
  35 + return success(CategoryConvert.INSTANCE.convertList03(list));
  36 + }
  37 +
  38 +
  39 +
  40 +}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/vo/AppCategoryListRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.category.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotBlank;
  8 +import javax.validation.constraints.NotNull;
  9 +
  10 +@Data
  11 +@ApiModel(value = "App - 商品分类 列表 Request VO", description = "参数和 CategoryBaseVO 是一致的")
  12 +public class AppCategoryListRespVO {
  13 +
  14 + @ApiModelProperty(value = "分类编号", required = true, example = "2")
  15 + private Long id;
  16 +
  17 + @ApiModelProperty(value = "父分类编号", required = true, example = "1")
  18 + @NotNull(message = "父分类编号不能为空")
  19 + private Long parentId;
  20 +
  21 + @ApiModelProperty(value = "分类名称", required = true, example = "办公文具")
  22 + @NotBlank(message = "分类名称不能为空")
  23 + private String name;
  24 +
  25 + @ApiModelProperty(value = "分类图标")
  26 + @NotBlank(message = "分类图标不能为空")
  27 + private String icon;
  28 +
  29 + @ApiModelProperty(value = "分类图片", required = true)
  30 + @NotBlank(message = "分类图片不能为空")
  31 + private String bannerUrl;
  32 +
  33 + @ApiModelProperty(value = "分类排序", required = true, example = "1")
  34 + private Integer sort;
  35 +
  36 + @ApiModelProperty(value = "分类描述", required = true, example = "描述")
  37 + private String description;
  38 +}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.spu;
  2 +
  3 +import cn.hutool.core.bean.BeanUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.SpuRespVO;
  7 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
  8 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
  9 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuRespVO;
  10 +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
  11 +import io.swagger.annotations.Api;
  12 +import io.swagger.annotations.ApiOperation;
  13 +import org.springframework.validation.annotation.Validated;
  14 +import org.springframework.web.bind.annotation.GetMapping;
  15 +import org.springframework.web.bind.annotation.RequestMapping;
  16 +import org.springframework.web.bind.annotation.RequestParam;
  17 +import org.springframework.web.bind.annotation.RestController;
  18 +
  19 +import javax.annotation.Resource;
  20 +import javax.validation.Valid;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  23 +
  24 +@Api(tags = "用户 APP - 商品spu")
  25 +@RestController
  26 +@RequestMapping("/product/spu")
  27 +@Validated
  28 +public class AppProductSpuController {
  29 +
  30 + @Resource
  31 + private ProductSpuService spuService;
  32 +
  33 + @GetMapping("/page")
  34 + @ApiOperation("获得商品spu分页")
  35 + public CommonResult<PageResult<AppSpuPageRespVO>> getSpuPage(@Valid AppSpuPageReqVO pageVO) {
  36 + return success(spuService.getSpuPage(pageVO));
  37 + }
  38 +
  39 + @GetMapping("/")
  40 + @ApiOperation("获取商品 - 通过商品id")
  41 + public CommonResult<AppSpuRespVO> getSpu(@RequestParam("spuId") Long spuId) {
  42 + AppSpuRespVO appSpuRespVO = BeanUtil.toBean(spuService.getSpu(spuId), AppSpuRespVO.class);
  43 + return success(appSpuRespVO);
  44 + }
  45 +}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.spu.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +
  10 +@ApiModel("App - 商品spu分页 Request VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +@ToString(callSuper = true)
  14 +public class AppSpuPageReqVO extends PageParam {
  15 +
  16 + @ApiModelProperty(value = "分类id")
  17 + private Long categoryId;
  18 +}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.spu.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotNull;
  8 +import java.util.List;
  9 +
  10 +@ApiModel("App - 商品spu分页 Request VO")
  11 +@Data
  12 +public class AppSpuPageRespVO {
  13 +
  14 + @ApiModelProperty(value = "主键", required = true, example = "1")
  15 + private Long id;
  16 +
  17 + @ApiModelProperty(value = "商品名称")
  18 + private String name;
  19 +
  20 + @ApiModelProperty(value = "卖点", required = true)
  21 + @NotNull(message = "卖点不能为空")
  22 + private String sellPoint;
  23 +
  24 + @ApiModelProperty(value = "描述", required = true)
  25 + @NotNull(message = "描述不能为空")
  26 + private String description;
  27 +
  28 + @ApiModelProperty(value = "分类id", required = true)
  29 + @NotNull(message = "分类id不能为空")
  30 + private Long categoryId;
  31 +
  32 + @ApiModelProperty(value = "商品主图地址,* 数组,以逗号分隔,最多上传15张", required = true)
  33 + @NotNull(message = "商品主图地址,* 数组,以逗号分隔,最多上传15张不能为空")
  34 + private List<String> picUrls;
  35 +
  36 + @ApiModelProperty(value = "排序字段", required = true)
  37 + @NotNull(message = "排序字段不能为空")
  38 + private Integer sort;
  39 +
  40 + @ApiModelProperty(value = "点赞初始人数")
  41 + private Integer likeCount;
  42 +
  43 + @ApiModelProperty(value = "价格 单位使用:分")
  44 + private Integer price;
  45 +
  46 + @ApiModelProperty(value = "库存数量")
  47 + private Integer quantity;
  48 +
  49 + @ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)")
  50 + private Boolean status;
  51 +
  52 +}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.spu.vo;
  2 +
  3 +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.SpuRespVO;
  4 +import io.swagger.annotations.ApiModel;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +/**
  10 + * <p>
  11 + *
  12 + * </p>
  13 + *
  14 + * @author LuoWenFeng
  15 + */
  16 +@ApiModel("App - 商品spu Response VO")
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +public class AppSpuRespVO extends SpuRespVO {
  20 +
  21 +
  22 +}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/CategoryConvert.java
... ... @@ -4,6 +4,7 @@ import java.util.*;
4 4  
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6  
  7 +import cn.iocoder.yudao.module.product.controller.app.category.vo.AppCategoryListRespVO;
7 8 import org.mapstruct.Mapper;
8 9 import org.mapstruct.factory.Mappers;
9 10 import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;
... ... @@ -31,4 +32,5 @@ public interface CategoryConvert {
31 32  
32 33 List<CategoryExcelVO> convertList02(List<CategoryDO> list);
33 34  
  35 + List<AppCategoryListRespVO> convertList03(List<CategoryDO> list);
34 36 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java
... ... @@ -4,6 +4,8 @@ import java.util.*;
4 4  
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6  
  7 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
  8 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
7 9 import org.mapstruct.Mapper;
8 10 import org.mapstruct.Mapping;
9 11 import org.mapstruct.Named;
... ... @@ -40,6 +42,11 @@ public interface ProductSpuConvert {
40 42  
41 43 List<SpuExcelVO> convertList02(List<ProductSpuDO> list);
42 44  
  45 + SpuPageReqVO convert(AppSpuPageReqVO bean);
  46 +
  47 + @Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "tokenizeToStringArray")
  48 + AppSpuPageRespVO convertAppResp(ProductSpuDO list);
  49 +
43 50 @Named("tokenizeToStringArray")
44 51 default List<String> translatePicUrlsArrayFromString(String picUrls) {
45 52 return Arrays.asList(StringUtils.tokenizeToStringArray(picUrls, ","));
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryService.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.product.service.category;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;
  5 +import cn.iocoder.yudao.module.product.controller.app.category.vo.AppCategoryListRespVO;
5 6 import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
6 7  
7 8 import javax.validation.Valid;
... ... @@ -83,4 +84,11 @@ public interface CategoryService {
83 84 * @param categoryId 分类id
84 85 */
85 86 void validatedCategoryById(Long categoryId);
  87 +
  88 + /**
  89 + * app端获得商品分类列表
  90 + *
  91 + * @return 商品分类列表
  92 + */
  93 + List<CategoryDO> getCategoryList();
86 94 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImpl.java
1 1 package cn.iocoder.yudao.module.product.service.category;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4 5 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
5 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
... ... @@ -13,6 +14,7 @@ import org.springframework.validation.annotation.Validated;
13 14 import javax.annotation.Resource;
14 15 import java.util.Collection;
15 16 import java.util.List;
  17 +import java.util.stream.Collectors;
16 18  
17 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
18 20 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
... ... @@ -106,4 +108,13 @@ public class CategoryServiceImpl implements CategoryService {
106 108 return categoryMapper.selectList(treeListReqVO);
107 109 }
108 110  
  111 + @Override
  112 + public List<CategoryDO> getCategoryList() {
  113 + return categoryMapper.selectList()
  114 + .stream()
  115 + .filter(v->v.getStatus().equals(CommonStatusEnum.ENABLE.getStatus()))
  116 + .collect(Collectors.toList());
  117 + }
  118 +
  119 +
109 120 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java
... ... @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.product.service.spu;
3 3 import java.util.*;
4 4 import javax.validation.*;
5 5 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
  6 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
  7 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
6 8 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
7 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 10  
... ... @@ -67,4 +69,13 @@ public interface ProductSpuService {
67 69 */
68 70 List<ProductSpuDO> getSpuList(SpuExportReqVO exportReqVO);
69 71  
  72 + /**
  73 + * 获得商品spu分页
  74 + *
  75 + * @param pageReqVO 分页查询
  76 + * @return 商品spu分页
  77 + */
  78 + PageResult<AppSpuPageRespVO> getSpuPage(AppSpuPageReqVO pageReqVO);
  79 +
  80 +
70 81 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
1 1 package cn.iocoder.yudao.module.product.service.spu;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO;
  5 +import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO;
  6 +import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO;
  7 +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
4 8 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO;
5 9 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO;
6 10 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
  11 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
  12 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
7 13 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
8 14 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
9   -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
10 15 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
11 16 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
12 17 import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
13 18 import cn.iocoder.yudao.module.product.service.category.CategoryService;
  19 +import cn.iocoder.yudao.module.product.service.property.ProductPropertyService;
14 20 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
15 21 import org.springframework.stereotype.Service;
16 22 import org.springframework.transaction.annotation.Transactional;
... ... @@ -41,6 +47,9 @@ public class ProductSpuServiceImpl implements ProductSpuService {
41 47 @Resource
42 48 private ProductSkuService productSkuService;
43 49  
  50 + @Resource
  51 + private ProductPropertyService productPropertyService;
  52 +
44 53 @Override
45 54 @Transactional
46 55 public Long createSpu(ProductSpuCreateReqVO createReqVO) {
... ... @@ -104,6 +113,29 @@ public class ProductSpuServiceImpl implements ProductSpuService {
104 113 if (null != spuVO) {
105 114 List<ProductSkuRespVO> skuReqs = ProductSkuConvert.INSTANCE.convertList(productSkuService.getSkusBySpuId(id));
106 115 spuVO.setSkus(skuReqs);
  116 + List<ProductSkuRespVO.Property> properties = new ArrayList<>();
  117 + // 组合 sku 规格属性
  118 + for (ProductSkuRespVO productSkuRespVO : skuReqs) {
  119 + properties.addAll(productSkuRespVO.getProperties());
  120 + }
  121 + Map<Long, List<ProductSkuBaseVO.Property>> propertyMaps = properties.stream().collect(Collectors.groupingBy(ProductSkuBaseVO.Property::getPropertyId));
  122 + List<ProductPropertyRespVO> propertyAndValueList = productPropertyService.selectByIds(new ArrayList<>(propertyMaps.keySet()));
  123 + // 装载组装过后的属性
  124 + List<ProductPropertyViewRespVO> productPropertyViews = new ArrayList<>();
  125 + propertyAndValueList.forEach(p -> {
  126 + ProductPropertyViewRespVO productPropertyViewRespVO = new ProductPropertyViewRespVO();
  127 + productPropertyViewRespVO.setPropertyId(p.getId());
  128 + productPropertyViewRespVO.setName(p.getName());
  129 + Set<ProductPropertyViewRespVO.Tuple2> propertyValues = new HashSet<>();
  130 + Map<Long, ProductPropertyValueRespVO> propertyValueMaps = p.getPropertyValueList().stream().collect(Collectors.toMap(ProductPropertyValueRespVO::getId, pv -> pv));
  131 + propertyMaps.get(p.getId()).forEach(pv -> {
  132 + ProductPropertyViewRespVO.Tuple2 tuple2 = new ProductPropertyViewRespVO.Tuple2(pv.getValueId(), propertyValueMaps.get(pv.getValueId()).getName());
  133 + propertyValues.add(tuple2);
  134 + });
  135 + productPropertyViewRespVO.setPropertyValues(propertyValues);
  136 + productPropertyViews.add(productPropertyViewRespVO);
  137 + });
  138 + spuVO.setProductPropertyViews(productPropertyViews);
107 139 // 组合分类
108 140 if (null != spuVO.getCategoryId()) {
109 141 LinkedList<Long> categoryArray = new LinkedList<>();
... ... @@ -144,4 +176,17 @@ public class ProductSpuServiceImpl implements ProductSpuService {
144 176 return ProductSpuMapper.selectList(exportReqVO);
145 177 }
146 178  
  179 + @Override
  180 + public PageResult<AppSpuPageRespVO> getSpuPage(AppSpuPageReqVO pageReqVO) {
  181 + PageResult<ProductSpuDO> productSpuDOPageResult = ProductSpuMapper.selectPage(ProductSpuConvert.INSTANCE.convert(pageReqVO));
  182 + PageResult<AppSpuPageRespVO> pageResult = new PageResult<>();
  183 + List<AppSpuPageRespVO> collect = productSpuDOPageResult.getList()
  184 + .stream()
  185 + .map(ProductSpuConvert.INSTANCE::convertAppResp)
  186 + .collect(Collectors.toList());
  187 + pageResult.setList(collect);
  188 + pageResult.setTotal(productSpuDOPageResult.getTotal());
  189 + return pageResult;
  190 + }
  191 +
147 192 }
... ...
yudao-ui-admin/src/views/mall/product/spu/index.vue
... ... @@ -25,9 +25,9 @@
25 25 <el-option label="下架" value="1"/>
26 26 </el-select>
27 27 </el-form-item>
28   - <el-form-item label="创建时间" prop="createTime">
29   - <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
30   - range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  28 + <el-form-item label="创建时间">
  29 + <el-date-picker v-model="dateRangeCreateTime" style="width: 240px" value-format="yyyy-MM-dd"
  30 + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"/>
31 31 </el-form-item>
32 32 <el-form-item>
33 33 <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
... ... @@ -55,8 +55,8 @@
55 55 <el-table v-loading="loading" :data="list">
56 56 <el-table-column label="主键" align="center" prop="id"/>
57 57 <el-table-column label="商品名称" align="center" prop="name"/>
58   -<!-- <el-table-column label="卖点" align="center" prop="sellPoint"/>-->
59   -<!-- <el-table-column label="描述" align="center" prop="description"/>-->
  58 + <!-- <el-table-column label="卖点" align="center" prop="sellPoint"/>-->
  59 + <!-- <el-table-column label="描述" align="center" prop="description"/>-->
60 60 <el-table-column label="分类id" align="center" prop="categoryId"/>
61 61 <el-table-column label="商品主图地址" align="center" prop="picUrls">
62 62 <template slot-scope="scope">
... ... @@ -67,7 +67,7 @@
67 67 <el-table-column label="点赞初始人数" align="center" prop="likeCount"/>
68 68 <el-table-column label="价格 (分)" align="center" prop="price"/>
69 69 <el-table-column label="库存数量" align="center" prop="quantity"/>
70   -<!-- <el-table-column label="状态" align="center" prop="status"/>-->
  70 + <!-- <el-table-column label="状态" align="center" prop="status"/>-->
71 71 <el-table-column label="创建时间" align="center" prop="createTime" width="180">
72 72 <template slot-scope="scope">
73 73 <span>{{ parseTime(scope.row.createTime) }}</span>
... ... @@ -102,12 +102,12 @@
102 102 </el-form-item>
103 103 <el-form-item label="分类id" prop="categoryIds">
104 104 <el-cascader
105   - v-model="form.categoryIds"
106   - placeholder="请输入分类id"
107   - style="width: 100%"
108   - :options="categoryList"
109   - :props="propName"
110   - clearable></el-cascader>
  105 + v-model="form.categoryIds"
  106 + placeholder="请输入分类id"
  107 + style="width: 100%"
  108 + :options="categoryList"
  109 + :props="propName"
  110 + clearable></el-cascader>
111 111 </el-form-item>
112 112 <el-form-item label="商品主图地址" prop="picUrls">
113 113 <ImageUpload v-model="form.picUrls" :limit="10"/>
... ... @@ -115,17 +115,17 @@
115 115 <el-form-item label="商品规格">
116 116 <el-button size="mini" @click="shopTagInput()">添加规格</el-button>
117 117 <div v-for="(tag, tagIndex) in skuTags" :key="tagIndex">
118   - <span>{{tag.name}}</span>
  118 + <span>{{ tag.name }}</span>
119 119 <el-button style="margin-left: 10px" class="button-new-tag" type="text" icon="el-icon-delete"
120 120 @click="removeTag(tagIndex)">删除
121 121 </el-button>
122 122 <br/>
123 123 <el-tag
124   - v-for="(tagItem, tagItemIndex) in tag.selectValues"
125   - :key="tagItem"
126   - style="margin-right: 10px"
127   - :disable-transitions="false">
128   - {{tagItem}}
  124 + v-for="(tagItem, tagItemIndex) in tag.selectValues"
  125 + :key="tagItem"
  126 + style="margin-right: 10px"
  127 + :disable-transitions="false">
  128 + {{ tagItem }}
129 129 </el-tag>
130 130 <!-- <el-input-->
131 131 <!-- class="input-new-tag"-->
... ... @@ -143,10 +143,10 @@
143 143 <el-select v-model="addTagInput.name" filterable allow-create default-first-option placeholder="请选择"
144 144 @change="handleTagClick">
145 145 <el-option
146   - v-for="item in unUseTags"
147   - :key="item.id"
148   - :label="item.name"
149   - :value="item.name">
  146 + v-for="item in unUseTags"
  147 + :key="item.id"
  148 + :label="item.name"
  149 + :value="item.name">
150 150 </el-option>
151 151 </el-select>
152 152 </el-col>
... ... @@ -156,10 +156,10 @@
156 156 <el-select v-model="addTagInput.selectValues" multiple filterable allow-create default-first-option
157 157 placeholder="请选择">
158 158 <el-option
159   - v-for="item in dbTagValues"
160   - :key="item.id"
161   - :label="item.name"
162   - :value="item.name">
  159 + v-for="item in dbTagValues"
  160 + :key="item.id"
  161 + :label="item.name"
  162 + :value="item.name">
163 163 </el-option>
164 164 </el-select>
165 165 </el-col>
... ... @@ -170,13 +170,13 @@
170 170 </el-form-item>
171 171 <el-form-item v-if="form.skus.length>0">
172 172 <el-table
173   - :data="form.skus"
174   - border
175   - style="width: 100%; margin-top: 20px"
176   - :span-method="tableSpanMethod">
  173 + :data="form.skus"
  174 + border
  175 + style="width: 100%; margin-top: 20px"
  176 + :span-method="tableSpanMethod">
177 177 <el-table-column v-for="(leftTitle, index) in skuTags" :key="index" :label="leftTitle.name">
178 178 <template slot-scope="scope">
179   - {{scope.row.propertyChildNames[index]}}
  179 + {{ scope.row.propertyChildNames[index] }}
180 180 </template>
181 181 </el-table-column>
182 182 <el-table-column v-if="skuTags.length"
... ... @@ -189,62 +189,63 @@
189 189 </template>
190 190 </el-table-column>
191 191 <el-table-column
192   - prop="prodName"
193   - label="条形码"
194   - width="250" v-if="skuTags.length">
  192 + prop="prodName"
  193 + label="条形码"
  194 + width="250" v-if="skuTags.length">
195 195 <template slot-scope="scope">
196   - <el-input v-model="scope.row.barCode" type="textarea" :disabled="scope.row.status===1"></el-input>
  196 + <el-input v-model="scope.row.barCode" type="textarea" :disabled="scope.row.status==1"></el-input>
197 197 </template>
198 198 </el-table-column>
199 199 <el-table-column
200   - prop="price"
201   - label="销售价">
  200 + prop="price"
  201 + label="销售价">
202 202 <template slot-scope="scope">
203 203 <el-input-number
204   - size="small"
205   - v-model="scope.row.price"
206   - controls-position="right"
207   - :precision="2"
208   - :max="1000000000"
209   - :min="0.01"
210   - :disabled="scope.row.status===1">
  204 + size="small"
  205 + v-model="scope.row.price"
  206 + controls-position="right"
  207 + :precision="2"
  208 + :max="1000000000"
  209 + :min="0.01"
  210 + :disabled="scope.row.status==1">
211 211 </el-input-number>
212 212 </template>
213 213 </el-table-column>
214 214 <el-table-column
215   - prop="oriPrice"
216   - label="成本价">
  215 + prop="oriPrice"
  216 + label="成本价">
217 217 <template slot-scope="scope">
218 218 <el-input-number
219   - size="small"
220   - v-model="scope.row.costPrice"
221   - controls-position="right"
222   - :precision="2"
223   - :max="1000000000"
224   - :min="0.01"
225   - :disabled="scope.row.status===1">
  219 + size="small"
  220 + v-model="scope.row.costPrice"
  221 + controls-position="right"
  222 + :precision="2"
  223 + :max="1000000000"
  224 + :min="0.01"
  225 + :disabled="scope.row.status==1">
226 226 </el-input-number>
227 227 </template>
228 228 </el-table-column>
229 229 <el-table-column
230   - prop="oriPrice"
231   - label="原价">
  230 + prop="oriPrice"
  231 + label="原价">
232 232 <template slot-scope="scope">
233 233 <el-input-number
234   - size="small"
235   - v-model="scope.row.originalPrice"
236   - controls-position="right"
237   - :precision="2"
238   - :max="1000000000"
239   - :min="0.01"
240   - :disabled="scope.row.status===1">
  234 + size="small"
  235 + v-model="scope.row.originalPrice"
  236 + controls-position="right"
  237 + :precision="2"
  238 + :max="1000000000"
  239 + :min="0.01"
  240 + :disabled="scope.row.status==1">
241 241 </el-input-number>
242 242 </template>
243 243 </el-table-column>
244 244 <el-table-column
245   - label="操作">
  245 + label="操作">
246 246 <template slot-scope="scope">
247   - <el-button type="text" size="small" @click="changeSkuStatus(`${scope.$index}`)" v-if="scope.row.status===0">
  247 + <el-button type="text" size="small" @click="changeSkuStatus(`${scope.$index}`)"
  248 + v-if="scope.row.status===0">
248 249 正常
249 250 </el-button>
250 251 <el-button type="text" size="small" @click="changeSkuStatus(`${scope.$index}`)" v-else>已禁用</el-button>
... ... @@ -280,456 +281,564 @@
280 281 </template>
281 282  
282 283 <script>
283   - import {createSpu, updateSpu, deleteSpu, getSpu, getSpuPage, exportSpuExcel} from "@/api/mall/product/spu";
284   - import {
285   - createCategory,
286   - deleteCategory,
287   - exportCategoryExcel,
288   - getCategory,
289   - listCategory,
290   - updateCategory
291   - } from "@/api/mall/product/category";
292   - import {
293   - createProperty,
294   - updateProperty,
295   - deleteProperty,
296   - getProperty,
297   - getPropertyPage,
298   - exportPropertyExcel
299   - } from "@/api/mall/product/property";
  284 +import {createSpu, updateSpu, deleteSpu, getSpu, getSpuPage, exportSpuExcel} from "@/api/mall/product/spu";
  285 +import {
  286 + createCategory,
  287 + deleteCategory,
  288 + exportCategoryExcel,
  289 + getCategory,
  290 + listCategory,
  291 + updateCategory
  292 +} from "@/api/mall/product/category";
  293 +import {
  294 + createProperty,
  295 + updateProperty,
  296 + deleteProperty,
  297 + getProperty,
  298 + getPropertyPage,
  299 + exportPropertyExcel
  300 +} from "@/api/mall/product/property";
300 301  
301   - import Editor from '@/components/Editor';
302   - import ImageUpload from '@/components/ImageUpload';
  302 +import Editor from '@/components/Editor';
  303 +import ImageUpload from '@/components/ImageUpload';
303 304  
304   - export default {
305   - name: "Spu",
306   - components: {
307   - Editor, ImageUpload
308   - },
309   - data() {
310   - return {
311   - tableLeftTitles: [],
312   - dbTagValues: [],
313   - allhistoryTags: [],
314   - unUseTags: [],
315   - propertyPageList: [],
316   - isShowTagInput: false,
317   - addTagInput: {
318   - name: '',
319   - propertyId: '',
320   - selectValues: [],
321   - selectValueIds: [],
322   - },
323   - skuTags: [],
324   - propName: {
325   - checkStrictly: true,
326   - label: 'name',
327   - value: 'id'
328   - },
329   - categoryList: [],
330   - // 遮罩层
331   - loading: true,
332   - // 导出遮罩层
333   - exportLoading: false,
334   - // 显示搜索条件
335   - showSearch: true,
336   - // 总条数
337   - total: 0,
338   - // 商品spu列表
339   - list: [],
340   - // 弹出层标题
341   - title: "",
342   - // 是否显示弹出层
343   - open: false,
344   - // 查询参数
345   - queryParams: {
346   - pageNo: 1,
347   - pageSize: 10,
348   - name: null,
349   - sellPoint: null,
350   - description: null,
351   - categoryId: null,
352   - picUrls: null,
353   - sort: null,
354   - likeCount: null,
355   - price: null,
356   - quantity: null,
357   - status: null,
358   - createTime: []
359   - },
360   - // 表单参数
361   - form: {
362   - id: undefined,
363   - name: undefined,
364   - sellPoint: undefined,
365   - description: undefined,
366   - categoryId: undefined,
367   - categoryIds: [],
368   - picUrls: undefined,
369   - sort: undefined,
370   - likeCount: undefined,
371   - price: undefined,
372   - quantity: undefined,
373   - status: undefined,
374   - isShowTagInput: undefined,
375   - skus:[],
376   - },
377   - // 表单校验
378   - rules: {
379   - sellPoint: [{required: true, message: "卖点不能为空", trigger: "blur"}],
380   - description: [{required: true, message: "描述不能为空", trigger: "blur"}],
381   - categoryIds: [{required: true, message: "分类id不能为空", trigger: "blur"}],
382   - picUrls: [{required: true, message: "商品主图地址", trigger: "blur"}],
383   - sort: [{required: true, message: "排序字段不能为空", trigger: "blur"}],
384   - },
385   - tagIndex:0,
386   - };
387   - },
388   - created() {
  305 +export default {
  306 + name: "Spu",
  307 + components: {
  308 + Editor, ImageUpload
  309 + },
  310 + data() {
  311 + return {
  312 + tableLeftTitles: [],
  313 + dbTagValues: [],
  314 + allhistoryTags: [],
  315 + unUseTags: [],
  316 + propertyPageList: [],
  317 + isShowTagInput: false,
  318 + addTagInput: {
  319 + name: '',
  320 + propertyId: '',
  321 + selectValues: [],
  322 + selectValueIds: [],
  323 + selectObect:[],
  324 + },
  325 + skuTags: [],
  326 + propName: {
  327 + checkStrictly: true,
  328 + label: 'name',
  329 + value: 'id'
  330 + },
  331 + categoryList: [],
  332 + // 遮罩层
  333 + loading: true,
  334 + // 导出遮罩层
  335 + exportLoading: false,
  336 + // 显示搜索条件
  337 + showSearch: true,
  338 + // 总条数
  339 + total: 0,
  340 + // 商品spu列表
  341 + list: [],
  342 + // 弹出层标题
  343 + title: "",
  344 + // 是否显示弹出层
  345 + open: false,
  346 + dateRangeCreateTime: [],
  347 + // 查询参数
  348 + queryParams: {
  349 + pageNo: 1,
  350 + pageSize: 10,
  351 + name: null,
  352 + sellPoint: null,
  353 + description: null,
  354 + categoryId: null,
  355 + picUrls: null,
  356 + sort: null,
  357 + likeCount: null,
  358 + price: null,
  359 + quantity: null,
  360 + status: null,
  361 + },
  362 + // 表单参数
  363 + form: {
  364 + id: undefined,
  365 + name: undefined,
  366 + sellPoint: undefined,
  367 + description: undefined,
  368 + categoryId: undefined,
  369 + categoryIds: [],
  370 + picUrls: undefined,
  371 + sort: undefined,
  372 + likeCount: undefined,
  373 + price: undefined,
  374 + quantity: undefined,
  375 + status: undefined,
  376 + isShowTagInput: undefined,
  377 + skus: [],
  378 + },
  379 + // 表单校验
  380 + rules: {
  381 + sellPoint: [{required: true, message: "卖点不能为空", trigger: "blur"}],
  382 + description: [{required: true, message: "描述不能为空", trigger: "blur"}],
  383 + categoryIds: [{required: true, message: "分类id不能为空", trigger: "blur"}],
  384 + picUrls: [{required: true, message: "商品主图地址", trigger: "blur"}],
  385 + sort: [{required: true, message: "排序字段不能为空", trigger: "blur"}],
  386 + },
  387 + tagIndex: 0,
  388 + };
  389 + },
  390 + created() {
389 391  
390   - this.getList();
  392 + this.getList();
  393 + this.getPropertyPageList();
  394 + this.getListCategory();
  395 + },
  396 + methods: {
  397 + getTableSpecData() {
  398 + return this.value
  399 + },
  400 + tableSpanMethod({row, column, rowIndex, columnIndex}) {
391 401  
392   - },
393   - methods: {
394   - getTableSpecData() {
395   - return this.value
396   - },
397   - tableSpanMethod({row, column, rowIndex, columnIndex}) {
  402 + },
  403 + changeSkuStatus(tagIndex) {
  404 + if (this.form.skus[tagIndex].status == 0) {
  405 + this.form.skus[tagIndex].status = 1;
  406 + } else {
  407 + this.form.skus[tagIndex].status = 0;
  408 + }
398 409  
399   - },
400   - changeSkuStatus(tagIndex) {
401   - if(this.form.skus[tagIndex].status === 0){
402   - this.form.skus[tagIndex].status = 1 ;
403   - }else {
404   - this.form.skus[tagIndex].status = 0 ;
405   - }
  410 + },
  411 + skuAddProdName() {
  412 + if (this.initing) {
  413 + return
  414 + }
  415 + let skuList = []
  416 + for (let i = 0; i < this.value.length; i++) {
  417 + const sku = Object.assign({}, this.value[i])
  418 + if (!sku.properties) {
  419 + return
  420 + }
  421 + sku.skuName = ''
  422 + let properties = sku.properties.split(';')
  423 + for (const propertiesKey in properties) {
  424 + sku.skuName += properties[propertiesKey].split(':')[1] + ' '
  425 + }
  426 + sku.prodName = this.prodName + ' ' + sku.skuName
  427 + skuList.push(sku)
  428 + }
  429 + this.$emit('input', skuList)
  430 + },
  431 + handleTagClose(tagIndex, tagItemIndex) {
406 432  
407   - },
408   - skuAddProdName() {
409   - if (this.initing) {
410   - return
411   - }
412   - let skuList = []
413   - for (let i = 0; i < this.value.length; i++) {
414   - const sku = Object.assign({}, this.value[i])
415   - if (!sku.properties) {
416   - return
417   - }
418   - sku.skuName = ''
419   - let properties = sku.properties.split(';')
420   - for (const propertiesKey in properties) {
421   - sku.skuName += properties[propertiesKey].split(':')[1] + ' '
422   - }
423   - sku.prodName = this.prodName + ' ' + sku.skuName
424   - skuList.push(sku)
425   - }
426   - this.$emit('input', skuList)
427   - },
428   - handleTagClose(tagIndex, tagItemIndex) {
429   - },
430   - //确定添加sku规格
431   - addTag() {
432   - let skus = this.unUseTags.map(function (item, index) {
433   - return item.name
434   - });
435   - console.log("skus=="+JSON.stringify(skus))
436   - let index = skus.indexOf(this.addTagInput.name);
437   - console.log("index=="+index)
438   - console.log("skus[index].id=="+this.unUseTags[index].id)
439   - console.log("this.unUseTags[index].propertyValueList=="+JSON.stringify(this.unUseTags[index].propertyValueList))
440   - this.addTagInput.propertyId = this.unUseTags[index].id;
441   - for (let i = 0; i < this.addTagInput.selectValues.length; i++) {
442   - for (let j = 0; j < this.unUseTags[index].propertyValueList.length; j++) {
443   - if (this.addTagInput.selectValues[i] === this.unUseTags[index].propertyValueList[j].name) {
444   - this.addTagInput.selectValueIds.push(this.unUseTags[index].propertyValueList[j].id)
445   - }
446   - }
447   - }
448   - let addTagInput = JSON.parse(JSON.stringify(this.addTagInput))
449   - console.log("addTagInput=="+JSON.stringify(addTagInput))
450   - this.skuTags.push(addTagInput);
451   - this.unUseTags.splice(index, 1);
452   - this.isShowTagInput = false;
453   - this.getTable();
454   - },
455   - getTable(){
456   - this.form.skus=[];
457   - let skuTags = JSON.parse(JSON.stringify(this.skuTags));
458   - let sku1s = [];
459   - let skuIds = [];
460   - let propertyIds = [];
461   - let propertyNames = [];
462   - for (let i = 0; i < skuTags.length; i++) {
463   - sku1s.push(skuTags[i].selectValues);
464   - skuIds.push(skuTags[i].selectValueIds);
465   - propertyIds.push(skuTags[i].propertyId);
466   - propertyNames.push(skuTags[i].name);
467   - }
468   - let skuAll = sku1s.reduce((x,y) =>{
469   - let arr = [];
470   - x.forEach(m => y.forEach(y => arr.push(m.concat([y]))))
471   - return arr;
472   - },[[]])
473   - console.log(skuAll);
  433 + },
  434 + //确定添加sku规格
  435 + addTag() {
474 436  
475   - let skuIdAll = skuIds.reduce((x,y) =>{
476   - let arr = [];
477   - x.forEach(m => y.forEach(y => arr.push(m.concat([y]))))
478   - return arr;
479   - },[[]])
480   - console.log(skuIdAll);
481   - for (let i = 0; i < skuAll.length; i++) {
482   - let han = {
483   - propertyNames:propertyNames,
484   - propertyIds:propertyIds,
485   - propertyChildNames:skuAll[i],
486   - propertyChildIds:skuIdAll[i],
487   - properties: [],
488   - picUrl: '',
489   - costPrice: '',
490   - originalPrice: '',
491   - spuId: '',
492   - prodName: '',
493   - price: '',
494   - barCode: '',
495   - status: '0',
496   - }
497   - this.form.skus.push(han);
498   - }
499   - this.form.skus.forEach(x=>{
500   - x.properties=[];
501   - for (let i = 0; i <x.propertyIds.length ; i++) {
502   - x.properties.push({
503   - propertyId:x.propertyIds[i],
504   - valueId:x.propertyChildIds[i]
505   - })
506   - }
507   - })
  437 + let skus = this.unUseTags.map(function (item, index) {
  438 + return item.name
  439 + });
  440 + let index = skus.indexOf(this.addTagInput.name);
508 441  
509   - console.log("this.skus=="+JSON.stringify(this.form.skus))
510   - },
511   - hideTagInput() {
512   - this.isShowTagInput = false;
513   - this.addTagInput = {
514   - name: '',
515   - propertyId: '',
516   - selectValues: [],
517   - selectValueIds: [],
518   - };
519   - },
520   - shopTagInput() {
521   - if (this.unUseTags.length <= 0) {
522   - return this.$message.error("规格已经添加完毕")
523   - }
524   - this.isShowTagInput = true;
525   - this.addTagInput = {
526   - name: '',
527   - propertyId: '',
528   - selectValues: [],
529   - selectValueIds: [],
530   - };
531   - },
532   - //删除已选的规格
533   - removeTag(row) {
534   - let skus = this.allhistoryTags.map(function (item, index) {
535   - return item.name
536   - })
537   - let index = skus.indexOf(this.skuTags[row].name);
538   - this.unUseTags.push(this.allhistoryTags[index]);
539   - this.skuTags.splice(row, 1);
540   - this.getTable();
541   - },
542   - handleTagClick(row) {
543   - for (let i = 0; i < this.propertyPageList.length; i++) {
544   - if (row == this.propertyPageList[i].name) {
545   - this.dbTagValues = this.propertyPageList[i].propertyValueList
546   - }
547   - }
548   - },
549   - /** 查询规格 */
550   - getPropertyPageList() {
551   - // 执行查询
552   - getPropertyPage().then(response => {
553   - this.propertyPageList = response.data.list;
  442 + this.addTagInput.propertyId = this.unUseTags[index].id;
  443 + for (let i = 0; i < this.addTagInput.selectValues.length; i++) {
  444 + for (let j = 0; j < this.unUseTags[index].propertyValueList.length; j++) {
  445 + if (this.addTagInput.selectValues[i] === this.unUseTags[index].propertyValueList[j].name) {
  446 + this.addTagInput.selectValueIds.push(this.unUseTags[index].propertyValueList[j].id)
  447 + this.addTagInput.selectObect.push({
  448 + id:this.unUseTags[index].propertyValueList[j].id,
  449 + name:this.unUseTags[index].propertyValueList[j].name,
  450 + })
  451 + }
  452 + }
  453 + }
  454 + let addTagInput = JSON.parse(JSON.stringify(this.addTagInput))
  455 + this.skuTags.push(addTagInput);
554 456  
555   - this.unUseTags = this.propertyPageList.map(function (item, index) {
556   - return item
557   - })
558   - this.allhistoryTags = JSON.parse(JSON.stringify(this.unUseTags));
559   - console.log(this.propertyPageList)
560   - });
561   - },
562   - /** 查询分类 */
563   - getListCategory() {
564   - // 执行查询
565   - listCategory().then(response => {
566   - this.categoryList = this.handleTree(response.data, "id", "parentId");
  457 + // if (this.skuTags.length > 1) {
  458 + this.skuTags = this.skuTags.sort((a, b) => a.propertyId - b.propertyId);
  459 + this.skuTags.forEach(function (item,index) {
  460 + item.selectObect = item.selectObect.sort((a, b) => a.id - b.id)
  461 + })
567 462  
568   - });
569   - },
570   - /** 查询列表 */
571   - getList() {
572   - this.loading = true;
573   - // 执行查询
574   - getSpuPage(this.queryParams).then(response => {
575   - this.list = response.data.list;
576   - this.total = response.data.total;
577   - this.loading = false;
578   - });
579   - },
580   - /** 取消按钮 */
581   - cancel() {
582   - this.open = false;
583   - this.reset();
584   - },
585   - /** 表单重置 */
586   - reset() {
  463 + for (let i = 0; i <this.skuTags.length ; i++) {
  464 + let selectValueIds=[];
  465 + let selectValues=[];
  466 + for (let j = 0; j < this.skuTags[i].selectObect.length; j++) {
  467 + selectValueIds.push(this.skuTags[i].selectObect[j].id);
  468 + selectValues.push(this.skuTags[i].selectObect[j].name);
  469 + }
  470 + this.skuTags[i].selectValues = selectValues;
  471 + this.skuTags[i].selectValueIds = selectValueIds;
  472 + }
  473 +
  474 + this.unUseTags.splice(index, 1);
  475 + this.isShowTagInput = false;
  476 + this.getTable();
  477 + },
  478 + getTable() {
  479 + this.form.skus = [];
  480 + let skuTags = JSON.parse(JSON.stringify(this.skuTags));
  481 + let sku1s = [];
  482 + let skuIds = [];
  483 + let propertyIds = [];
  484 + let propertyNames = [];
  485 + for (let i = 0; i < skuTags.length; i++) {
  486 + sku1s.push(skuTags[i].selectValues);
  487 + skuIds.push(skuTags[i].selectValueIds);
  488 + propertyIds.push(skuTags[i].propertyId);
  489 + propertyNames.push(skuTags[i].name);
  490 + }
587 491  
588   - this.form = {
589   - id: undefined,
590   - name: undefined,
591   - sellPoint: undefined,
592   - description: undefined,
593   - categoryId: undefined,
594   - categoryIds: [],
595   - picUrls: undefined,
596   - sort: undefined,
597   - likeCount: undefined,
598   - price: undefined,
599   - quantity: undefined,
600   - status: undefined,
601   - isShowTagInput: undefined,
602   - skus:[],
603   - };
604   - this.skuTags=[];
605   - this.resetForm("form");
606   - },
607   - /** 搜索按钮操作 */
608   - handleQuery() {
609   - this.queryParams.pageNo = 1;
610   - this.getList();
611   - },
612   - /** 重置按钮操作 */
613   - resetQuery() {
614   - this.dateRangeCreateTime = [];
615   - this.resetForm("queryForm");
616   - this.handleQuery();
617   - },
618   - /** 新增按钮操作 */
619   - handleAdd() {
620   - this.reset();
621   - this.open = true;
622   - this.title = "添加商品spu";
623   - this.getListCategory();
624   - this.getPropertyPageList();
625   - },
626   - /** 修改按钮操作 */
627   - handleUpdate(row) {
628   - this.reset();
629   - getSpu(row.id).then(response => {
630   - console.log(">>>>>> response.data:" + JSON.stringify(response.data))
631   - let dataSpu = response.data;
632   - this.form = {
633   - id: dataSpu.id,
634   - name: dataSpu.name,
635   - sellPoint: dataSpu.sellPoint,
636   - description: dataSpu.sellPoint,
637   - categoryId: dataSpu.sellPoint,
638   - categoryIds: dataSpu.categoryIds,
639   - picUrls: dataSpu.picUrls,
640   - sort: dataSpu.sort,
641   - likeCount: dataSpu.likeCount,
642   - price: dataSpu.price,
643   - quantity: dataSpu.quantity,
644   - status: dataSpu.status,
645   - isShowTagInput:undefined,
646   - skus:dataSpu.skus
647   - // skus:dataSpu.productSkuRespVOS,
648   - };
649   - this.open = true;
650   - this.title = "修改商品spu";
651   - });
652   - },
  492 + let skuAll = sku1s.reduce((x, y) => {
  493 + let arr = [];
  494 + x.forEach(m => y.forEach(y => arr.push(m.concat([y]))))
  495 + return arr;
  496 + }, [[]])
653 497  
654   - /** 提交按钮 */
655   - submitForm() {
656   - console.log(this.form.picUrls.split(','));
657   - this.$refs["form"].validate(valid => {
658   - if (!valid) {
659   - return;
660   - }
661   - this.form.picUrls = this.form.picUrls.split(',');
662   - this.form.categoryId = this.form.categoryIds[(this.form.categoryIds.length - 1)];
663   - this.form.status = Number(this.form.status);
664   - // 修改的提交
665   - if (this.form.id != null) {
666   - updateSpu(this.form).then(response => {
667   - this.$modal.msgSuccess("修改成功");
668   - this.open = false;
669   - this.getList();
670   - });
671   - return;
672   - }
673   - // 添加的提交
674   - createSpu(this.form).then(response => {
675   - this.$modal.msgSuccess("新增成功");
676   - this.open = false;
677   - this.getList();
678   - });
679   - });
680   - },
681   - /** 删除按钮操作 */
682   - handleDelete(row) {
683   - const id = row.id;
684   - this.$modal.confirm('是否确认删除商品spu编号为"' + id + '"的数据项?').then(function () {
685   - return deleteSpu(id);
686   - }).then(() => {
687   - this.getList();
688   - this.$modal.msgSuccess("删除成功");
689   - }).catch(() => {
690   - });
691   - },
692   - /** 导出按钮操作 */
693   - handleExport() {
694   - // 处理查询参数
695   - let params = {...this.queryParams};
696   - params.pageNo = undefined;
697   - params.pageSize = undefined;
698   - // 执行导出
699   - this.$modal.confirm('是否确认导出所有商品spu数据项?').then(() => {
700   - this.exportLoading = true;
701   - return exportSpuExcel(params);
702   - }).then(response => {
703   - this.$download.excel(response, '商品spu.xls');
704   - this.exportLoading = false;
705   - }).catch(() => {
706   - });
707   - }
  498 + let skuIdAll = skuIds.reduce((x, y) => {
  499 + let arr = [];
  500 + x.forEach(m => y.forEach(y => arr.push(m.concat([y]))))
  501 + return arr;
  502 + }, [[]])
  503 +
  504 + for (let i = 0; i < skuAll.length; i++) {
  505 + let han = {
  506 + propertyNames: propertyNames,
  507 + propertyIds: propertyIds,
  508 + propertyChildNames: skuAll[i],
  509 + propertyChildIds: skuIdAll[i],
  510 + properties: [],
  511 + picUrl: '',
  512 + costPrice: '',
  513 + originalPrice: '',
  514 + spuId: '',
  515 + prodName: '',
  516 + price: '',
  517 + barCode: '',
  518 + status: '0',
708 519 }
709   - };
  520 + this.form.skus.push(han);
  521 + }
  522 + this.form.skus.forEach(x => {
  523 + x.properties = [];
  524 + for (let i = 0; i < x.propertyIds.length; i++) {
  525 + x.properties.push({
  526 + propertyId: x.propertyIds[i],
  527 + valueId: x.propertyChildIds[i]
  528 + })
  529 + }
  530 + })
  531 +
  532 + },
  533 + hideTagInput() {
  534 + this.isShowTagInput = false;
  535 + this.addTagInput = {
  536 + name: '',
  537 + propertyId: '',
  538 + selectValues: [],
  539 + selectValueIds: [],
  540 + selectObect: [],
  541 + };
  542 + },
  543 + shopTagInput() {
  544 + if (this.unUseTags.length <= 0) {
  545 + return this.$message.error("规格已经添加完毕")
  546 + }
  547 + this.isShowTagInput = true;
  548 + this.addTagInput = {
  549 + name: '',
  550 + propertyId: '',
  551 + selectValues: [],
  552 + selectValueIds: [],
  553 + selectObect: [],
  554 + };
  555 + },
  556 + //删除已选的规格
  557 + removeTag(row) {
  558 + let skus = this.allhistoryTags.map(function (item, index) {
  559 + return item.name
  560 + })
  561 + let index = skus.indexOf(this.skuTags[row].name);
  562 + this.unUseTags.push(this.allhistoryTags[index]);
  563 + this.skuTags.splice(row, 1);
  564 + this.getTable();
  565 + },
  566 + handleTagClick(row) {
  567 + for (let i = 0; i < this.propertyPageList.length; i++) {
  568 + if (row == this.propertyPageList[i].name) {
  569 + this.dbTagValues = this.propertyPageList[i].propertyValueList
  570 + }
  571 + }
  572 + },
  573 + /** 查询规格 */
  574 + getPropertyPageList() {
  575 + // 执行查询
  576 + getPropertyPage().then(response => {
  577 + this.propertyPageList = response.data.list;
  578 +
  579 + this.unUseTags = this.propertyPageList.map(function (item, index) {
  580 + return item
  581 + })
  582 + this.allhistoryTags = JSON.parse(JSON.stringify(this.unUseTags));
  583 + });
  584 + },
  585 + /** 查询分类 */
  586 + getListCategory() {
  587 + // 执行查询
  588 + listCategory().then(response => {
  589 + this.categoryList = this.handleTree(response.data, "id", "parentId");
  590 +
  591 + });
  592 + },
  593 + /** 查询列表 */
  594 + getList() {
  595 + this.loading = true;
  596 + // 处理查询参数
  597 + let params = {...this.queryParams};
  598 + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
  599 + // 执行查询
  600 + getSpuPage(params).then(response => {
  601 + this.list = response.data.list;
  602 + this.total = response.data.total;
  603 + this.loading = false;
  604 + });
  605 + },
  606 + /** 取消按钮 */
  607 + cancel() {
  608 + this.open = false;
  609 + this.reset();
  610 + },
  611 + /** 表单重置 */
  612 + reset() {
  613 +
  614 + this.form = {
  615 + id: undefined,
  616 + name: undefined,
  617 + sellPoint: undefined,
  618 + description: undefined,
  619 + categoryId: undefined,
  620 + categoryIds: [],
  621 + picUrls: undefined,
  622 + sort: undefined,
  623 + likeCount: undefined,
  624 + price: undefined,
  625 + quantity: undefined,
  626 + status: undefined,
  627 + isShowTagInput: undefined,
  628 + skus: [],
  629 + };
  630 + this.skuTags = [];
  631 + this.resetForm("form");
  632 + },
  633 + /** 搜索按钮操作 */
  634 + handleQuery() {
  635 + this.queryParams.pageNo = 1;
  636 + this.getList();
  637 + },
  638 + /** 重置按钮操作 */
  639 + resetQuery() {
  640 + this.dateRangeCreateTime = [];
  641 + this.resetForm("queryForm");
  642 + this.handleQuery();
  643 + },
  644 + /** 新增按钮操作 */
  645 + handleAdd() {
  646 + this.reset();
  647 + this.open = true;
  648 + this.title = "添加商品spu";
  649 + this.getPropertyPageList();
  650 + },
  651 + /** 修改按钮操作 */
  652 + handleUpdate(row) {
  653 + this.reset();
  654 + const id = row.id;
  655 + getSpu(id).then(response => {
  656 + let dataSpu = response.data;
  657 + this.form = {
  658 + id: dataSpu.id,
  659 + name: dataSpu.name,
  660 + sellPoint: dataSpu.sellPoint,
  661 + description: dataSpu.sellPoint,
  662 + categoryId: dataSpu.sellPoint,
  663 + categoryIds: dataSpu.categoryIds,
  664 + picUrls: dataSpu.picUrls,
  665 + sort: dataSpu.sort,
  666 + likeCount: dataSpu.likeCount,
  667 + price: dataSpu.price,
  668 + quantity: dataSpu.quantity,
  669 + status: dataSpu.status,
  670 + isShowTagInput: undefined,
  671 + skus: [],
  672 + skusList: dataSpu.skus,
  673 + productPropertyViews: dataSpu.productPropertyViews,
  674 + // skus:dataSpu.productSkuRespVOS,
  675 + };
  676 + this.getDataHandle();
  677 + this.open = true;
  678 + this.title = "修改商品spu";
  679 + });
  680 + },
  681 + getDataHandle() {
  682 + let that = this;
  683 + let productPropertyViews = JSON.parse(JSON.stringify(this.form.productPropertyViews));
  684 + productPropertyViews = productPropertyViews.sort((a, b) => a.propertyId - b.propertyId);
  685 + productPropertyViews.forEach(item => {
  686 + item.propertyValues = item.propertyValues.sort((a, b) => a.v1 - b.v1);
  687 + })
  688 + let skuIds = [];
  689 + for (let i = 0; i < productPropertyViews.length; i++) {
  690 + let han = {
  691 + name: productPropertyViews[i].name,
  692 + propertyId: productPropertyViews[i].propertyId,
  693 + selectValues: [],
  694 + selectValueIds: [],
  695 + }
  696 + for (let j = 0; j < productPropertyViews[i].propertyValues.length; j++) {
  697 + han.selectValues.push(productPropertyViews[i].propertyValues[j].v2);
  698 + han.selectValueIds.push(productPropertyViews[i].propertyValues[j].v1);
  699 + }
  700 + skuIds.push(han)
  701 + }
  702 + this.skuTags = skuIds;
  703 + this.unUseTags = this.allhistoryTags.filter((v) =>
  704 + skuIds.every((val) => val.name != v.name)
  705 + )
  706 + this.getHandleTable();
  707 + },
  708 + getHandleTable() {
  709 + this.form.skus = [];
  710 + let skuTags = JSON.parse(JSON.stringify(this.skuTags));
  711 + let sku1s = [];
  712 + let skuIds = [];
  713 + let propertyIds = [];
  714 + let propertyNames = [];
  715 + for (let i = 0; i < skuTags.length; i++) {
  716 + sku1s.push(skuTags[i].selectValues);
  717 + skuIds.push(skuTags[i].selectValueIds);
  718 + propertyIds.push(skuTags[i].propertyId);
  719 + propertyNames.push(skuTags[i].name);
  720 + }
  721 + let skuAll = sku1s.reduce((x, y) => {
  722 + let arr = [];
  723 + x.forEach(m => y.forEach(y => arr.push(m.concat([y]))))
  724 + return arr;
  725 + }, [[]])
  726 +
  727 + let skuIdAll = skuIds.reduce((x, y) => {
  728 + let arr = [];
  729 + x.forEach(m => y.forEach(y => arr.push(m.concat([y]))))
  730 + return arr;
  731 + }, [[]])
  732 +
  733 + for (let i = 0; i < skuAll.length; i++) {
  734 + let han = {
  735 + propertyNames: propertyNames,
  736 + propertyIds: propertyIds,
  737 + propertyChildNames: skuAll[i],
  738 + propertyChildIds: skuIdAll[i],
  739 + properties: this.form.skusList[i].properties,
  740 + picUrl: this.form.skusList[i].picUrl,
  741 + costPrice: this.form.skusList[i].costPrice,
  742 + originalPrice: this.form.skusList[i].originalPrice,
  743 + spuId: this.form.skusList[i].spuId,
  744 + prodName: this.form.skusList[i].prodName,
  745 + price: this.form.skusList[i].price,
  746 + barCode: this.form.skusList[i].barCode,
  747 + status: this.form.skusList[i].status,
  748 + }
  749 + this.form.skus.push(han);
  750 + }
  751 + this.form.skus.forEach(x => {
  752 + x.properties = [];
  753 + for (let i = 0; i < x.propertyIds.length; i++) {
  754 + x.properties.push({
  755 + propertyId: x.propertyIds[i],
  756 + valueId: x.propertyChildIds[i]
  757 + })
  758 + }
  759 + })
  760 +
  761 + },
  762 + /** 提交按钮 */
  763 + submitForm() {
  764 +
  765 + this.$refs["form"].validate(valid => {
  766 + if (!valid) {
  767 + return;
  768 + }
  769 + this.form.picUrls = this.form.picUrls.split(',');
  770 + this.form.categoryId = this.form.categoryIds[(this.form.categoryIds.length - 1)];
  771 + this.form.status = Number(this.form.status);
  772 + // 修改的提交
  773 + if (this.form.id != null) {
  774 + updateSpu(this.form).then(response => {
  775 + this.$modal.msgSuccess("修改成功");
  776 + this.open = false;
  777 + this.getList();
  778 + });
  779 + return;
  780 + }
  781 + // 添加的提交
  782 + createSpu(this.form).then(response => {
  783 + this.$modal.msgSuccess("新增成功");
  784 + this.open = false;
  785 + this.getList();
  786 + });
  787 + });
  788 + },
  789 + /** 删除按钮操作 */
  790 + handleDelete(row) {
  791 + const id = row.id;
  792 + this.$modal.confirm('是否确认删除商品spu编号为"' + id + '"的数据项?').then(function () {
  793 + return deleteSpu(id);
  794 + }).then(() => {
  795 + this.getList();
  796 + this.$modal.msgSuccess("删除成功");
  797 + }).catch(() => {
  798 + });
  799 + },
  800 + /** 导出按钮操作 */
  801 + handleExport() {
  802 + // 处理查询参数
  803 + let params = {...this.queryParams};
  804 + params.pageNo = undefined;
  805 + params.pageSize = undefined;
  806 + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
  807 + // 执行导出
  808 + this.$modal.confirm('是否确认导出所有商品spu数据项?').then(() => {
  809 + this.exportLoading = true;
  810 + return exportSpuExcel(params);
  811 + }).then(response => {
  812 + this.$download.excel(response, '商品spu.xls');
  813 + this.exportLoading = false;
  814 + }).catch(() => {
  815 + });
  816 + }
  817 + }
  818 +};
710 819 </script>
711 820 <style lang="scss">
712   - .app-container {
713   - .el-tag + .el-tag {
714   - margin-left: 10px;
715   - }
  821 +.app-container {
  822 + .el-tag + .el-tag {
  823 + margin-left: 10px;
  824 + }
716 825  
717   - .button-new-tag {
718   - margin-left: 10px;
719   - height: 32px;
720   - line-height: 30px;
721   - padding-top: 0;
722   - padding-bottom: 0;
723   - }
  826 + .button-new-tag {
  827 + margin-left: 10px;
  828 + height: 32px;
  829 + line-height: 30px;
  830 + padding-top: 0;
  831 + padding-bottom: 0;
  832 + }
724 833  
725   - .input-new-tag {
726   - width: 90px;
727   - margin-left: 10px;
728   - vertical-align: bottom;
729   - }
  834 + .input-new-tag {
  835 + width: 90px;
  836 + margin-left: 10px;
  837 + vertical-align: bottom;
  838 + }
730 839  
731   - .img-height {
732   - height: 65px;
733   - }
  840 + .img-height {
  841 + height: 65px;
734 842 }
  843 +}
735 844 </style>
... ...
yudao-ui-app/api/category.js 0 → 100644
  1 +//请求工具参考https://ext.dcloud.net.cn/plugin?id=392
  2 +const { http } = uni.$u
  3 +
  4 +// 查询分类列表
  5 +export const categoryListData = params => http.get('product/category/list', { params })
... ...
yudao-ui-app/api/product.js 0 → 100644
  1 +//请求工具参考https://ext.dcloud.net.cn/plugin?id=392
  2 +const { http } = uni.$u
  3 +
  4 +// 查询商品spu列表
  5 +export const productSpuPage = params => http.get('product/spu/page', { params })
  6 +
  7 +// 查询商品
  8 +export const productSpu = params => http.get('product/spu/', { params })
... ...
yudao-ui-app/pages.json
... ... @@ -12,6 +12,14 @@
12 12 "navigationBarTitleText": "分类"
13 13 }
14 14 },
  15 + {
  16 + "path": "pages/category/product-list",
  17 + "style": {
  18 + "navigationBarTitleText": "",
  19 + "navigationStyle": "custom",
  20 + "navigationBarTextStyle": "white"
  21 + }
  22 + },
15 23 {
16 24 "path": "pages/cart/cart",
17 25 "style": {
... ... @@ -112,7 +120,7 @@
112 120 "globalStyle": {
113 121 "navigationBarTextStyle": "black",
114 122 "navigationBarTitleText": "yudao-ui-app",
115   - "navigationBarBackgroundColor": "#F8F8F8",
116   - "backgroundColor": "#FFFFFF"
  123 + "navigationBarBackgroundColor": "#ffffff",
  124 + "backgroundColor": "#ffffff"
117 125 }
118 126 }
... ...
yudao-ui-app/pages/category/category.vue
1 1 <template>
2 2 <view class="container">
  3 + <!-- 搜索框 -->
3 4 <view class="search-wrap">
4   - <u-search placeholder="搜索" disabled height="32" :show-action="false" @click="handleSearchClick"></u-search>
  5 + <u-search placeholder="搜索" disabled height="32" bgColor="#f2f2f2" margin="0 20rpx" :show-action="false"
  6 + @click="handleSearchClick"></u-search>
5 7 </view>
  8 +
  9 + <!-- 分类内容 -->
6 10 <view class="category-box">
7   - <view class="box-left">
8   - <view>
9   - <view class="category-item" v-for="(item, index) in categoryList" :key="item.id">
10   - <view class="item-title" :class="{ active: currentIndex === index }" @click="handleCategoryClick(index)">
11   - <text>{{ item.name }}</text>
12   - </view>
  11 + <!-- 左侧导航栏 -->
  12 + <scroll-view scroll-y="true" class='box-left'>
  13 + <view class="category-item" v-for="(item, index) in categoryList" :key="item.id">
  14 + <view class="item-title" :class="{ active: currentIndex === index }" @click="handleCategoryClick(index)">
  15 + <text>{{ item.name }}</text>
13 16 </view>
14 17 </view>
15   - </view>
16   - <view class="box-right">
17   - <image class="category-image" :showLoading="true" :src="categoryList[currentIndex].image" width="530rpx" height="160rpx" @click="click"></image>
  18 + </scroll-view>
  19 +
  20 + <!-- 右侧分类内容 -->
  21 + <scroll-view scroll-y="true" class="box-right">
  22 + <view class="category-image">
  23 + <image :showLoading="true" :src="categoryList[currentIndex].bannerUrl" mode='widthFix' @click="click"></image>
  24 + </view>
  25 +
18 26 <view class="sub-category-box" v-for="(item, index) in categoryList[currentIndex].children" :key="item.id">
19 27 <view class="sub-category-header">
20   - <view class="title">{{ item.title }}</view>
21   - <view class="more">查看更多</view>
  28 + <view class="title">{{ item.name }}</view>
  29 + <view class="more" @click="handleCategory(item, 0)">查看更多</view>
  30 + </view>
  31 +
  32 + <view class="sub-category-grid">
  33 + <u-grid col="3">
  34 + <u-grid-item v-for="(subItem, subIndex) in item.children" :key="subItem.id">
  35 + <view class="sub-category-item" @click="handleCategory(item, subIndex)">
  36 + <u-icon name="photo" :size="80" v-if="subItem.bannerUrl === null"></u-icon>
  37 + <image :src="item.bannerUrl" v-if="subItem.bannerUrl != null" mode='widthFix' />
  38 + <text class="sub-category-title">{{ subItem.name }}</text>
  39 + </view>
  40 + </u-grid-item>
  41 + </u-grid>
22 42 </view>
23   - <u-grid class="sub-category-grid" col="3">
24   - <u-grid-item v-for="(subItem, subIndex) in item.category" :key="subItem.id">
25   - <view class="sub-category-item">
26   - <u-icon name="photo" :size="80"></u-icon>
27   - <text class="sub-category-title">{{ subItem.title }}</text>
28   - </view>
29   - </u-grid-item>
30   - </u-grid>
31 43 </view>
32   - </view>
  44 + </scroll-view>
33 45 </view>
34 46 </view>
35 47 </template>
36 48  
37 49 <script>
38   -export default {
39   - data() {
40   - return {
41   - currentIndex: 0,
42   - categoryList: []
43   - }
44   - },
45   - onLoad() {
46   - for (let i = 0; i < 10; i++) {
47   - this.categoryList.push({
48   - id: i,
49   - image: 'https://cdn.uviewui.com/uview/swiper/swiper1.png',
50   - name: '商品分类' + i,
51   - children: [
52   - {
53   - id: 0,
54   - title: '分类' + i + '-1',
55   - category: [
56   - {
57   - id: 0,
58   - image: '',
59   - title: '分类' + i + '-1-1'
60   - },
61   - {
62   - id: 2,
63   - image: '',
64   - title: '分类' + i + '-1-2'
65   - },
66   - {
67   - id: 3,
68   - image: '',
69   - title: '分类' + i + '-1-3'
70   - }
71   - ]
72   - },
73   - {
74   - id: 1,
75   - title: '分类' + i + '-2',
76   - category: [
77   - {
78   - id: 0,
79   - image: '',
80   - title: '分类' + i + '-2-1'
81   - },
82   - {
83   - id: 2,
84   - image: '',
85   - title: '分类' + i + '-2-2'
86   - },
87   - {
88   - id: 3,
89   - image: '',
90   - title: '分类' + i + '-2-3'
91   - }
92   - ]
93   - }
94   - ]
95   - })
96   - }
97   - },
98   - methods: {
99   - handleSearchClick(e) {
100   - uni.$u.route('/pages/search/search')
  50 + import { categoryListData } from '../../api/category';
  51 + import { handleTree, convertTree } from '../../utils/tree.js';
  52 +
  53 + export default {
  54 + data() {
  55 + return {
  56 + currentIndex: 0,
  57 + categoryList: []
  58 + }
101 59 },
102   - handleCategoryClick(index) {
103   - if (this.currentIndex !== index) {
104   - this.currentIndex = index
  60 + onLoad() {
  61 + this.handleCategoryList();
  62 + },
  63 + methods: {
  64 + // 点击搜索框
  65 + handleSearchClick(e) {
  66 + uni.$u.route('/pages/search/search')
  67 + },
  68 + // 点击左侧导航栏
  69 + handleCategoryClick(index) {
  70 + if (this.currentIndex !== index) {
  71 + this.currentIndex = index
  72 + }
  73 + },
  74 + // 获取分类列表并构建树形结构
  75 + handleCategoryList() {
  76 + categoryListData().then(res => {
  77 + this.categoryList = handleTree(res.data, "id", "parentId");
  78 + })
  79 + },
  80 + handleCategory(item, index){
  81 + // console.log(item)
  82 + // console.log(index)
  83 + uni.navigateTo({
  84 + url:"./product-list?item="+encodeURIComponent(JSON.stringify(item))+"&index="+index
  85 + })
105 86 }
106 87 }
107 88 }
108   -}
109 89 </script>
110 90  
111 91 <style lang="scss" scoped>
  92 + .search-wrap {
  93 + background: #ffffff;
  94 + position: fixed;
  95 + top: 0;
  96 + left: 0;
  97 + box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.07);
  98 + padding: 20rpx 0;
  99 + width: 100%;
  100 + z-index: 3;
  101 + }
  102 +
  103 + .category-box {
  104 + position: fixed;
  105 + display: flex;
  106 + overflow: hidden;
  107 + margin-top: 100rpx;
  108 + height: calc(100% - 100rpx);
  109 +
  110 + .box-left {
  111 + width: 200rpx;
  112 + padding-top: 5rpx;
  113 + overflow: scroll;
  114 + z-index: 2;
  115 + background-color: #f2f2f2;
  116 +
  117 + .category-item {
  118 + line-height: 80rpx;
  119 + height: 80rpx;
  120 + text-align: center;
  121 + color: #777;
112 122  
113   -.search-wrap {
114   - background: $custom-bg-color;
115   - padding: 20rpx;
116   -}
117   -
118   -.category-box {
119   - display: flex;
120   - .box-left {
121   - width: 200rpx;
122   - padding-top: 20rpx;
123   - border-right: $custom-border-style;
124   - .category-item {
125   - border-bottom: $custom-border-style;
126   - padding: 20rpx 0;
127   - .item-title {
128   - padding-left: 30rpx;
129   - font-size: 28rpx;
130   - &.active {
131   - border-left: 6rpx solid $u-primary;
132   - font-weight: 700;
  123 + .item-title {
  124 + font-size: 28rpx;
  125 +
  126 + &.active {
  127 + font-size: 28rpx;
  128 + font-weight: bold;
  129 + position: relative;
  130 + background: #fff;
  131 + color: $u-primary;
  132 + }
  133 +
  134 + &.active::before {
  135 + position: absolute;
  136 + left: 0;
  137 + content: "";
  138 + width: 8rpx;
  139 + height: 32rpx;
  140 + top: 25rpx;
  141 + background: $u-primary;
  142 + }
133 143 }
134 144 }
135 145 }
136   - }
137   - .box-right {
138   - flex: 1;
139   - .category-image {
140   - width: 510rpx;
141   - height: 160rpx;
142   - padding: 20rpx;
143   - }
144 146  
145   - .sub-category-box {
146   - .sub-category-header {
147   - @include flex-space-between;
148   - padding: 30rpx 20rpx;
  147 + .box-right {
  148 + width: 550rpx;
  149 + height: 100%;
  150 + box-sizing: border-box;
  151 + z-index: 1;
149 152  
150   - .title {
151   - font-size: 28rpx;
152   - font-weight: 700;
153   - }
154   - .more {
155   - font-size: 22rpx;
156   - color: #939393;
  153 + .category-image {
  154 + width: 510rpx;
  155 + box-sizing: border-box;
  156 + overflow: hidden;
  157 + position: relative;
  158 + margin: 30rpx 20rpx 0;
  159 +
  160 + image {
  161 + width: 100%;
157 162 }
158 163 }
159 164  
160   - .sub-category-grid {
161   - padding: 0 15rpx;
  165 + .sub-category-box {
  166 + .sub-category-header {
  167 + @include flex-space-between;
  168 + padding: 20rpx 20rpx;
  169 +
  170 + .title {
  171 + font-size: 28rpx;
  172 + font-weight: bolder;
  173 + }
  174 +
  175 + .more {
  176 + font-size: 22rpx;
  177 + color: #939393;
  178 + }
  179 + }
  180 +
  181 + .sub-category-grid {
  182 + padding: 0 15rpx;
  183 +
  184 + .sub-category-item {
  185 + @include flex-center(column);
  186 + background: #fff;
162 187  
163   - .sub-category-item {
164   - @include flex-center(column);
165   - background: #fff;
  188 + image {
  189 + text-align: center;
  190 + width: 150rpx;
  191 + height: 150rpx;
  192 + line-height: 150rpx;
  193 + font-size: 0;
  194 + }
166 195  
167   - .sub-category-title {
168   - margin: 15rpx 0;
169   - font-size: 24rpx;
  196 + .sub-category-title {
  197 + margin: 15rpx 0;
  198 + font-size: 22rpx;
  199 + }
170 200 }
171 201 }
172 202 }
173 203 }
174 204 }
175   -}
176 205 </style>
... ...
yudao-ui-app/pages/category/product-list.vue 0 → 100644
  1 +<template>
  2 + <view class="container">
  3 + <u-navbar :title="title" :autoBack="true" placeholder="true" titleStyle="font-size: 28rpx">
  4 + </u-navbar>
  5 + <view class="context">
  6 + <view class="tabs-top">
  7 + <u-tabs :list="categoryList" @click="changeTabs" :current="current" lineHeight="2" lineWidth="85rpx"
  8 + itemStyle="padding-left: 15px; padding-right: 15px; height: 85rpx;"></u-tabs>
  9 + </view>
  10 + <scroll-view scroll-y="true" class="product-list" enable-flex="true">
  11 + <view class="flex-box">
  12 + <block v-for="(item, index) in productList[current]" :key="index">
  13 + <view class="product-item">
  14 + <view class="product-image">
  15 + <image :src="item.picUrls[0]" mode='widthFix' />
  16 + </view>
  17 + <view class="product-button">
  18 + <view class="product-text">【{{ item.sellPoint }}】{{ item.name }}</view>
  19 + <view class="product-price-button">
  20 + <text class="product-price">¥
  21 + <text class="price-size">{{ towNumber(item.price) }}</text></text>
  22 + <text class="product-like-ccount">销量 {{ item.likeCount }}</text>
  23 + </view>
  24 + </view>
  25 + </view>
  26 + </block>
  27 + </view>
  28 + </scroll-view>
  29 + </view>
  30 + </view>
  31 +</template>
  32 +
  33 +<script>
  34 + import {
  35 + productSpuPage
  36 + } from '../../api/product';
  37 +
  38 + export default {
  39 + data() {
  40 + return {
  41 + title: "",
  42 + current: 0,
  43 + categoryList: [],
  44 + productList: {}
  45 + }
  46 + },
  47 + onLoad(option) {
  48 + const item = JSON.parse(decodeURIComponent(option.item))
  49 + this.title = item.name
  50 + this.categoryList = item.children
  51 + this.handleProductSpu(option.index);
  52 + },
  53 + methods: {
  54 + changeTabs(item) {
  55 + if (item.index != this.current) {
  56 + this.handleProductSpu(item.index)
  57 + }
  58 + },
  59 + handleProductSpu(index) {
  60 + let param = {}
  61 + param.categoryId = this.categoryList[index].id
  62 + console.log(this.categoryList)
  63 + console.log(index)
  64 + productSpuPage(param).then(res => {
  65 + this.productList[index] = res.data.list
  66 + this.current = index
  67 + })
  68 + },
  69 + towNumber(val) {
  70 + return (val / 100).toFixed(2)
  71 + }
  72 + }
  73 + }
  74 +</script>
  75 +
  76 +<style lang="scss" scoped>
  77 + .context {
  78 + width: 100vw;
  79 + position: fixed;
  80 + top: 160rpx;
  81 + left: 0;
  82 + }
  83 +
  84 + .tabs-top {
  85 + position: relative;
  86 + top: 0;
  87 + width: 100%;
  88 + height: 85rpx;
  89 + }
  90 +
  91 + .product-list {
  92 + position: relative;
  93 + background-color: #f2f2f2;
  94 + height: calc(100vh - 88rpx - 100rpx - var(--status-bar-height));
  95 + width: 100%;
  96 +
  97 + .flex-box {
  98 + width: 730rpx;
  99 + margin: 0 auto;
  100 + @include flex;
  101 + flex-wrap: wrap;
  102 + justify-content: left;
  103 +
  104 + .product-item {
  105 + width: 345rpx;
  106 + height: 450rpx;
  107 + background-color: #ffffff;
  108 + margin: 20rpx 10rpx 0;
  109 + border-radius: 20rpx;
  110 +
  111 + .product-image {
  112 + width: 100%;
  113 + height: 300rpx;
  114 + overflow: hidden;
  115 + border-radius: 20rpx;
  116 + image {
  117 + width: 100%;
  118 + }
  119 + }
  120 +
  121 + .product-button {
  122 + width: 330rpx;
  123 + margin: 15rpx auto 0;
  124 +
  125 + .product-text {
  126 + font-size: 25rpx;
  127 + height: 70rpx;
  128 + overflow: hidden;
  129 + -webkit-line-clamp: 2;
  130 + text-overflow: ellipsis;
  131 + display: -webkit-box;
  132 + -webkit-box-orient: vertical;
  133 + }
  134 +
  135 + .product-price-button {
  136 + font-size: 20rpx;
  137 + margin-top: 20rpx;
  138 +
  139 + .product-price {
  140 + color: red;
  141 +
  142 + .price-size {
  143 + font-size: 26rpx;
  144 + }
  145 + }
  146 +
  147 + .product-like-ccount {
  148 + font-size: 16rpx;
  149 + margin-left: 10rpx;
  150 + }
  151 + }
  152 +
  153 + }
  154 + }
  155 +
  156 + }
  157 +
  158 + // }
  159 +
  160 + }
  161 +</style>
... ...
yudao-ui-app/pages/index/index.vue
... ... @@ -169,8 +169,8 @@ export default {
169 169 }
170 170 },
171 171 onLoad() {
172   - //this.loadBannerData();
173   - //this.loadNoticeData();
  172 + this.loadBannerData();
  173 + this.loadNoticeData();
174 174 },
175 175 methods: {
176 176 loadBannerData() {
... ... @@ -302,7 +302,7 @@ export default {
302 302 margin: 10rpx;
303 303 background: #ffffff;
304 304 border-radius: 10rpx;
305   - box-shadow: -1rpx 1rpx 2rpx #afd3f5, 1rpx 1rpx 0rpx #afd3f5;
  305 + box-shadow: 0rpx 6rpx 8rpx rgba(58,134,185,0.2);
306 306 .prod-image {
307 307 width: 224rpx;
308 308 height: 224rpx;
... ...
yudao-ui-app/pages/product/product.vue
... ... @@ -43,7 +43,7 @@
43 43 <u-popup :show="skuPopup" :round="10" :closeable="true" :closeOnClickOverlay="false" @close="skuPopup = false">
44 44 <view class="sku-popup-slot">
45 45 <view class="current-sku-info">
46   - <u--image class="current-sku-img" :showLoading="true" :src="product.sku[currentSkuIndex].image" width="120rpx" height="120rpx"></u--image>
  46 + <u--image class="current-sku-img" :showLoading="true" :src="product.sku[currentSkuIndex].picUrl" width="120rpx" height="120rpx"></u--image>
47 47 <view class="current-sku-desc">
48 48 <view class="name">{{ product.sku[currentSkuIndex].desc }}</view>
49 49 <custom-text-price color="red" size="12" intSize="18" :price="product.sku[currentSkuIndex].price"></custom-text-price>
... ... @@ -178,6 +178,8 @@
178 178 </template>
179 179  
180 180 <script>
  181 + import { productSpu } from '../../api/product';
  182 +
181 183 export default {
182 184 data() {
183 185 return {
... ... @@ -194,19 +196,19 @@ export default {
194 196 sku: [
195 197 {
196 198 id: 0,
197   - image: 'https://cdn.uviewui.com/uview/album/1.jpg',
  199 + picUrl: 'https://cdn.uviewui.com/uview/album/1.jpg',
198 200 price: 13.0,
199 201 desc: '山不在高,有仙则名。'
200 202 },
201 203 {
202 204 id: 1,
203   - image: 'https://cdn.uviewui.com/uview/album/2.jpg',
  205 + picUrl: 'https://cdn.uviewui.com/uview/album/2.jpg',
204 206 price: 11.0,
205 207 desc: '水不在深,有龙则灵。'
206 208 },
207 209 {
208 210 id: 2,
209   - image: 'https://cdn.uviewui.com/uview/album/3.jpg',
  211 + picUrl: 'https://cdn.uviewui.com/uview/album/3.jpg',
210 212 price: 10.0,
211 213 desc: '斯是陋室,惟吾德馨。'
212 214 }
... ... @@ -302,7 +304,18 @@ export default {
302 304 }
303 305 },
304 306 methods: {
305   - loadProductData() {},
  307 + loadProductData() {
  308 + let param = {}
  309 + param.spuId = this.product.id
  310 + productSpu(param).then(res => {
  311 + this.product.images = res.data.picUrls;
  312 + this.product.sku = res.data.skus;
  313 + this.product.desc = res.data.description.replace(/<[^>]*>/g,'');
  314 + this.product.price = res.data.price;
  315 + this.product.title = res.data.name;
  316 + console.log(res)
  317 + })
  318 + },
306 319 handleSkuItemClick(index) {
307 320 this.currentSkuIndex = index
308 321 },
... ...
yudao-ui-app/utils/tree.js 0 → 100644
  1 +/**
  2 + * 构造树型结构数据
  3 + * @param {*} data 数据源
  4 + * @param {*} id id字段 默认 'id'
  5 + * @param {*} parentId 父节点字段 默认 'parentId'
  6 + * @param {*} children 孩子节点字段 默认 'children'
  7 + * @param {*} rootId 根Id 默认 0
  8 + */
  9 +export function handleTree(data, id, parentId, children, rootId) {
  10 + id = id || 'id'
  11 + parentId = parentId || 'parentId'
  12 + children = children || 'children'
  13 + rootId = rootId || Math.min.apply(Math, data.map(item => {
  14 + return item[parentId]
  15 + })) || 0
  16 + //对源数据深度克隆
  17 + const cloneData = JSON.parse(JSON.stringify(data))
  18 + //循环所有项
  19 + const treeData = cloneData.filter(father => {
  20 + let branchArr = cloneData.filter(child => {
  21 + //返回每一项的子级数组
  22 + return father[id] === child[parentId]
  23 + });
  24 + branchArr.length > 0 ? father.children = branchArr : '';
  25 + //返回第一层
  26 + return father[parentId] === rootId;
  27 + });
  28 + return treeData !== '' ? treeData : data;
  29 +}
  30 +
  31 +/**
  32 + * 树形结构进行删除深度不够的分支
  33 + * 目前只删除了不够三层的分支
  34 + * 对于高于三层的部分目前不做处理
  35 + * todo 暴力遍历,可用递归修改
  36 + * @param {*} data 树形结构
  37 + */
  38 +export function convertTree(data) {
  39 + //对源数据深度克隆
  40 + const cloneData = JSON.parse(JSON.stringify(data))
  41 + // 遍历克隆数据,对源数据进行删除操作
  42 + for (let first = 0; first < cloneData.length; first++) {
  43 + for (let second = 0; second < cloneData[first].children.length; second++) {
  44 + for (let three = 0; three < cloneData[first].children[second].children.length; three++) {
  45 + if (data[first].children[second].children[three].children == undefined ||
  46 + data[first].children[second].children[three].children === 0) {
  47 + data[first].children[second].children.splice(second, 1);
  48 + }
  49 + }
  50 + if (data[first].children[second].children == undefined || data[first].children[second].children === 0) {
  51 + data[first].children.splice(second, 1);
  52 + }
  53 + }
  54 + if (data[first].children == undefined || data[first].children.length === 0) {
  55 + data.splice(first, 1);
  56 + }
  57 + }
  58 + return data;
  59 +}
... ...