Commit 617573a59bf811605e5700813a197189158b629e

Authored by luowenfeng
1 parent de27f4c2

feature(uniapp分类): 商品创建功能以及查询功能正常使用

sql/optional/mall/mall.sql
@@ -298,3 +298,27 @@ INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, ` @@ -298,3 +298,27 @@ INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `
298 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2027, 'Banner创建', 'market:banner:create', 3, 2, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0'); 298 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2027, 'Banner创建', 'market:banner:create', 3, 2, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
299 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2028, 'Banner更新', 'market:banner:update', 3, 3, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0'); 299 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2028, 'Banner更新', 'market:banner:update', 3, 3, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
300 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2029, 'Banner删除', 'market:banner:delete', 3, 4, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0'); 300 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2029, 'Banner删除', 'market:banner:delete', 3, 4, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
  301 +
  302 +alter table product_spu add `code` varchar(128) COMMENT '商品编码';
  303 +alter table product_spu add total_stock int COMMENT '总库存';
  304 +alter table product_spu add warn_stock int COMMENT '预警预存';
  305 +alter table product_spu add show_stock int COMMENT '是否展示库存';
  306 +alter table product_spu add sales_count int COMMENT '商品销量';
  307 +alter table product_spu add virtual_sales_count int COMMENT '虚拟销量';
  308 +alter table product_spu add click_count int COMMENT '商品点击量';
  309 +alter table product_spu add banner_url varchar(128) COMMENT '主图地址';
  310 +alter table product_spu add spec_type int COMMENT '规格类型';
  311 +alter table product_spu add brand_id int COMMENT '商品品牌编号';
  312 +alter table product_spu add video_url varchar(128) COMMENT '商品视频';
  313 +alter table product_spu add min_price int COMMENT '最小价格,单位使用:分';
  314 +alter table product_spu add max_price int COMMENT '最大价格,单位使用:分';
  315 +alter table product_spu add market_price int COMMENT '市场价,单位使用:分';
  316 +
  317 +
  318 +alter table product_sku add `name` varchar(128) COMMENT '商品 SKU 名字';
  319 +alter table product_sku add `stock` int COMMENT '库存';
  320 +alter table product_sku add `weight` double COMMENT '商品重量';
  321 +alter table product_sku add `volume` double COMMENT '商品体积';
  322 +
  323 +
  324 +alter table product_sku DROP `original_price`;
301 \ No newline at end of file 325 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java
@@ -27,16 +27,16 @@ public class ProductSkuBaseVO { @@ -27,16 +27,16 @@ public class ProductSkuBaseVO {
27 @NotNull(message = "销售价格,单位:分不能为空") 27 @NotNull(message = "销售价格,单位:分不能为空")
28 private Integer price; 28 private Integer price;
29 29
30 - @ApiModelProperty(value = "原价, 单位: 分", required = true)  
31 - @NotNull(message = "原价, 单位: 分不能为空")  
32 - private Integer originalPrice; 30 + @ApiModelProperty(value = "市场价, 单位: 分", required = true)
  31 + @NotNull(message = "市场价, 单位: 分不能为空")
  32 + private Integer marketPrice;
33 33
34 @ApiModelProperty(value = "成本价,单位: 分", required = true) 34 @ApiModelProperty(value = "成本价,单位: 分", required = true)
35 @NotNull(message = "成本价,单位: 分不能为空") 35 @NotNull(message = "成本价,单位: 分不能为空")
36 private Integer costPrice; 36 private Integer costPrice;
37 37
38 - @ApiModelProperty(value = "条形码", required = true)  
39 - @NotNull(message = "条形码不能为空") 38 + @ApiModelProperty(value = "条形码")
  39 +// @NotNull(message = "条形码不能为空")
40 private String barCode; 40 private String barCode;
41 41
42 @ApiModelProperty(value = "图片地址", required = true) 42 @ApiModelProperty(value = "图片地址", required = true)
@@ -46,7 +46,15 @@ public class ProductSkuBaseVO { @@ -46,7 +46,15 @@ public class ProductSkuBaseVO {
46 @ApiModelProperty(value = "状态: 0-正常 1-禁用") 46 @ApiModelProperty(value = "状态: 0-正常 1-禁用")
47 private Integer status; 47 private Integer status;
48 48
49 - // TODO @franky 要有 swagger 注解 49 + @ApiModelProperty(value = "库存")
  50 + private Integer stock;
  51 +
  52 + @ApiModelProperty(value = "商品重量,单位:kg 千克")
  53 + private Double weight;
  54 +
  55 + @ApiModelProperty(value = "商品体积,单位:m^3 平米")
  56 + private Double volume;
  57 +
50 @Data 58 @Data
51 public static class Property { 59 public static class Property {
52 @NotNull(message = "规格属性名id不能为空") 60 @NotNull(message = "规格属性名id不能为空")
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java
1 package cn.iocoder.yudao.module.product.controller.admin.spu.vo; 1 package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
2 2
  3 +import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;
  4 +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
  5 +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum;
3 import io.swagger.annotations.ApiModelProperty; 6 import io.swagger.annotations.ApiModelProperty;
4 import lombok.Data; 7 import lombok.Data;
5 8
@@ -16,6 +19,11 @@ public class ProductSpuBaseVO { @@ -16,6 +19,11 @@ public class ProductSpuBaseVO {
16 @ApiModelProperty(value = "商品名称") 19 @ApiModelProperty(value = "商品名称")
17 private String name; 20 private String name;
18 21
  22 + /**
  23 + * 商品编码
  24 + */
  25 + private String code;
  26 +
19 @ApiModelProperty(value = "卖点", required = true) 27 @ApiModelProperty(value = "卖点", required = true)
20 @NotNull(message = "卖点不能为空") 28 @NotNull(message = "卖点不能为空")
21 private String sellPoint; 29 private String sellPoint;
@@ -24,28 +32,87 @@ public class ProductSpuBaseVO { @@ -24,28 +32,87 @@ public class ProductSpuBaseVO {
24 @NotNull(message = "描述不能为空") 32 @NotNull(message = "描述不能为空")
25 private String description; 33 private String description;
26 34
  35 + /**
  36 + * 商品品牌编号
  37 + *
  38 + * 关联 {@link ProductBrandDO#getId()}
  39 + */
  40 + private Long brandId;
  41 +
27 @ApiModelProperty(value = "分类id", required = true) 42 @ApiModelProperty(value = "分类id", required = true)
28 @NotNull(message = "分类id不能为空") 43 @NotNull(message = "分类id不能为空")
29 private Long categoryId; 44 private Long categoryId;
30 45
  46 + /**
  47 + * 商品主图
  48 + */
  49 + private String bannerUrl;
  50 +
31 @ApiModelProperty(value = "商品主图地址,* 数组,以逗号分隔,最多上传15张", required = true) 51 @ApiModelProperty(value = "商品主图地址,* 数组,以逗号分隔,最多上传15张", required = true)
32 @NotNull(message = "商品主图地址,* 数组,以逗号分隔,最多上传15张不能为空") 52 @NotNull(message = "商品主图地址,* 数组,以逗号分隔,最多上传15张不能为空")
33 private List<String> picUrls; 53 private List<String> picUrls;
34 54
  55 + /**
  56 + * 商品视频
  57 + */
  58 + private String videoUrl;
  59 +
  60 + /**
  61 + * 规格类型
  62 + *
  63 + * 枚举 {@link ProductSpuSpecTypeEnum}
  64 + */
  65 + private Integer specType;
  66 +
35 @ApiModelProperty(value = "排序字段", required = true) 67 @ApiModelProperty(value = "排序字段", required = true)
36 @NotNull(message = "排序字段不能为空") 68 @NotNull(message = "排序字段不能为空")
37 private Integer sort; 69 private Integer sort;
38 -  
39 - @ApiModelProperty(value = "点赞初始人数")  
40 - private Integer likeCount;  
41 -  
42 - @ApiModelProperty(value = "价格 单位使用:分")  
43 - private Integer price;  
44 -  
45 - @ApiModelProperty(value = "库存数量")  
46 - private Integer quantity; 70 + /**
  71 + * 最小价格,单位使用:分
  72 + *
  73 + * 基于其对应的 {@link ProductSkuDO#getPrice()} 最小值
  74 + */
  75 + private Integer minPrice;
  76 + /**
  77 + * 最大价格,单位使用:分
  78 + *
  79 + * 基于其对应的 {@link ProductSkuDO#getPrice()} 最大值
  80 + */
  81 + private Integer maxPrice;
  82 + /**
  83 + * 市场价,单位使用:分
  84 + *
  85 + * 基于其对应的 {@link ProductSkuDO#getMarketPrice()} 最大值
  86 + */
  87 + private Integer marketPrice;
  88 + /**
  89 + * 总库存
  90 + *
  91 + * 基于其对应的 {@link ProductSkuDO#getStock()} 求和
  92 + */
  93 + private Integer totalStock;
  94 + /**
  95 + * 预警预存
  96 + */
  97 + private Integer warnStock;
  98 + /**
  99 + * 是否展示库存
  100 + */
  101 + private Boolean showStock;
47 102
48 @ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)") 103 @ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)")
49 private Integer status; 104 private Integer status;
50 105
  106 + /**
  107 + * 商品销量
  108 + */
  109 + private Integer salesCount;
  110 + /**
  111 + * 虚拟销量
  112 + */
  113 + private Integer virtualSalesCount;
  114 + /**
  115 + * 商品点击量
  116 + */
  117 + private Integer clickCount;
51 } 118 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java
@@ -49,7 +49,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { @@ -49,7 +49,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
49 //插入属性值 49 //插入属性值
50 List<ProductPropertyValueCreateReqVO> propertyValueList = createReqVO.getPropertyValueList(); 50 List<ProductPropertyValueCreateReqVO> propertyValueList = createReqVO.getPropertyValueList();
51 List<ProductPropertyValueDO> productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList); 51 List<ProductPropertyValueDO> productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList);
52 - productPropertyValueDOList.stream().forEach(x-> x.setPropertyId(property.getId())); 52 + productPropertyValueDOList.forEach(x-> x.setPropertyId(property.getId()));
53 productPropertyValueMapper.insertBatch(productPropertyValueDOList); 53 productPropertyValueMapper.insertBatch(productPropertyValueDOList);
54 // 返回 54 // 返回
55 return property.getId(); 55 return property.getId();
@@ -67,7 +67,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { @@ -67,7 +67,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
67 productPropertyValueMapper.deletePropertyValueByPropertyId(updateReqVO.getId()); 67 productPropertyValueMapper.deletePropertyValueByPropertyId(updateReqVO.getId());
68 List<ProductPropertyValueCreateReqVO> propertyValueList = updateReqVO.getPropertyValueList(); 68 List<ProductPropertyValueCreateReqVO> propertyValueList = updateReqVO.getPropertyValueList();
69 List<ProductPropertyValueDO> productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList); 69 List<ProductPropertyValueDO> productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList);
70 - productPropertyValueDOList.stream().forEach(x-> x.setPropertyId(updateReqVO.getId())); 70 + productPropertyValueDOList.forEach(x-> x.setPropertyId(updateReqVO.getId()));
71 productPropertyValueMapper.insertBatch(productPropertyValueDOList); 71 productPropertyValueMapper.insertBatch(productPropertyValueDOList);
72 } 72 }
73 73
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
@@ -2,9 +2,14 @@ package cn.iocoder.yudao.module.product.service.sku; @@ -2,9 +2,14 @@ package cn.iocoder.yudao.module.product.service.sku;
2 2
3 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; 3 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyCreateReqVO;
5 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO; 6 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO;
  7 +import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueCreateReqVO;
6 import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO; 8 import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO;
7 -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.*; 9 +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
  10 +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO;
  11 +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuPageReqVO;
  12 +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuUpdateReqVO;
8 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; 13 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
9 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; 14 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
10 import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper; 15 import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;
@@ -84,8 +89,6 @@ public class ProductSkuServiceImpl implements ProductSkuService { @@ -84,8 +89,6 @@ public class ProductSkuServiceImpl implements ProductSkuService {
84 return productSkuMapper.selectPage(pageReqVO); 89 return productSkuMapper.selectPage(pageReqVO);
85 } 90 }
86 91
87 - // TODO @franky:这个方法,貌似实现的还是有点问题哈。例如说,throw 异常,后面还执行逻辑~  
88 - // TODO @艿艿 咳咳,throw 那里我是偷懒省略了{},哈哈,我加上,然后我调试下,在优化下  
89 @Override 92 @Override
90 public void validateSkus(List<ProductSkuCreateReqVO> list) { 93 public void validateSkus(List<ProductSkuCreateReqVO> list) {
91 List<ProductSkuBaseVO.Property> skuPropertyList = list.stream().flatMap(p -> p.getProperties().stream()).collect(Collectors.toList()); 94 List<ProductSkuBaseVO.Property> skuPropertyList = list.stream().flatMap(p -> p.getProperties().stream()).collect(Collectors.toList());
@@ -99,8 +102,9 @@ public class ProductSkuServiceImpl implements ProductSkuService { @@ -99,8 +102,9 @@ public class ProductSkuServiceImpl implements ProductSkuService {
99 skuPropertyList.forEach(p -> { 102 skuPropertyList.forEach(p -> {
100 ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId()); 103 ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId());
101 // 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示 104 // 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示
102 - if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty()) 105 + if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty()) {
103 throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS); 106 throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS);
  107 + }
104 // 判断改属性名对应的属性值是否存在,不存在,给出提示 108 // 判断改属性名对应的属性值是否存在,不存在,给出提示
105 if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) { 109 if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) {
106 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS); 110 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS);
yudao-ui-admin/src/views/mall/product/spu/index.vue
@@ -181,246 +181,6 @@ @@ -181,246 +181,6 @@
181 <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body > 181 <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body >
182 <save @closeDialog="open = false"/> 182 <save @closeDialog="open = false"/>
183 </el-dialog> 183 </el-dialog>
184 - <!-- 对话框(添加 / 修改) -->  
185 - <!-- <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>  
186 - <el-form ref="form" :model="form" :rules="rules" label-width="80px">  
187 - <el-form-item label="商品名称" prop="name">  
188 - <el-input v-model="form.name" placeholder="请输入商品名称" />  
189 - </el-form-item>  
190 - <el-form-item label="卖点" prop="sellPoint">  
191 - <el-input v-model="form.sellPoint" placeholder="请输入卖点" />  
192 - </el-form-item>  
193 - <el-form-item label="描述">  
194 - <editor v-model="form.description" :min-height="192" />  
195 - </el-form-item>  
196 - <el-form-item label="分类id" prop="categoryIds">  
197 - <el-cascader  
198 - v-model="form.categoryIds"  
199 - placeholder="请输入分类id"  
200 - style="width: 100%"  
201 - :options="categoryList"  
202 - :props="propName"  
203 - clearable  
204 - ></el-cascader>  
205 - </el-form-item>  
206 - <el-form-item label="商品主图地址" prop="picUrls">  
207 - <ImageUpload v-model="form.picUrls" :limit="10" />  
208 - </el-form-item>  
209 - <el-form-item label="商品规格">  
210 - <el-button size="mini" @click="shopTagInput()">添加规格</el-button>  
211 - <div v-for="(tag, tagIndex) in skuTags" :key="tagIndex">  
212 - <span>{{ tag.name }}</span>  
213 - <el-button  
214 - style="margin-left: 10px"  
215 - class="button-new-tag"  
216 - type="text"  
217 - icon="el-icon-delete"  
218 - @click="removeTag(tagIndex)"  
219 - >删除  
220 - </el-button>  
221 - <br />  
222 - <el-tag  
223 - v-for="(tagItem, tagItemIndex) in tag.selectValues"  
224 - :key="tagItemIndex"  
225 - style="margin-right: 10px"  
226 - :disable-transitions="false"  
227 - >  
228 - {{ tagItem }}  
229 - </el-tag> -->  
230 - <!-- <el-input-->  
231 - <!-- class="input-new-tag"-->  
232 - <!-- v-if="tagItemInputs[tagIndex] && tagItemInputs[tagIndex].visible"-->  
233 - <!-- v-model="tagItemInputs[tagIndex].value"-->  
234 - <!-- :ref="`saveTagInput${tagIndex}`"-->  
235 - <!-- size="small"-->  
236 - <!-- @keyup.enter.native="handleInputConfirm(tagIndex)"-->  
237 - <!-- @blur="handleInputConfirm(tagIndex)">-->  
238 - <!-- </el-input>-->  
239 - <!-- </div>  
240 - </el-form-item>  
241 - <el-form-item label="规格名" v-show="isShowTagInput">  
242 - <el-col :span="8">  
243 - <el-select  
244 - v-model="addTagInput.name"  
245 - filterable  
246 - allow-create  
247 - default-first-option  
248 - placeholder="请选择"  
249 - @change="handleTagClick"  
250 - >  
251 - <el-option  
252 - v-for="item in unUseTags"  
253 - :key="item.id"  
254 - :label="item.name"  
255 - :value="item.name"  
256 - >  
257 - </el-option>  
258 - </el-select>  
259 - </el-col>  
260 - </el-form-item>  
261 - <el-form-item label="规格值" v-show="isShowTagInput">  
262 - <el-col :span="8">  
263 - <el-select  
264 - v-model="addTagInput.selectValues"  
265 - multiple  
266 - filterable  
267 - allow-create  
268 - default-first-option  
269 - placeholder="请选择"  
270 - >  
271 - <el-option  
272 - v-for="item in dbTagValues"  
273 - :key="item.id"  
274 - :label="item.name"  
275 - :value="item.name"  
276 - >  
277 - </el-option>  
278 - </el-select>  
279 - </el-col>  
280 - </el-form-item>  
281 - <el-form-item>  
282 - <el-button  
283 - size="mini"  
284 - type="primary"  
285 - @click="addTag()"  
286 - v-show="isShowTagInput"  
287 - >确定</el-button  
288 - >  
289 - <el-button size="mini" @click="hideTagInput()" v-show="isShowTagInput"  
290 - >取消</el-button  
291 - >  
292 - </el-form-item>  
293 - <el-form-item v-if="form.skus.length > 0">  
294 - <el-table  
295 - :data="form.skus"  
296 - border  
297 - style="width: 100%; margin-top: 20px"  
298 - :span-method="tableSpanMethod"  
299 - >  
300 - <el-table-column  
301 - v-for="(leftTitle, index) in skuTags"  
302 - :key="index"  
303 - :label="leftTitle.name"  
304 - >  
305 - <template slot-scope="scope">  
306 - {{ scope.row.propertyChildNames[index] }}  
307 - </template>  
308 - </el-table-column>  
309 - <el-table-column  
310 - v-if="skuTags.length"  
311 - prop="picUrl"  
312 - label="sku图片"  
313 - width="180"  
314 - >  
315 - <template slot-scope="scope">  
316 - <ImageUpload v-model="scope.row.picUrl" :limit="1">  
317 - </ImageUpload>  
318 - </template>  
319 - </el-table-column>  
320 - <el-table-column  
321 - prop="prodName"  
322 - label="条形码"  
323 - width="250"  
324 - v-if="skuTags.length"  
325 - >  
326 - <template slot-scope="scope">  
327 - <el-input  
328 - v-model="scope.row.barCode"  
329 - type="textarea"  
330 - :disabled="scope.row.status == 1"  
331 - ></el-input>  
332 - </template>  
333 - </el-table-column>  
334 - <el-table-column prop="price" label="销售价">  
335 - <template slot-scope="scope">  
336 - <el-input-number  
337 - size="small"  
338 - v-model="scope.row.price"  
339 - controls-position="right"  
340 - :precision="2"  
341 - :max="1000000000"  
342 - :min="0.01"  
343 - :disabled="scope.row.status == 1"  
344 - >  
345 - </el-input-number>  
346 - </template>  
347 - </el-table-column>  
348 - <el-table-column prop="oriPrice" label="成本价">  
349 - <template slot-scope="scope">  
350 - <el-input-number  
351 - size="small"  
352 - v-model="scope.row.costPrice"  
353 - controls-position="right"  
354 - :precision="2"  
355 - :max="1000000000"  
356 - :min="0.01"  
357 - :disabled="scope.row.status == 1"  
358 - >  
359 - </el-input-number>  
360 - </template>  
361 - </el-table-column>  
362 - <el-table-column prop="oriPrice" label="原价">  
363 - <template slot-scope="scope">  
364 - <el-input-number  
365 - size="small"  
366 - v-model="scope.row.originalPrice"  
367 - controls-position="right"  
368 - :precision="2"  
369 - :max="1000000000"  
370 - :min="0.01"  
371 - :disabled="scope.row.status == 1"  
372 - >  
373 - </el-input-number>  
374 - </template>  
375 - </el-table-column>  
376 - <el-table-column label="操作">  
377 - <template slot-scope="scope">  
378 - <el-button  
379 - type="text"  
380 - size="small"  
381 - @click="changeSkuStatus(`${scope.$index}`)"  
382 - v-if="scope.row.status === 0"  
383 - >  
384 - 正常  
385 - </el-button>  
386 - <el-button  
387 - type="text"  
388 - size="small"  
389 - @click="changeSkuStatus(`${scope.$index}`)"  
390 - v-else  
391 - >已禁用</el-button  
392 - >  
393 - </template>  
394 - </el-table-column>  
395 - </el-table>  
396 - </el-form-item>  
397 - <el-form-item label="排序字段" prop="sort">  
398 - <el-input v-model="form.sort" placeholder="请输入排序字段" />  
399 - </el-form-item>  
400 - <el-form-item label="点赞初始人数" prop="likeCount">  
401 - <el-input v-model="form.likeCount" placeholder="请输入点赞初始人数" />  
402 - </el-form-item>  
403 - <el-form-item label="价格 单位使用:分" prop="price">  
404 - <el-input  
405 - v-model="form.price"  
406 - placeholder="请输入价格 单位使用:分"  
407 - />  
408 - </el-form-item>  
409 - <el-form-item label="库存数量" prop="quantity">  
410 - <el-input v-model="form.quantity" placeholder="请输入库存数量" />  
411 - </el-form-item>  
412 - <el-form-item label="上下架状态" prop="status">  
413 - <el-radio-group v-model="form.status">  
414 - <el-radio label="0">上架</el-radio>  
415 - <el-radio label="1">下架</el-radio>  
416 - </el-radio-group>  
417 - </el-form-item>  
418 - </el-form>  
419 - <div slot="footer" class="dialog-footer">  
420 - <el-button type="primary" @click="submitForm">确 定</el-button>  
421 - <el-button @click="cancel">取 消</el-button>  
422 - </div>  
423 - </el-dialog> -->  
424 </div> 184 </div>
425 </template> 185 </template>
426 186
yudao-ui-admin/src/views/mall/product/spu/save.vue
1 <template> 1 <template>
2 <div class="container"> 2 <div class="container">
3 - <el-dialog  
4 - title="请输入规格值,多个请换行"  
5 - :visible.sync="dialogForSpec"  
6 - append-to-body  
7 - width="400px"  
8 - @close="dialogForSpec = false; specValue = null"  
9 - >  
10 - <el-input  
11 - v-model="specValue"  
12 - type="textarea"  
13 - :autosize="{ minRows: 6, maxRows: 6}"  
14 - placeholder="请输入内容"  
15 - />  
16 -  
17 - <div slot="footer" class="dialog-footer">  
18 - <el-button @click="dialogForSpec = false; specValue = null">取 消</el-button>  
19 - <el-button type="primary" @click="addSpecValue">确 定</el-button>  
20 - </div>  
21 - </el-dialog>  
22 -  
23 - <el-tabs v-model="activeName" class="tabs"> 3 + <el-tabs v-model="activeName" class="tabs">
24 <!-- 基础设置 --> 4 <!-- 基础设置 -->
25 <el-tab-pane label="基础设置" name="base"> 5 <el-tab-pane label="基础设置" name="base">
26 <el-form ref="baseForm" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%"> 6 <el-form ref="baseForm" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
@@ -40,11 +20,11 @@ @@ -40,11 +20,11 @@
40 clearable 20 clearable
41 ></el-cascader> 21 ></el-cascader>
42 </el-form-item> 22 </el-form-item>
43 - <el-form-item label="商品主图" prop="picUrls">  
44 - <ImageUpload v-model="baseForm.picUrl" :limit="1" /> 23 + <el-form-item label="商品主图" prop="bannerUrl">
  24 + <ImageUpload v-model="baseForm.bannerUrl" :limit="1"/>
45 </el-form-item> 25 </el-form-item>
46 - <el-form-item label="商品轮播图" prop="picUrl">  
47 - <ImageUpload v-model="baseForm.picUrls" :limit="10" /> 26 + <el-form-item label="商品轮播图" prop="picUrls">
  27 + <ImageUpload v-model="baseForm.picUrls" :limit="10"/>
48 </el-form-item> 28 </el-form-item>
49 <el-form-item label="排序字段" prop="sort"> 29 <el-form-item label="排序字段" prop="sort">
50 <el-input v-model="baseForm.sort" placeholder="请输入排序字段"/> 30 <el-input v-model="baseForm.sort" placeholder="请输入排序字段"/>
@@ -80,29 +60,28 @@ @@ -80,29 +60,28 @@
80 60
81 <div class="spec-header"> 61 <div class="spec-header">
82 规格项: 62 规格项:
83 - <el-input  
84 - v-model="specs.spec_name"  
85 - placeholder="请填写规格项"  
86 - class="spec-name"  
87 - ></el-input> 63 + <el-select v-model="specs.specId" filterable placeholder="请选择" @change="changeSpec">
  64 + <el-option
  65 + v-for="item in propertyPageList"
  66 + :key="item.id"
  67 + :label="item.name"
  68 + :value="item.id">
  69 + </el-option>
  70 + </el-select>
88 </div> 71 </div>
89 <div class="spec-values"> 72 <div class="spec-values">
90 - <template v-for="(specsValue, i) in specs.spec_values">  
91 - <el-input  
92 - v-model="specs.spec_values[i]"  
93 - class="spec-value"  
94 - :key="specsValue"  
95 - ></el-input> 73 + <template v-for="(v, i) in specs.specValue">
  74 + <el-input v-model="v.name" class="spec-value" :key="i"/>
96 </template> 75 </template>
97 - <el-button 76 + <!-- <el-button
98 type="primary" 77 type="primary"
99 icon="el-icon-plus" 78 icon="el-icon-plus"
100 circle 79 circle
101 @click="dialogForSpec = true; currentSpec = index" 80 @click="dialogForSpec = true; currentSpec = index"
102 - ></el-button> 81 + ></el-button> -->
103 </div> 82 </div>
104 </div> 83 </div>
105 - <el-button type="primary" @click="dynamicSpec.push({spec_values: []}); ratesForm.rates = []" 84 + <el-button type="primary" @click="dynamicSpec.push({specValue: []}); ratesForm.rates = []"
106 >添加规格项目 85 >添加规格项目
107 </el-button 86 </el-button
108 > 87 >
@@ -111,22 +90,24 @@ @@ -111,22 +90,24 @@
111 <!-- 规格明细 --> 90 <!-- 规格明细 -->
112 <el-form-item label="规格明细"> 91 <el-form-item label="规格明细">
113 <el-table :data="ratesForm.rates" border style="width: 100%" ref="rates"> 92 <el-table :data="ratesForm.rates" border style="width: 100%" ref="rates">
114 -  
115 - <template v-if="ratesForm.spec == 1" >  
116 - <el-table-column :key="index" v-for="(item, index) in dynamicSpec.filter(v=>v.spec_name != undefined)" :label="item.spec_name" >  
117 - <template slot-scope="scope">  
118 - <el-input  
119 - v-if="scope.row.spec"  
120 - v-model="scope.row.spec[index]"  
121 - disabled  
122 - ></el-input>  
123 - </template>  
124 - </el-table-column> 93 +
  94 + <template v-if="ratesForm.spec == 1">
  95 + <el-table-column :key="index" v-for="(item, index) in dynamicSpec.filter(v=>v.specName != undefined)"
  96 + :label="item.specName">
  97 + <template slot-scope="scope">
  98 + <el-input
  99 + v-if="scope.row.spec"
  100 + v-model="scope.row.spec[index]"
  101 + disabled
  102 + ></el-input>
  103 + </template>
  104 + </el-table-column>
125 </template> 105 </template>
126 - 106 +
127 <el-table-column label="规格图片" width="120px"> 107 <el-table-column label="规格图片" width="120px">
128 <template slot-scope="scope"> 108 <template slot-scope="scope">
129 - <ImageUpload v-model="scope.row.picUrl" :limit="1" :isShowTip="false" style="width: 100px; height: 50px" /> 109 + <ImageUpload v-model="scope.row.picUrl" :limit="1" :isShowTip="false"
  110 + style="width: 100px; height: 50px"/>
130 </template> 111 </template>
131 </el-table-column> 112 </el-table-column>
132 <el-table-column label="市场价(元)"> 113 <el-table-column label="市场价(元)">
@@ -177,40 +158,40 @@ @@ -177,40 +158,40 @@
177 158
178 <!-- 商品详情 --> 159 <!-- 商品详情 -->
179 <el-tab-pane label="商品描述" name="third"> 160 <el-tab-pane label="商品描述" name="third">
180 - <editor v-model="baseForm.description" :min-height="400" /> 161 + <editor v-model="baseForm.description" :min-height="400"/>
181 </el-tab-pane> 162 </el-tab-pane>
182 163
183 <!-- 销售设置 --> 164 <!-- 销售设置 -->
184 <el-tab-pane label="销售设置" name="fourth"> 165 <el-tab-pane label="销售设置" name="fourth">
185 <el-form ref="baseForm" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%"> 166 <el-form ref="baseForm" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
186 - <el-form-item label="库存数量" prop="quantity">  
187 - <el-input v-model="baseForm.quantity" placeholder="请输入库存数量" />  
188 - </el-form-item>  
189 - <el-form-item label="上下架状态" prop="status">  
190 - <el-radio-group v-model="baseForm.status">  
191 - <el-radio label="0">上架</el-radio>  
192 - <el-radio label="1">下架</el-radio>  
193 - </el-radio-group>  
194 - </el-form-item> 167 + <el-form-item label="库存数量" prop="totalStock">
  168 + <el-input v-model="baseForm.totalStock" placeholder="请输入库存数量"/>
  169 + </el-form-item>
  170 + <el-form-item label="上下架状态" prop="status">
  171 + <el-radio-group v-model="baseForm.status">
  172 + <el-radio :label="0">上架</el-radio>
  173 + <el-radio :label="1">下架</el-radio>
  174 + </el-radio-group>
  175 + </el-form-item>
195 </el-form> 176 </el-form>
196 </el-tab-pane> 177 </el-tab-pane>
197 </el-tabs> 178 </el-tabs>
198 179
199 - <div class="buttons">  
200 - <el-button type="info" round @click="cancel">取消</el-button>  
201 - <el-button type="success" round @click="submit">确认</el-button>  
202 - </div> 180 + <div class="buttons">
  181 + <el-button type="info" round @click="cancel">取消</el-button>
  182 + <el-button type="success" round @click="submit">确认</el-button>
  183 + </div>
203 184
204 </div> 185 </div>
205 </template> 186 </template>
206 187
207 <script> 188 <script>
208 -import {  
209 - getProductCategoryList  
210 -} from "@/api/mall/product/category";  
211 - 189 +import {getProductCategoryList} from "@/api/mall/product/category";
  190 +import {createSpu,} from "@/api/mall/product/spu";
  191 +import {getPropertyPage,} from "@/api/mall/product/property";
212 import Editor from "@/components/Editor"; 192 import Editor from "@/components/Editor";
213 import ImageUpload from "@/components/ImageUpload"; 193 import ImageUpload from "@/components/ImageUpload";
  194 +
214 export default { 195 export default {
215 components: { 196 components: {
216 Editor, 197 Editor,
@@ -231,12 +212,12 @@ export default { @@ -231,12 +212,12 @@ export default {
231 categoryIds: null, 212 categoryIds: null,
232 sort: null, 213 sort: null,
233 description: null, 214 description: null,
234 - picUrl:null, 215 + bannerUrl: null,
235 picUrls: [], 216 picUrls: [],
236 - quantity: null 217 + totalStock: null,
  218 + status: 0
237 }, 219 },
238 categoryList: [], 220 categoryList: [],
239 -  
240 // 价格库存 221 // 价格库存
241 ratesForm: { 222 ratesForm: {
242 spec: 0, 223 spec: 0,
@@ -245,15 +226,16 @@ export default { @@ -245,15 +226,16 @@ export default {
245 }, 226 },
246 dynamicSpec: [ 227 dynamicSpec: [
247 // { 228 // {
248 - // spec_id: 86,  
249 - // spec_name: "颜色",  
250 - // spec_values: [],  
251 - // spec_value_ids: [225], 229 + // specId: 86,
  230 + // specName: "颜色",
  231 + // specValue:[{
  232 + // name: "红色",
  233 + // id: 225,
  234 + // }]
252 // }, 235 // },
253 ], 236 ],
254 - dialogForSpec: false, 237 + propertyPageList: [],
255 specValue: null, 238 specValue: null,
256 - currentSpec: null,  
257 239
258 // 表单校验 240 // 表单校验
259 rules: { 241 rules: {
@@ -263,7 +245,7 @@ export default { @@ -263,7 +245,7 @@ export default {
263 categoryIds: [ 245 categoryIds: [
264 {required: true, message: "分类id不能为空", trigger: "blur"}, 246 {required: true, message: "分类id不能为空", trigger: "blur"},
265 ], 247 ],
266 - picUrls: [{required: true, message: "商品主图地址", trigger: "blur"}], 248 + bannerUrl: [{required: true, message: "商品主图地址", trigger: "blur"}],
267 sort: [ 249 sort: [
268 {required: true, message: "排序字段不能为空", trigger: "blur"}, 250 {required: true, message: "排序字段不能为空", trigger: "blur"},
269 ], 251 ],
@@ -272,54 +254,44 @@ export default { @@ -272,54 +254,44 @@ export default {
272 }, 254 },
273 created() { 255 created() {
274 this.getListCategory(); 256 this.getListCategory();
  257 + this.getPropertyPageList();
275 }, 258 },
276 methods: { 259 methods: {
277 changeRadio() { 260 changeRadio() {
278 261
279 this.$refs.rates.doLayout() 262 this.$refs.rates.doLayout()
280 - if(this.ratesForm.spec == 0){ 263 + if (this.ratesForm.spec == 0) {
281 this.ratesForm.rates = [{}] 264 this.ratesForm.rates = [{}]
282 - }else{ 265 + } else {
283 this.ratesForm.rates = [] 266 this.ratesForm.rates = []
284 - if(this.dynamicSpec.length > 0){ 267 + if (this.dynamicSpec.length > 0) {
285 this.buildRatesFormRates() 268 this.buildRatesFormRates()
286 } 269 }
287 } 270 }
288 }, 271 },
289 // 构建规格明细笛卡尔积 272 // 构建规格明细笛卡尔积
290 - buildRatesFormRates(){ 273 + buildRatesFormRates() {
291 let rates = []; 274 let rates = [];
292 - this.dynamicSpec.map(v=>v.spec_values)  
293 - .reduce((last, current) => {  
294 - const array = [];  
295 - last.forEach(par1 => { 275 + this.dynamicSpec.map(v => v.specValue.map(m => m.name))
  276 + .reduce((last, current) => {
  277 + const array = [];
  278 + last.forEach(par1 => {
296 current.forEach(par2 => { 279 current.forEach(par2 => {
297 - let v  
298 - if(par1 instanceof Array){ 280 + let v
  281 + if (par1 instanceof Array) {
299 v = par1.concat(par2) 282 v = par1.concat(par2)
300 - }else{ 283 + } else {
301 v = [par1, par2]; 284 v = [par1, par2];
302 } 285 }
303 array.push(v) 286 array.push(v)
304 }); 287 });
  288 + });
  289 + return array;
  290 + })
  291 + .forEach(v => {
  292 + rates.push({spec: v})
305 }); 293 });
306 - return array;  
307 - })  
308 - .forEach(v=>{  
309 - rates.push({spec: v})  
310 - });  
311 - console.log(rates)  
312 - this.ratesForm.rates = rates  
313 - },  
314 - addSpecValue() {  
315 - this.dialogForSpec = false;  
316 - let specValue = this.dynamicSpec[this.currentSpec].spec_values  
317 - .concat(this.specValue.split(/[(\r\n)\r\n]+/))  
318 - .filter(v => v != "");  
319 - console.log(specValue)  
320 - this.dynamicSpec[this.currentSpec].spec_values = [...new Set(specValue)];  
321 - this.currentSpec = null;  
322 - this.buildRatesFormRates() 294 + this.ratesForm.rates = rates
323 }, 295 },
324 /** 查询分类 */ 296 /** 查询分类 */
325 getListCategory() { 297 getListCategory() {
@@ -328,9 +300,53 @@ export default { @@ -328,9 +300,53 @@ export default {
328 this.categoryList = this.handleTree(response.data, "id", "parentId"); 300 this.categoryList = this.handleTree(response.data, "id", "parentId");
329 }); 301 });
330 }, 302 },
331 - cancel(){ 303 + cancel() {
  304 + this.$emit("closeDialog");
  305 + },
  306 + submit() {
  307 + let rates = this.ratesForm.rates;
  308 + // 动态规格调整字段
  309 + if (this.ratesForm.spec == 1) {
  310 + rates.forEach(r => {
  311 + let properties = []
  312 + r.spec.forEach((v, i) => {
  313 + let specValue = this.dynamicSpec[i].specValue.find(o => o.name == v);
  314 + let propertie = {};
  315 + propertie.propertyId = this.dynamicSpec[i].specId;
  316 + propertie.valueId = specValue.id;
  317 + properties.push(propertie);
  318 + })
  319 + r.properties = properties;
  320 + })
  321 + }
  322 + this.baseForm.skus = rates;
  323 + this.baseForm.specType = this.ratesForm.spec;
  324 + this.baseForm.categoryId = this.baseForm.categoryIds[this.baseForm.categoryIds.length - 1];
  325 + console.log(this.baseForm)
  326 + createSpu(this.baseForm).then((response) => {
  327 + this.$modal.msgSuccess("新增成功");
  328 + this.open = false;
  329 + this.getList();
332 this.$emit("closeDialog"); 330 this.$emit("closeDialog");
  331 + });
333 }, 332 },
  333 + /** 查询规格 */
  334 + getPropertyPageList() {
  335 + // 执行查询
  336 + getPropertyPage().then((response) => {
  337 + this.propertyPageList = response.data.list;
  338 + });
  339 + },
  340 + changeSpec(val) {
  341 + let obj = this.propertyPageList.find(o => o.id == val);
  342 + let dynamicSpec = this.dynamicSpec;
  343 + let spec = dynamicSpec.find(o => o.specId == val)
  344 + spec.specId = obj.id;
  345 + spec.specName = obj.name;
  346 + spec.specValue = obj.propertyValueList;
  347 + this.dynamicSpec = dynamicSpec;
  348 + this.buildRatesFormRates();
  349 + }
334 }, 350 },
335 }; 351 };
336 </script> 352 </script>
@@ -349,7 +365,7 @@ export default { @@ -349,7 +365,7 @@ export default {
349 365
350 .spec-header { 366 .spec-header {
351 padding: 30px; 367 padding: 30px;
352 - padding-bottom: 0; 368 + padding-bottom: 20px;
353 369
354 .spec-name { 370 .spec-name {
355 display: inline; 371 display: inline;
@@ -364,6 +380,7 @@ export default { @@ -364,6 +380,7 @@ export default {
364 width: 84%; 380 width: 84%;
365 padding: 25px; 381 padding: 25px;
366 margin: auto; 382 margin: auto;
  383 + padding-top: 5px;
367 384
368 .spec-value { 385 .spec-value {
369 display: inline-block; 386 display: inline-block;
@@ -381,35 +398,40 @@ export default { @@ -381,35 +398,40 @@ export default {
381 } 398 }
382 } 399 }
383 400
384 -.tabs{ 401 +.tabs {
385 height: 500px; 402 height: 500px;
386 border-bottom: 2px solid #f2f2f2; 403 border-bottom: 2px solid #f2f2f2;
387 - .el-tab-pane{ 404 +
  405 + .el-tab-pane {
388 height: 445px; 406 height: 445px;
389 overflow-y: auto; 407 overflow-y: auto;
390 } 408 }
391 } 409 }
392 410
393 // 库存价格图片样式修改 411 // 库存价格图片样式修改
394 -.rates{  
395 -.component-upload-image{  
396 - margin: auto;  
397 -}  
398 -.el-upload--picture-card{  
399 - width: 100px;  
400 - height: 50px;  
401 - line-height: 60px;  
402 - margin: auto;  
403 -}  
404 -.el-upload-list__item{  
405 - width: 100px !important;  
406 - height: 50px !important;  
407 -} 412 +.rates {
  413 + .component-upload-image {
  414 + margin: auto;
  415 + }
  416 +
  417 + .el-upload--picture-card {
  418 + width: 100px;
  419 + height: 50px;
  420 + line-height: 60px;
  421 + margin: auto;
  422 + }
  423 +
  424 + .el-upload-list__item {
  425 + width: 100px !important;
  426 + height: 50px !important;
  427 + }
408 } 428 }
409 -.buttons{ 429 +
  430 +.buttons {
410 margin-top: 20px; 431 margin-top: 20px;
411 height: 36px; 432 height: 36px;
412 - button{ 433 +
  434 + button {
413 float: right; 435 float: right;
414 margin-left: 15px; 436 margin-left: 15px;
415 } 437 }