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 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 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 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 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 27 @NotNull(message = "销售价格,单位:分不能为空")
28 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 34 @ApiModelProperty(value = "成本价,单位: 分", required = true)
35 35 @NotNull(message = "成本价,单位: 分不能为空")
36 36 private Integer costPrice;
37 37  
38   - @ApiModelProperty(value = "条形码", required = true)
39   - @NotNull(message = "条形码不能为空")
  38 + @ApiModelProperty(value = "条形码")
  39 +// @NotNull(message = "条形码不能为空")
40 40 private String barCode;
41 41  
42 42 @ApiModelProperty(value = "图片地址", required = true)
... ... @@ -46,7 +46,15 @@ public class ProductSkuBaseVO {
46 46 @ApiModelProperty(value = "状态: 0-正常 1-禁用")
47 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 58 @Data
51 59 public static class Property {
52 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 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 6 import io.swagger.annotations.ApiModelProperty;
4 7 import lombok.Data;
5 8  
... ... @@ -16,6 +19,11 @@ public class ProductSpuBaseVO {
16 19 @ApiModelProperty(value = "商品名称")
17 20 private String name;
18 21  
  22 + /**
  23 + * 商品编码
  24 + */
  25 + private String code;
  26 +
19 27 @ApiModelProperty(value = "卖点", required = true)
20 28 @NotNull(message = "卖点不能为空")
21 29 private String sellPoint;
... ... @@ -24,28 +32,87 @@ public class ProductSpuBaseVO {
24 32 @NotNull(message = "描述不能为空")
25 33 private String description;
26 34  
  35 + /**
  36 + * 商品品牌编号
  37 + *
  38 + * 关联 {@link ProductBrandDO#getId()}
  39 + */
  40 + private Long brandId;
  41 +
27 42 @ApiModelProperty(value = "分类id", required = true)
28 43 @NotNull(message = "分类id不能为空")
29 44 private Long categoryId;
30 45  
  46 + /**
  47 + * 商品主图
  48 + */
  49 + private String bannerUrl;
  50 +
31 51 @ApiModelProperty(value = "商品主图地址,* 数组,以逗号分隔,最多上传15张", required = true)
32 52 @NotNull(message = "商品主图地址,* 数组,以逗号分隔,最多上传15张不能为空")
33 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 67 @ApiModelProperty(value = "排序字段", required = true)
36 68 @NotNull(message = "排序字段不能为空")
37 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 103 @ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)")
49 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 49 //插入属性值
50 50 List<ProductPropertyValueCreateReqVO> propertyValueList = createReqVO.getPropertyValueList();
51 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 53 productPropertyValueMapper.insertBatch(productPropertyValueDOList);
54 54 // 返回
55 55 return property.getId();
... ... @@ -67,7 +67,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
67 67 productPropertyValueMapper.deletePropertyValueByPropertyId(updateReqVO.getId());
68 68 List<ProductPropertyValueCreateReqVO> propertyValueList = updateReqVO.getPropertyValueList();
69 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 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 2  
3 3 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyCreateReqVO;
5 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 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 13 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
9 14 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
10 15 import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;
... ... @@ -84,8 +89,6 @@ public class ProductSkuServiceImpl implements ProductSkuService {
84 89 return productSkuMapper.selectPage(pageReqVO);
85 90 }
86 91  
87   - // TODO @franky:这个方法,貌似实现的还是有点问题哈。例如说,throw 异常,后面还执行逻辑~
88   - // TODO @艿艿 咳咳,throw 那里我是偷懒省略了{},哈哈,我加上,然后我调试下,在优化下
89 92 @Override
90 93 public void validateSkus(List<ProductSkuCreateReqVO> list) {
91 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 102 skuPropertyList.forEach(p -> {
100 103 ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId());
101 104 // 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示
102   - if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty())
  105 + if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty()) {
103 106 throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS);
  107 + }
104 108 // 判断改属性名对应的属性值是否存在,不存在,给出提示
105 109 if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) {
106 110 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS);
... ...
yudao-ui-admin/src/views/mall/product/spu/index.vue
... ... @@ -181,246 +181,6 @@
181 181 <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body >
182 182 <save @closeDialog="open = false"/>
183 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 184 </div>
425 185 </template>
426 186  
... ...
yudao-ui-admin/src/views/mall/product/spu/save.vue
1 1 <template>
2 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 5 <el-tab-pane label="基础设置" name="base">
26 6 <el-form ref="baseForm" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
... ... @@ -40,11 +20,11 @@
40 20 clearable
41 21 ></el-cascader>
42 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 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 28 </el-form-item>
49 29 <el-form-item label="排序字段" prop="sort">
50 30 <el-input v-model="baseForm.sort" placeholder="请输入排序字段"/>
... ... @@ -80,29 +60,28 @@
80 60  
81 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 71 </div>
89 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 75 </template>
97   - <el-button
  76 + <!-- <el-button
98 77 type="primary"
99 78 icon="el-icon-plus"
100 79 circle
101 80 @click="dialogForSpec = true; currentSpec = index"
102   - ></el-button>
  81 + ></el-button> -->
103 82 </div>
104 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 86 </el-button
108 87 >
... ... @@ -111,22 +90,24 @@
111 90 <!-- 规格明细 -->
112 91 <el-form-item label="规格明细">
113 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 105 </template>
126   -
  106 +
127 107 <el-table-column label="规格图片" width="120px">
128 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 111 </template>
131 112 </el-table-column>
132 113 <el-table-column label="市场价(元)">
... ... @@ -177,40 +158,40 @@
177 158  
178 159 <!-- 商品详情 -->
179 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 162 </el-tab-pane>
182 163  
183 164 <!-- 销售设置 -->
184 165 <el-tab-pane label="销售设置" name="fourth">
185 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 176 </el-form>
196 177 </el-tab-pane>
197 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 185 </div>
205 186 </template>
206 187  
207 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 192 import Editor from "@/components/Editor";
213 193 import ImageUpload from "@/components/ImageUpload";
  194 +
214 195 export default {
215 196 components: {
216 197 Editor,
... ... @@ -231,12 +212,12 @@ export default {
231 212 categoryIds: null,
232 213 sort: null,
233 214 description: null,
234   - picUrl:null,
  215 + bannerUrl: null,
235 216 picUrls: [],
236   - quantity: null
  217 + totalStock: null,
  218 + status: 0
237 219 },
238 220 categoryList: [],
239   -
240 221 // 价格库存
241 222 ratesForm: {
242 223 spec: 0,
... ... @@ -245,15 +226,16 @@ export default {
245 226 },
246 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 238 specValue: null,
256   - currentSpec: null,
257 239  
258 240 // 表单校验
259 241 rules: {
... ... @@ -263,7 +245,7 @@ export default {
263 245 categoryIds: [
264 246 {required: true, message: "分类id不能为空", trigger: "blur"},
265 247 ],
266   - picUrls: [{required: true, message: "商品主图地址", trigger: "blur"}],
  248 + bannerUrl: [{required: true, message: "商品主图地址", trigger: "blur"}],
267 249 sort: [
268 250 {required: true, message: "排序字段不能为空", trigger: "blur"},
269 251 ],
... ... @@ -272,54 +254,44 @@ export default {
272 254 },
273 255 created() {
274 256 this.getListCategory();
  257 + this.getPropertyPageList();
275 258 },
276 259 methods: {
277 260 changeRadio() {
278 261  
279 262 this.$refs.rates.doLayout()
280   - if(this.ratesForm.spec == 0){
  263 + if (this.ratesForm.spec == 0) {
281 264 this.ratesForm.rates = [{}]
282   - }else{
  265 + } else {
283 266 this.ratesForm.rates = []
284   - if(this.dynamicSpec.length > 0){
  267 + if (this.dynamicSpec.length > 0) {
285 268 this.buildRatesFormRates()
286 269 }
287 270 }
288 271 },
289 272 // 构建规格明细笛卡尔积
290   - buildRatesFormRates(){
  273 + buildRatesFormRates() {
291 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 279 current.forEach(par2 => {
297   - let v
298   - if(par1 instanceof Array){
  280 + let v
  281 + if (par1 instanceof Array) {
299 282 v = par1.concat(par2)
300   - }else{
  283 + } else {
301 284 v = [par1, par2];
302 285 }
303 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 297 getListCategory() {
... ... @@ -328,9 +300,53 @@ export default {
328 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 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 352 </script>
... ... @@ -349,7 +365,7 @@ export default {
349 365  
350 366 .spec-header {
351 367 padding: 30px;
352   - padding-bottom: 0;
  368 + padding-bottom: 20px;
353 369  
354 370 .spec-name {
355 371 display: inline;
... ... @@ -364,6 +380,7 @@ export default {
364 380 width: 84%;
365 381 padding: 25px;
366 382 margin: auto;
  383 + padding-top: 5px;
367 384  
368 385 .spec-value {
369 386 display: inline-block;
... ... @@ -381,35 +398,40 @@ export default {
381 398 }
382 399 }
383 400  
384   -.tabs{
  401 +.tabs {
385 402 height: 500px;
386 403 border-bottom: 2px solid #f2f2f2;
387   - .el-tab-pane{
  404 +
  405 + .el-tab-pane {
388 406 height: 445px;
389 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 431 margin-top: 20px;
411 432 height: 36px;
412   - button{
  433 +
  434 + button {
413 435 float: right;
414 436 margin-left: 15px;
415 437 }
... ...