Commit f07bf8b0fd5bf043a60b636b41d1dde0aa4be17e

Authored by franky
1 parent 6dca8e08

spu保存校验添加

yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java
@@ -28,4 +28,5 @@ public interface ErrorCodeConstants { @@ -28,4 +28,5 @@ public interface ErrorCodeConstants {
28 28
29 // ========== 商品sku 1008006000 ========== 29 // ========== 商品sku 1008006000 ==========
30 ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品sku不存在"); 30 ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品sku不存在");
  31 + ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品sku的属性组合存在重复");
31 } 32 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java
@@ -45,7 +45,9 @@ public class ProductSkuBaseVO { @@ -45,7 +45,9 @@ public class ProductSkuBaseVO {
45 45
46 @Data 46 @Data
47 public static class Property { 47 public static class Property {
  48 + @NotNull(message = "规格属性名id不能为空")
48 private Integer propertyId; 49 private Integer propertyId;
  50 + @NotNull(message = "规格属性值id不能为空")
49 private Integer valueId; 51 private Integer valueId;
50 } 52 }
51 53
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java
@@ -37,6 +37,8 @@ public interface ProductSkuConvert { @@ -37,6 +37,8 @@ public interface ProductSkuConvert {
37 37
38 List<ProductSkuRespVO> convertList(List<ProductSkuDO> list); 38 List<ProductSkuRespVO> convertList(List<ProductSkuDO> list);
39 39
  40 + List<ProductSkuDO> convertSkuDOList(List<ProductSkuCreateReqVO> list);
  41 +
40 PageResult<ProductSkuRespVO> convertPage(PageResult<ProductSkuDO> page); 42 PageResult<ProductSkuRespVO> convertPage(PageResult<ProductSkuDO> page);
41 43
42 List<ProductSkuExcelVO> convertList02(List<ProductSkuDO> list); 44 List<ProductSkuExcelVO> convertList02(List<ProductSkuDO> list);
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java
@@ -76,4 +76,10 @@ public interface ProductPropertyService { @@ -76,4 +76,10 @@ public interface ProductPropertyService {
76 76
77 ProductPropertyRespVO getPropertyResp(Long id); 77 ProductPropertyRespVO getPropertyResp(Long id);
78 78
  79 + /**
  80 + * 根据数据名id集合查询属性名以及属性值的集合
  81 + * @param propertyIds 属性名id集合
  82 + * @return
  83 + */
  84 + List<ProductPropertyRespVO> selectByIds(List<Integer> propertyIds);
79 } 85 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java
1 package cn.iocoder.yudao.module.product.service.property; 1 package cn.iocoder.yudao.module.product.service.property;
2 2
  3 +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.product.controller.admin.property.vo.*; 5 import cn.iocoder.yudao.module.product.controller.admin.property.vo.*;
5 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyCreateReqVO; 6 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyCreateReqVO;
@@ -113,13 +114,13 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { @@ -113,13 +114,13 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
113 //获取属性列表 114 //获取属性列表
114 PageResult<ProductPropertyDO> pageResult = productPropertyMapper.selectPage(pageReqVO); 115 PageResult<ProductPropertyDO> pageResult = productPropertyMapper.selectPage(pageReqVO);
115 PageResult<ProductPropertyRespVO> propertyRespVOPageResult = ProductPropertyConvert.INSTANCE.convertPage(pageResult); 116 PageResult<ProductPropertyRespVO> propertyRespVOPageResult = ProductPropertyConvert.INSTANCE.convertPage(pageResult);
116 - List<Long> propertyIds = propertyRespVOPageResult.getList().stream().map(x -> x.getId()).collect(Collectors.toList()); 117 + List<Long> propertyIds = propertyRespVOPageResult.getList().stream().map(ProductPropertyRespVO::getId).collect(Collectors.toList());
117 118
118 //获取属性值列表 119 //获取属性值列表
119 List<ProductPropertyValueDO> productPropertyValueDOList = productPropertyValueService.getPropertyValueListByPropertyId(propertyIds); 120 List<ProductPropertyValueDO> productPropertyValueDOList = productPropertyValueService.getPropertyValueListByPropertyId(propertyIds);
120 List<ProductPropertyValueRespVO> propertyValueRespVOList = ProductPropertyValueConvert.INSTANCE.convertList(productPropertyValueDOList); 121 List<ProductPropertyValueRespVO> propertyValueRespVOList = ProductPropertyValueConvert.INSTANCE.convertList(productPropertyValueDOList);
121 //组装一对多 122 //组装一对多
122 - propertyRespVOPageResult.getList().stream().forEach(x->{ 123 + propertyRespVOPageResult.getList().forEach(x->{
123 Long propertyId = x.getId(); 124 Long propertyId = x.getId();
124 List<ProductPropertyValueRespVO> valueDOList = propertyValueRespVOList.stream().filter(v -> v.getPropertyId().equals(propertyId)).collect(Collectors.toList()); 125 List<ProductPropertyValueRespVO> valueDOList = propertyValueRespVOList.stream().filter(v -> v.getPropertyId().equals(propertyId)).collect(Collectors.toList());
125 x.setPropertyValueList(valueDOList); 126 x.setPropertyValueList(valueDOList);
@@ -140,4 +141,8 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { @@ -140,4 +141,8 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
140 return propertyRespVO; 141 return propertyRespVO;
141 } 142 }
142 143
  144 + @Override
  145 + public List<ProductPropertyRespVO> selectByIds(List<Integer> propertyIds) {
  146 + return ProductPropertyConvert.INSTANCE.convertList(productPropertyMapper.selectBatchIds(propertyIds));
  147 + }
143 } 148 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java
@@ -72,4 +72,11 @@ public interface ProductSkuService { @@ -72,4 +72,11 @@ public interface ProductSkuService {
72 * @param skuCreateReqList sku组合的集合 72 * @param skuCreateReqList sku组合的集合
73 */ 73 */
74 void validatedSkuReq(List<ProductSkuCreateReqVO> skuCreateReqList); 74 void validatedSkuReq(List<ProductSkuCreateReqVO> skuCreateReqList);
  75 +
  76 + /**
  77 + * 批量保存sku
  78 + * @param skuDOList sku对象集合
  79 + * @return
  80 + */
  81 + void batchSave(List<ProductSkuDO> skuDOList);
75 } 82 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
1 package cn.iocoder.yudao.module.product.service.sku; 1 package cn.iocoder.yudao.module.product.service.sku;
2 2
  3 +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO;
  6 +import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO;
  7 +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.*;
  8 +import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
  9 +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
  10 +import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;
  11 +import cn.iocoder.yudao.module.product.enums.ErrorCodeConstants;
  12 +import cn.iocoder.yudao.module.product.service.property.ProductPropertyService;
3 import org.springframework.stereotype.Service; 13 import org.springframework.stereotype.Service;
4 -import javax.annotation.Resource;  
5 import org.springframework.validation.annotation.Validated; 14 import org.springframework.validation.annotation.Validated;
6 15
  16 +import javax.annotation.Resource;
7 import java.util.*; 17 import java.util.*;
8 import java.util.stream.Collectors; 18 import java.util.stream.Collectors;
9 19
10 -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.*;  
11 -import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;  
12 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
13 -  
14 -import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;  
15 -import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;  
16 -  
17 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 20 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
18 -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; 21 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_NOT_EXISTS;
  22 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
19 23
20 /** 24 /**
21 * 商品sku Service 实现类 25 * 商品sku Service 实现类
@@ -27,13 +31,16 @@ import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; @@ -27,13 +31,16 @@ import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
27 public class ProductSkuServiceImpl implements ProductSkuService { 31 public class ProductSkuServiceImpl implements ProductSkuService {
28 32
29 @Resource 33 @Resource
30 - private ProductSkuMapper ProductSkuMapper; 34 + private ProductSkuMapper productSkuMapper;
  35 +
  36 + @Resource
  37 + private ProductPropertyService productPropertyService;
31 38
32 @Override 39 @Override
33 public Long createSku(ProductSkuCreateReqVO createReqVO) { 40 public Long createSku(ProductSkuCreateReqVO createReqVO) {
34 // 插入 41 // 插入
35 ProductSkuDO sku = ProductSkuConvert.INSTANCE.convert(createReqVO); 42 ProductSkuDO sku = ProductSkuConvert.INSTANCE.convert(createReqVO);
36 - ProductSkuMapper.insert(sku); 43 + productSkuMapper.insert(sku);
37 // 返回 44 // 返回
38 return sku.getId(); 45 return sku.getId();
39 } 46 }
@@ -44,7 +51,7 @@ public class ProductSkuServiceImpl implements ProductSkuService { @@ -44,7 +51,7 @@ public class ProductSkuServiceImpl implements ProductSkuService {
44 this.validateSkuExists(updateReqVO.getId()); 51 this.validateSkuExists(updateReqVO.getId());
45 // 更新 52 // 更新
46 ProductSkuDO updateObj = ProductSkuConvert.INSTANCE.convert(updateReqVO); 53 ProductSkuDO updateObj = ProductSkuConvert.INSTANCE.convert(updateReqVO);
47 - ProductSkuMapper.updateById(updateObj); 54 + productSkuMapper.updateById(updateObj);
48 } 55 }
49 56
50 @Override 57 @Override
@@ -52,40 +59,67 @@ public class ProductSkuServiceImpl implements ProductSkuService { @@ -52,40 +59,67 @@ public class ProductSkuServiceImpl implements ProductSkuService {
52 // 校验存在 59 // 校验存在
53 this.validateSkuExists(id); 60 this.validateSkuExists(id);
54 // 删除 61 // 删除
55 - ProductSkuMapper.deleteById(id); 62 + productSkuMapper.deleteById(id);
56 } 63 }
57 64
58 private void validateSkuExists(Long id) { 65 private void validateSkuExists(Long id) {
59 - if (ProductSkuMapper.selectById(id) == null) { 66 + if (productSkuMapper.selectById(id) == null) {
60 throw exception(SKU_NOT_EXISTS); 67 throw exception(SKU_NOT_EXISTS);
61 } 68 }
62 } 69 }
63 70
64 @Override 71 @Override
65 public ProductSkuDO getSku(Long id) { 72 public ProductSkuDO getSku(Long id) {
66 - return ProductSkuMapper.selectById(id); 73 + return productSkuMapper.selectById(id);
67 } 74 }
68 75
69 @Override 76 @Override
70 public List<ProductSkuDO> getSkuList(Collection<Long> ids) { 77 public List<ProductSkuDO> getSkuList(Collection<Long> ids) {
71 - return ProductSkuMapper.selectBatchIds(ids); 78 + return productSkuMapper.selectBatchIds(ids);
72 } 79 }
73 80
74 @Override 81 @Override
75 public PageResult<ProductSkuDO> getSkuPage(ProductSkuPageReqVO pageReqVO) { 82 public PageResult<ProductSkuDO> getSkuPage(ProductSkuPageReqVO pageReqVO) {
76 - return ProductSkuMapper.selectPage(pageReqVO); 83 + return productSkuMapper.selectPage(pageReqVO);
77 } 84 }
78 85
79 @Override 86 @Override
80 public List<ProductSkuDO> getSkuList(ProductSkuExportReqVO exportReqVO) { 87 public List<ProductSkuDO> getSkuList(ProductSkuExportReqVO exportReqVO) {
81 - return ProductSkuMapper.selectList(exportReqVO); 88 + return productSkuMapper.selectList(exportReqVO);
82 } 89 }
83 90
84 @Override 91 @Override
85 public void validatedSkuReq(List<ProductSkuCreateReqVO> skuCreateReqList) { 92 public void validatedSkuReq(List<ProductSkuCreateReqVO> skuCreateReqList) {
  93 + List<ProductSkuBaseVO.Property> skuPropertyList = skuCreateReqList.stream().flatMap(p -> p.getProperties().stream()).collect(Collectors.toList());
86 // 校验规格属性以及规格值是否存在 94 // 校验规格属性以及规格值是否存在
87 - List<Integer> propertyIds = skuCreateReqList.stream().flatMap(p -> p.getProperties().stream()).map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toList());  
88 - 95 + List<Integer> propertyIds = skuPropertyList.stream().map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toList());
  96 + List<ProductPropertyRespVO> propertyAndValueList = productPropertyService.selectByIds(propertyIds);
  97 + if (propertyAndValueList.isEmpty())
  98 + throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS);
  99 + Map<Long, ProductPropertyRespVO> propertyMap = propertyAndValueList.stream().collect(Collectors.toMap(ProductPropertyRespVO::getId, p -> p));
  100 + skuPropertyList.forEach(p -> {
  101 + ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId());
  102 + // 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示
  103 + if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty())
  104 + throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS);
  105 + // 判断改属性名对应的属性值是否存在,不存在,给出提示
  106 + if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) {
  107 + throw ServiceExceptionUtil.exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS);
  108 + }
  109 + });
89 // 校验是否有重复的sku组合 110 // 校验是否有重复的sku组合
  111 + List<List<ProductSkuBaseVO.Property>> skuProperties = skuCreateReqList.stream().map(ProductSkuBaseVO::getProperties).collect(Collectors.toList());
  112 + Set<String> skuPropertiesConvertSet = new HashSet<>();
  113 + skuProperties.forEach(p -> {
  114 + // 组合属性值id为 1~2~3.... 形式的字符串,通过set的特性判断是否有重复的组合
  115 + if (!skuPropertiesConvertSet.add(p.stream().map(pr -> String.valueOf(pr.getValueId())).sorted().collect(Collectors.joining("~")))) {
  116 + throw ServiceExceptionUtil.exception(ErrorCodeConstants.SKU_PROPERTIES_DUPLICATED);
  117 + }
  118 + });
  119 + }
  120 +
  121 + @Override
  122 + public void batchSave(List<ProductSkuDO> skuDOList) {
  123 + productSkuMapper.insertBatch(skuDOList);
90 } 124 }
91 } 125 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
1 package cn.iocoder.yudao.module.product.service.spu; 1 package cn.iocoder.yudao.module.product.service.spu;
2 2
3 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO; 3 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO;
  4 +import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
  5 +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
4 import cn.iocoder.yudao.module.product.service.category.CategoryService; 6 import cn.iocoder.yudao.module.product.service.category.CategoryService;
5 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; 7 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
6 import org.springframework.stereotype.Service; 8 import org.springframework.stereotype.Service;
@@ -11,6 +13,8 @@ import org.springframework.transaction.annotation.Transactional; @@ -11,6 +13,8 @@ import org.springframework.transaction.annotation.Transactional;
11 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
12 14
13 import java.util.*; 15 import java.util.*;
  16 +import java.util.stream.Collectors;
  17 +
14 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; 18 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
15 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; 19 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
16 import cn.iocoder.yudao.framework.common.pojo.PageResult; 20 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -47,12 +51,16 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -47,12 +51,16 @@ public class ProductSpuServiceImpl implements ProductSpuService {
47 // 校验SKU 51 // 校验SKU
48 List<ProductSkuCreateReqVO> skuCreateReqList = createReqVO.getProductSkuCreateReqVOS(); 52 List<ProductSkuCreateReqVO> skuCreateReqList = createReqVO.getProductSkuCreateReqVOS();
49 productSkuService.validatedSkuReq(skuCreateReqList); 53 productSkuService.validatedSkuReq(skuCreateReqList);
50 - // 插入 54 + // 插入SPU
51 ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO); 55 ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO);
  56 + ProductSpuMapper.insert(spu);
  57 + // sku关联SPU属性
52 skuCreateReqList.forEach(p -> { 58 skuCreateReqList.forEach(p -> {
53 p.setSpuId(spu.getId()); 59 p.setSpuId(spu.getId());
54 }); 60 });
55 - ProductSpuMapper.insert(spu); 61 + List<ProductSkuDO> skuDOList = ProductSkuConvert.INSTANCE.convertSkuDOList(skuCreateReqList);
  62 + // 批量插入sku
  63 + productSkuService.batchSave(skuDOList);
56 // 返回 64 // 返回
57 return spu.getId(); 65 return spu.getId();
58 } 66 }
yudao-ui-admin/src/views/mall/product/spu/index.vue
@@ -20,14 +20,14 @@ @@ -20,14 +20,14 @@
20 </el-form-item> 20 </el-form-item>
21 <el-form-item label="状态" prop="status"> 21 <el-form-item label="状态" prop="status">
22 <el-select v-model="queryParams.status" placeholder="请选择上下架状态" clearable size="small"> 22 <el-select v-model="queryParams.status" placeholder="请选择上下架状态" clearable size="small">
23 - <el-option label="请选择字典生成" value="" />  
24 - <el-option label="上架" value="0" />  
25 - <el-option label="下架" value="1" /> 23 + <el-option label="请选择字典生成" value=""/>
  24 + <el-option label="上架" value="0"/>
  25 + <el-option label="下架" value="1"/>
26 </el-select> 26 </el-select>
27 </el-form-item> 27 </el-form-item>
28 <el-form-item label="创建时间"> 28 <el-form-item label="创建时间">
29 <el-date-picker v-model="dateRangeCreateTime" style="width: 240px" value-format="yyyy-MM-dd" 29 <el-date-picker v-model="dateRangeCreateTime" style="width: 240px" value-format="yyyy-MM-dd"
30 - type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> 30 + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"/>
31 </el-form-item> 31 </el-form-item>
32 <el-form-item> 32 <el-form-item>
33 <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button> 33 <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
@@ -39,28 +39,31 @@ @@ -39,28 +39,31 @@
39 <el-row :gutter="10" class="mb8"> 39 <el-row :gutter="10" class="mb8">
40 <el-col :span="1.5"> 40 <el-col :span="1.5">
41 <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" 41 <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
42 - v-hasPermi="['product:spu:create']">新增</el-button> 42 + v-hasPermi="['product:spu:create']">新增
  43 + </el-button>
43 </el-col> 44 </el-col>
44 <el-col :span="1.5"> 45 <el-col :span="1.5">
45 - <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"  
46 - v-hasPermi="['product:spu:export']">导出</el-button> 46 + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
  47 + :loading="exportLoading"
  48 + v-hasPermi="['product:spu:export']">导出
  49 + </el-button>
47 </el-col> 50 </el-col>
48 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> 51 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
49 </el-row> 52 </el-row>
50 53
51 <!-- 列表 --> 54 <!-- 列表 -->
52 <el-table v-loading="loading" :data="list"> 55 <el-table v-loading="loading" :data="list">
53 - <el-table-column label="主键" align="center" prop="id" />  
54 - <el-table-column label="商品名称" align="center" prop="name" />  
55 - <el-table-column label="卖点" align="center" prop="sellPoint" />  
56 - <el-table-column label="描述" align="center" prop="description" />  
57 - <el-table-column label="分类id" align="center" prop="categoryId" />  
58 - <el-table-column label="商品主图地址" align="center" prop="picUrls" />  
59 - <el-table-column label="排序字段" align="center" prop="sort" />  
60 - <el-table-column label="点赞初始人数" align="center" prop="likeCount" />  
61 - <el-table-column label="价格 (分)" align="center" prop="price" />  
62 - <el-table-column label="库存数量" align="center" prop="quantity" />  
63 - <el-table-column label="状态" align="center" prop="status" /> 56 + <el-table-column label="主键" align="center" prop="id"/>
  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"/>
  60 + <el-table-column label="分类id" align="center" prop="categoryId"/>
  61 + <el-table-column label="商品主图地址" align="center" prop="picUrls"/>
  62 + <el-table-column label="排序字段" align="center" prop="sort"/>
  63 + <el-table-column label="点赞初始人数" align="center" prop="likeCount"/>
  64 + <el-table-column label="价格 (分)" align="center" prop="price"/>
  65 + <el-table-column label="库存数量" align="center" prop="quantity"/>
  66 + <el-table-column label="状态" align="center" prop="status"/>
64 <el-table-column label="创建时间" align="center" prop="createTime" width="180"> 67 <el-table-column label="创建时间" align="center" prop="createTime" width="180">
65 <template slot-scope="scope"> 68 <template slot-scope="scope">
66 <span>{{ parseTime(scope.row.createTime) }}</span> 69 <span>{{ parseTime(scope.row.createTime) }}</span>
@@ -69,9 +72,11 @@ @@ -69,9 +72,11 @@
69 <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> 72 <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
70 <template slot-scope="scope"> 73 <template slot-scope="scope">
71 <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" 74 <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
72 - v-hasPermi="['product:spu:update']">修改</el-button> 75 + v-hasPermi="['product:spu:update']">修改
  76 + </el-button>
73 <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" 77 <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
74 - v-hasPermi="['product:spu:delete']">删除</el-button> 78 + v-hasPermi="['product:spu:delete']">删除
  79 + </el-button>
75 </template> 80 </template>
76 </el-table-column> 81 </el-table-column>
77 </el-table> 82 </el-table>
@@ -83,31 +88,91 @@ @@ -83,31 +88,91 @@
83 <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body> 88 <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
84 <el-form ref="form" :model="form" :rules="rules" label-width="80px"> 89 <el-form ref="form" :model="form" :rules="rules" label-width="80px">
85 <el-form-item label="商品名称" prop="name"> 90 <el-form-item label="商品名称" prop="name">
86 - <el-input v-model="form.name" placeholder="请输入商品名称" /> 91 + <el-input v-model="form.name" placeholder="请输入商品名称"/>
87 </el-form-item> 92 </el-form-item>
88 <el-form-item label="卖点" prop="sellPoint"> 93 <el-form-item label="卖点" prop="sellPoint">
89 - <el-input v-model="form.sellPoint" placeholder="请输入卖点" /> 94 + <el-input v-model="form.sellPoint" placeholder="请输入卖点"/>
90 </el-form-item> 95 </el-form-item>
91 <el-form-item label="描述"> 96 <el-form-item label="描述">
92 <editor v-model="form.description" :min-height="192"/> 97 <editor v-model="form.description" :min-height="192"/>
93 </el-form-item> 98 </el-form-item>
94 - <el-form-item label="分类id" prop="categoryId">  
95 - <el-input v-model="form.categoryId" placeholder="请输入分类id" /> 99 + <el-form-item label="分类id" prop="categoryIds">
  100 + <el-cascader
  101 + v-model="form.categoryIds"
  102 + placeholder="请输入分类id"
  103 + style="width: 100%"
  104 + :options="categoryList"
  105 + :props="propName"
  106 + clearable></el-cascader>
96 </el-form-item> 107 </el-form-item>
97 <el-form-item label="商品主图地址" prop="picUrls"> 108 <el-form-item label="商品主图地址" prop="picUrls">
98 - <ImageUpload v-model="form.picUrls" :limit="1"/> 109 + <ImageUpload v-model="form.picUrls" :limit="10"/>
99 </el-form-item> 110 </el-form-item>
  111 + <el-form-item label="商品规格">
  112 + <el-button size="mini" @click="shopTagInput()">添加规格</el-button>
  113 + <div v-for="(tag, tagIndex) in skuTags" :key="tagIndex">
  114 + <span>{{tag.tagName}}</span>
  115 + <el-button class="button-new-tag" type="text" icon="el-icon-delete" @click="removeTag(tagIndex)">删除</el-button>
  116 + <br/>
  117 + <el-tag
  118 + v-for="(tagItem, tagItemIndex) in tag.tagItems"
  119 + :key="tagItem.valueId"
  120 + closable
  121 + :disable-transitions="false"
  122 + @close="handleTagClose(tagIndex, tagItemIndex)">
  123 + {{tagItem.propValue}}
  124 + </el-tag>
  125 + <el-input
  126 + class="input-new-tag"
  127 + v-if="tagItemInputs[tagIndex] && tagItemInputs[tagIndex].visible"
  128 + v-model="tagItemInputs[tagIndex].value"
  129 + :ref="`saveTagInput${tagIndex}`"
  130 + size="small"
  131 + @keyup.enter.native="handleInputConfirm(tagIndex)"
  132 + @blur="handleInputConfirm(tagIndex)">
  133 + </el-input>
  134 + </div>
  135 + </el-form-item>
  136 + <el-form-item label="规格名" v-show="isShowTagInput">
  137 + <el-col :span="8">
  138 + <el-select v-model="addTagInput.name" filterable allow-create default-first-option placeholder="请选择" @change="handleTagClick">
  139 + <el-option
  140 + v-for="item in unUseTags"
  141 + :key="item.id"
  142 + :label="item.name"
  143 + :value="item.name">
  144 + </el-option>
  145 + </el-select>
  146 + </el-col>
  147 + </el-form-item>
  148 + <el-form-item label="规格值" v-show="isShowTagInput">
  149 + <el-col :span="8">
  150 + <el-select v-model="addTagInput.selectValues" multiple filterable allow-create default-first-option placeholder="请选择">
  151 + <el-option
  152 + v-for="item in dbTagValues"
  153 + :key="item.valueId"
  154 + :label="item.propValue"
  155 + :value="item.propValue">
  156 + </el-option>
  157 + </el-select>
  158 + </el-col>
  159 + </el-form-item>
  160 + <el-form-item>
  161 + <el-button size="mini" type="primary" @click="addTag()" v-show="isShowTagInput">确定</el-button>
  162 + <el-button size="mini" @click="hideTagInput()" v-show="isShowTagInput">取消</el-button>
  163 + </el-form-item>
  164 +
100 <el-form-item label="排序字段" prop="sort"> 165 <el-form-item label="排序字段" prop="sort">
101 - <el-input v-model="form.sort" placeholder="请输入排序字段" /> 166 + <el-input v-model="form.sort" placeholder="请输入排序字段"/>
102 </el-form-item> 167 </el-form-item>
103 <el-form-item label="点赞初始人数" prop="likeCount"> 168 <el-form-item label="点赞初始人数" prop="likeCount">
104 - <el-input v-model="form.likeCount" placeholder="请输入点赞初始人数" /> 169 + <el-input v-model="form.likeCount" placeholder="请输入点赞初始人数"/>
105 </el-form-item> 170 </el-form-item>
106 <el-form-item label="价格 单位使用:分" prop="price"> 171 <el-form-item label="价格 单位使用:分" prop="price">
107 - <el-input v-model="form.price" placeholder="请输入价格 单位使用:分" /> 172 + <el-input v-model="form.price" placeholder="请输入价格 单位使用:分"/>
108 </el-form-item> 173 </el-form-item>
109 <el-form-item label="库存数量" prop="quantity"> 174 <el-form-item label="库存数量" prop="quantity">
110 - <el-input v-model="form.quantity" placeholder="请输入库存数量" /> 175 + <el-input v-model="form.quantity" placeholder="请输入库存数量"/>
111 </el-form-item> 176 </el-form-item>
112 <el-form-item label="上下架状态" prop="status"> 177 <el-form-item label="上下架状态" prop="status">
113 <el-radio-group v-model="form.status"> 178 <el-radio-group v-model="form.status">
@@ -125,173 +190,242 @@ @@ -125,173 +190,242 @@
125 </template> 190 </template>
126 191
127 <script> 192 <script>
128 -import { createSpu, updateSpu, deleteSpu, getSpu, getSpuPage, exportSpuExcel } from "@/api/mall/product/spu";  
129 -import Editor from '@/components/Editor'; 193 + import {createSpu, updateSpu, deleteSpu, getSpu, getSpuPage, exportSpuExcel} from "@/api/mall/product/spu";
  194 + import {
  195 + createCategory,
  196 + deleteCategory,
  197 + exportCategoryExcel,
  198 + getCategory,
  199 + listCategory,
  200 + updateCategory
  201 + } from "@/api/mall/product/category";
  202 + import { createProperty, updateProperty, deleteProperty, getProperty, getPropertyPage, exportPropertyExcel } from "@/api/mall/product/property";
130 203
131 -export default {  
132 - name: "Spu",  
133 - components: {  
134 - Editor,  
135 - },  
136 - data() {  
137 - return {  
138 - // 遮罩层  
139 - loading: true,  
140 - // 导出遮罩层  
141 - exportLoading: false,  
142 - // 显示搜索条件  
143 - showSearch: true,  
144 - // 总条数  
145 - total: 0,  
146 - // 商品spu列表  
147 - list: [],  
148 - // 弹出层标题  
149 - title: "",  
150 - // 是否显示弹出层  
151 - open: false,  
152 - dateRangeCreateTime: [],  
153 - // 查询参数  
154 - queryParams: {  
155 - pageNo: 1,  
156 - pageSize: 10,  
157 - name: null,  
158 - sellPoint: null,  
159 - description: null,  
160 - categoryId: null,  
161 - picUrls: null,  
162 - sort: null,  
163 - likeCount: null,  
164 - price: null,  
165 - quantity: null,  
166 - status: null,  
167 - },  
168 - // 表单参数  
169 - form: {},  
170 - // 表单校验  
171 - rules: {  
172 - sellPoint: [{ required: true, message: "卖点不能为空", trigger: "blur" }],  
173 - description: [{ required: true, message: "描述不能为空", trigger: "blur" }],  
174 - categoryId: [{ required: true, message: "分类id不能为空", trigger: "blur" }],  
175 - picUrls: [{ required: true, message: "商品主图地址", trigger: "blur" }],  
176 - sort: [{ required: true, message: "排序字段不能为空", trigger: "blur" }],  
177 - }  
178 - };  
179 - },  
180 - created() {  
181 - this.getList();  
182 - },  
183 - methods: {  
184 - /** 查询列表 */  
185 - getList() {  
186 - this.loading = true;  
187 - // 处理查询参数  
188 - let params = {...this.queryParams};  
189 - this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');  
190 - // 执行查询  
191 - getSpuPage(params).then(response => {  
192 - this.list = response.data.list;  
193 - this.total = response.data.total;  
194 - this.loading = false;  
195 - });  
196 - },  
197 - /** 取消按钮 */  
198 - cancel() {  
199 - this.open = false;  
200 - this.reset();  
201 - },  
202 - /** 表单重置 */  
203 - reset() {  
204 - this.form = {  
205 - id: undefined,  
206 - name: undefined,  
207 - sellPoint: undefined,  
208 - description: undefined,  
209 - categoryId: undefined,  
210 - picUrls: undefined,  
211 - sort: undefined,  
212 - likeCount: undefined,  
213 - price: undefined,  
214 - quantity: undefined,  
215 - status: undefined,  
216 - };  
217 - this.resetForm("form");  
218 - },  
219 - /** 搜索按钮操作 */  
220 - handleQuery() {  
221 - this.queryParams.pageNo = 1;  
222 - this.getList();  
223 - },  
224 - /** 重置按钮操作 */  
225 - resetQuery() {  
226 - this.dateRangeCreateTime = [];  
227 - this.resetForm("queryForm");  
228 - this.handleQuery();  
229 - },  
230 - /** 新增按钮操作 */  
231 - handleAdd() {  
232 - this.reset();  
233 - this.open = true;  
234 - this.title = "添加商品spu";  
235 - },  
236 - /** 修改按钮操作 */  
237 - handleUpdate(row) {  
238 - this.reset();  
239 - const id = row.id;  
240 - getSpu(id).then(response => {  
241 - this.form = response.data;  
242 - this.open = true;  
243 - this.title = "修改商品spu";  
244 - });  
245 - },  
246 - /** 提交按钮 */  
247 - submitForm() {  
248 - this.$refs["form"].validate(valid => {  
249 - if (!valid) {  
250 - return;  
251 - }  
252 - // 修改的提交  
253 - if (this.form.id != null) {  
254 - updateSpu(this.form).then(response => {  
255 - this.$modal.msgSuccess("修改成功");  
256 - this.open = false; 204 + import Editor from '@/components/Editor';
  205 + import ImageUpload from '@/components/ImageUpload';
  206 + export default {
  207 + name: "Spu",
  208 + components: {
  209 + Editor,ImageUpload
  210 + },
  211 + data() {
  212 + return {
  213 + dbTagValues:[],
  214 + unUseTags:[],
  215 + propertyPageList:[],
  216 + isShowTagInput: false,
  217 + addTagInput: {
  218 + name: '',
  219 + selectValues: []
  220 + },
  221 + skuTags:[],
  222 + propName: {
  223 + checkStrictly: true,
  224 + label:'name',
  225 + value:'id'
  226 + },
  227 + categoryList: [],
  228 + // 遮罩层
  229 + loading: true,
  230 + // 导出遮罩层
  231 + exportLoading: false,
  232 + // 显示搜索条件
  233 + showSearch: true,
  234 + // 总条数
  235 + total: 0,
  236 + // 商品spu列表
  237 + list: [],
  238 + // 弹出层标题
  239 + title: "",
  240 + // 是否显示弹出层
  241 + open: false,
  242 + dateRangeCreateTime: [],
  243 + // 查询参数
  244 + queryParams: {
  245 + pageNo: 1,
  246 + pageSize: 10,
  247 + name: null,
  248 + sellPoint: null,
  249 + description: null,
  250 + categoryId: null,
  251 + picUrls: null,
  252 + sort: null,
  253 + likeCount: null,
  254 + price: null,
  255 + quantity: null,
  256 + status: null,
  257 + },
  258 + // 表单参数
  259 + form: {},
  260 + // 表单校验
  261 + rules: {
  262 + sellPoint: [{required: true, message: "卖点不能为空", trigger: "blur"}],
  263 + description: [{required: true, message: "描述不能为空", trigger: "blur"}],
  264 + categoryIds: [{required: true, message: "分类id不能为空", trigger: "blur"}],
  265 + picUrls: [{required: true, message: "商品主图地址", trigger: "blur"}],
  266 + sort: [{required: true, message: "排序字段不能为空", trigger: "blur"}],
  267 + }
  268 + };
  269 + },
  270 + created() {
257 this.getList(); 271 this.getList();
258 - });  
259 - return; 272 + this.getListCategory();
  273 + this.getPropertyPageList();
  274 + },
  275 + methods: {
  276 + addTag(){
  277 +
  278 + },
  279 + hideTagInput(){
  280 +
  281 + },
  282 + shopTagInput(){
  283 +
  284 + },
  285 + removeTag(row){
  286 +
  287 + },
  288 + handleTagClick(row){
  289 +
  290 + },
  291 + /** 查询规格 */
  292 + getPropertyPageList() {
  293 + // 执行查询
  294 + getPropertyPage().then(response => {
  295 + this.propertyPageList = response.data.list;
  296 + this.unUseTags= this.propertyPageList.map(function (item,index) {
  297 + return item
  298 + })
  299 + console.log(this.propertyPageList)
  300 + });
  301 + },
  302 + /** 查询分类 */
  303 + getListCategory() {
  304 + // 执行查询
  305 + listCategory().then(response => {
  306 + this.categoryList = this.handleTree(response.data, "id", "parentId");
  307 +
  308 + });
  309 + },
  310 + /** 查询列表 */
  311 + getList() {
  312 + this.loading = true;
  313 + // 处理查询参数
  314 + let params = {...this.queryParams};
  315 + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
  316 + // 执行查询
  317 + getSpuPage(params).then(response => {
  318 + this.list = response.data.list;
  319 + this.total = response.data.total;
  320 + this.loading = false;
  321 + });
  322 + },
  323 + /** 取消按钮 */
  324 + cancel() {
  325 + this.open = false;
  326 + this.reset();
  327 + },
  328 + /** 表单重置 */
  329 + reset() {
  330 + this.form = {
  331 + id: undefined,
  332 + name: undefined,
  333 + sellPoint: undefined,
  334 + description: undefined,
  335 + categoryId: undefined,
  336 + categoryIds: [],
  337 + picUrls: undefined,
  338 + sort: undefined,
  339 + likeCount: undefined,
  340 + price: undefined,
  341 + quantity: undefined,
  342 + status: undefined,
  343 + isShowTagInput:undefined,
  344 +
  345 + };
  346 + this.resetForm("form");
  347 + },
  348 + /** 搜索按钮操作 */
  349 + handleQuery() {
  350 + this.queryParams.pageNo = 1;
  351 + this.getList();
  352 + },
  353 + /** 重置按钮操作 */
  354 + resetQuery() {
  355 + this.dateRangeCreateTime = [];
  356 + this.resetForm("queryForm");
  357 + this.handleQuery();
  358 + },
  359 + /** 新增按钮操作 */
  360 + handleAdd() {
  361 + this.reset();
  362 + this.open = true;
  363 + this.title = "添加商品spu";
  364 + },
  365 + /** 修改按钮操作 */
  366 + handleUpdate(row) {
  367 + this.reset();
  368 + const id = row.id;
  369 + getSpu(id).then(response => {
  370 + this.form = response.data;
  371 + this.open = true;
  372 + this.title = "修改商品spu";
  373 + });
  374 + },
  375 + /** 提交按钮 */
  376 + submitForm() {
  377 + console.log(this.form.picUrls.split(','));
  378 +
  379 + this.$refs["form"].validate(valid => {
  380 + if (!valid) {
  381 + return;
  382 + }
  383 + this.form.categoryId= this.form.categoryIds[(this.form.categoryIds.length-1)]
  384 + // 修改的提交
  385 + if (this.form.id != null) {
  386 + updateSpu(this.form).then(response => {
  387 + this.$modal.msgSuccess("修改成功");
  388 + this.open = false;
  389 + this.getList();
  390 + });
  391 + return;
  392 + }
  393 + // 添加的提交
  394 + createSpu(this.form).then(response => {
  395 + this.$modal.msgSuccess("新增成功");
  396 + this.open = false;
  397 + this.getList();
  398 + });
  399 + });
  400 + },
  401 + /** 删除按钮操作 */
  402 + handleDelete(row) {
  403 + const id = row.id;
  404 + this.$modal.confirm('是否确认删除商品spu编号为"' + id + '"的数据项?').then(function () {
  405 + return deleteSpu(id);
  406 + }).then(() => {
  407 + this.getList();
  408 + this.$modal.msgSuccess("删除成功");
  409 + }).catch(() => {
  410 + });
  411 + },
  412 + /** 导出按钮操作 */
  413 + handleExport() {
  414 + // 处理查询参数
  415 + let params = {...this.queryParams};
  416 + params.pageNo = undefined;
  417 + params.pageSize = undefined;
  418 + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
  419 + // 执行导出
  420 + this.$modal.confirm('是否确认导出所有商品spu数据项?').then(() => {
  421 + this.exportLoading = true;
  422 + return exportSpuExcel(params);
  423 + }).then(response => {
  424 + this.$download.excel(response, '商品spu.xls');
  425 + this.exportLoading = false;
  426 + }).catch(() => {
  427 + });
  428 + }
260 } 429 }
261 - // 添加的提交  
262 - createSpu(this.form).then(response => {  
263 - this.$modal.msgSuccess("新增成功");  
264 - this.open = false;  
265 - this.getList();  
266 - });  
267 - });  
268 - },  
269 - /** 删除按钮操作 */  
270 - handleDelete(row) {  
271 - const id = row.id;  
272 - this.$modal.confirm('是否确认删除商品spu编号为"' + id + '"的数据项?').then(function() {  
273 - return deleteSpu(id);  
274 - }).then(() => {  
275 - this.getList();  
276 - this.$modal.msgSuccess("删除成功");  
277 - }).catch(() => {});  
278 - },  
279 - /** 导出按钮操作 */  
280 - handleExport() {  
281 - // 处理查询参数  
282 - let params = {...this.queryParams};  
283 - params.pageNo = undefined;  
284 - params.pageSize = undefined;  
285 - this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');  
286 - // 执行导出  
287 - this.$modal.confirm('是否确认导出所有商品spu数据项?').then(() => {  
288 - this.exportLoading = true;  
289 - return exportSpuExcel(params);  
290 - }).then(response => {  
291 - this.$download.excel(response, '商品spu.xls');  
292 - this.exportLoading = false;  
293 - }).catch(() => {});  
294 - }  
295 - }  
296 -}; 430 + };
297 </script> 431 </script>