瀏覽代碼

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

# Conflicts:
#	yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java
#	yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java
#	yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
#	yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
YunaiV 2 年之前
父節點
當前提交
a11dd1be28

+ 24 - 0
sql/optional/mall/mall.sql

@@ -298,3 +298,27 @@ INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `
 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');
 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');
 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');
 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');
 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');
 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');
+
+alter table product_spu add `code` varchar(128) COMMENT '商品编码';
+alter table product_spu add  total_stock int COMMENT '总库存';
+alter table product_spu add  warn_stock int COMMENT '预警预存';
+alter table product_spu add  show_stock int COMMENT '是否展示库存';
+alter table product_spu add  sales_count int COMMENT '商品销量';
+alter table product_spu add  virtual_sales_count int COMMENT '虚拟销量';
+alter table product_spu add  click_count int COMMENT '商品点击量';
+alter table product_spu add  banner_url  varchar(128) COMMENT '主图地址';
+alter table product_spu add  spec_type int COMMENT '规格类型';
+alter table product_spu add  brand_id int COMMENT '商品品牌编号';
+alter table product_spu add  video_url varchar(128) COMMENT '商品视频';
+alter table product_spu add  min_price int COMMENT '最小价格,单位使用:分';
+alter table product_spu add  max_price int COMMENT '最大价格,单位使用:分';
+alter table product_spu add  market_price int COMMENT '市场价,单位使用:分';
+
+
+alter table product_sku add `name` varchar(128) COMMENT '商品 SKU 名字';
+alter table product_sku add `stock` int COMMENT '库存';
+alter table product_sku add `weight` double COMMENT '商品重量';
+alter table product_sku add `volume` double COMMENT '商品体积';
+
+
+alter table product_sku DROP `original_price`;

+ 0 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java

@@ -38,7 +38,6 @@ public class ProductSkuBaseVO {
     private Integer costPrice;
     private Integer costPrice;
 
 
     @ApiModelProperty(value = "条形码", example = "haha")
     @ApiModelProperty(value = "条形码", example = "haha")
-    @NotNull(message = "条形码不能为空")
     private String barCode;
     private String barCode;
 
 
     @ApiModelProperty(value = "图片地址")
     @ApiModelProperty(value = "图片地址")

+ 2 - 2
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 {
         //插入属性值
         //插入属性值
         List<ProductPropertyValueCreateReqVO> propertyValueList = createReqVO.getPropertyValueList();
         List<ProductPropertyValueCreateReqVO> propertyValueList = createReqVO.getPropertyValueList();
         List<ProductPropertyValueDO> productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList);
         List<ProductPropertyValueDO> productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList);
-        productPropertyValueDOList.stream().forEach(x-> x.setPropertyId(property.getId()));
+        productPropertyValueDOList.forEach(x-> x.setPropertyId(property.getId()));
         productPropertyValueMapper.insertBatch(productPropertyValueDOList);
         productPropertyValueMapper.insertBatch(productPropertyValueDOList);
         // 返回
         // 返回
         return property.getId();
         return property.getId();
@@ -67,7 +67,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
         productPropertyValueMapper.deletePropertyValueByPropertyId(updateReqVO.getId());
         productPropertyValueMapper.deletePropertyValueByPropertyId(updateReqVO.getId());
         List<ProductPropertyValueCreateReqVO> propertyValueList = updateReqVO.getPropertyValueList();
         List<ProductPropertyValueCreateReqVO> propertyValueList = updateReqVO.getPropertyValueList();
         List<ProductPropertyValueDO> productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList);
         List<ProductPropertyValueDO> productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList);
-        productPropertyValueDOList.stream().forEach(x-> x.setPropertyId(updateReqVO.getId()));
+        productPropertyValueDOList.forEach(x-> x.setPropertyId(updateReqVO.getId()));
         productPropertyValueMapper.insertBatch(productPropertyValueDOList);
         productPropertyValueMapper.insertBatch(productPropertyValueDOList);
     }
     }
 
 

+ 10 - 6
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;
 
 
 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyCreateReqVO;
 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO;
 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO;
+import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueCreateReqVO;
 import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO;
 import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO;
-import cn.iocoder.yudao.module.product.controller.admin.sku.vo.*;
+import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
+import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO;
+import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuPageReqVO;
+import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuUpdateReqVO;
 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
 import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;
 import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;
@@ -84,11 +89,9 @@ public class ProductSkuServiceImpl implements ProductSkuService {
         return productSkuMapper.selectPage(pageReqVO);
         return productSkuMapper.selectPage(pageReqVO);
     }
     }
 
 
-    // TODO @franky:这个方法,貌似实现的还是有点问题哈。例如说,throw 异常,后面还执行逻辑~
-    // TODO @艿艿 咳咳,throw 那里我是偷懒省略了{},哈哈,我加上,然后我调试下,在优化下
     @Override
     @Override
-    public void validateSkus(List<ProductSkuCreateOrUpdateReqVO> list) {
-        List<ProductSkuBaseVO.Property> skuPropertyList = list.stream().flatMap(p -> p.getProperties().stream()).collect(Collectors.toList());
+    public void validateSkus(List<ProductSkuCreateReqVO> list) {
+        List<ProductSkuBaseVO.Property> skuPropertyList = list.stream().flatMap(p -> Optional.of(p.getProperties()).orElse(new ArrayList<>()).stream()).collect(Collectors.toList());
         // 校验规格属性以及规格值是否存在
         // 校验规格属性以及规格值是否存在
         List<Long> propertyIds = skuPropertyList.stream().map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toList());
         List<Long> propertyIds = skuPropertyList.stream().map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toList());
         List<ProductPropertyRespVO> propertyAndValueList = productPropertyService.selectByIds(propertyIds);
         List<ProductPropertyRespVO> propertyAndValueList = productPropertyService.selectByIds(propertyIds);
@@ -99,8 +102,9 @@ public class ProductSkuServiceImpl implements ProductSkuService {
         skuPropertyList.forEach(p -> {
         skuPropertyList.forEach(p -> {
             ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId());
             ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId());
             // 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示
             // 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示
-            if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty())
+            if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty()) {
                 throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS);
                 throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS);
+            }
             // 判断改属性名对应的属性值是否存在,不存在,给出提示
             // 判断改属性名对应的属性值是否存在,不存在,给出提示
             if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) {
             if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) {
                 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS);
                 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS);

+ 8 - 4
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java

@@ -56,8 +56,10 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         // 校验分类
         // 校验分类
         categoryService.validateProductCategory(createReqVO.getCategoryId());
         categoryService.validateProductCategory(createReqVO.getCategoryId());
         // 校验SKU
         // 校验SKU
-        List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = createReqVO.getSkus();
-        productSkuService.validateSkus(skuCreateReqList);
+        List<ProductSkuCreateReqVO> skuCreateReqList = createReqVO.getSkus();
+        if(createReqVO.getSpecType() == 1) {
+            productSkuService.validateSkus(skuCreateReqList);
+        }
         // 插入SPU
         // 插入SPU
         ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO);
         ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO);
         ProductSpuMapper.insert(spu);
         ProductSpuMapper.insert(spu);
@@ -76,8 +78,10 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         // 校验分类
         // 校验分类
         categoryService.validateProductCategory(updateReqVO.getCategoryId());
         categoryService.validateProductCategory(updateReqVO.getCategoryId());
         // 校验SKU
         // 校验SKU
-        List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = updateReqVO.getSkus();
-        productSkuService.validateSkus(skuCreateReqList);
+        List<ProductSkuCreateReqVO> skuCreateReqList = updateReqVO.getSkus();
+        if(updateReqVO.getSpecType() == 1) {
+            productSkuService.validateSkus(skuCreateReqList);
+        }
         // 更新
         // 更新
         ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO);
         ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO);
         ProductSpuMapper.updateById(updateObj);
         ProductSpuMapper.updateById(updateObj);

+ 312 - 366
yudao-ui-admin/src/views/mall/product/spu/index.vue

@@ -1,36 +1,81 @@
 <template>
 <template>
   <div class="app-container">
   <div class="app-container">
-
     <!-- 搜索工作栏 -->
     <!-- 搜索工作栏 -->
-    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+    <el-form
+      :model="queryParams"
+      ref="queryForm"
+      size="small"
+      :inline="true"
+      v-show="showSearch"
+      label-width="68px"
+    >
       <el-form-item label="商品名称" prop="name">
       <el-form-item label="商品名称" prop="name">
-        <el-input v-model="queryParams.name" placeholder="请输入商品名称" clearable @keyup.enter.native="handleQuery"/>
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入商品名称"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
       </el-form-item>
       </el-form-item>
       <el-form-item label="卖点" prop="sellPoint">
       <el-form-item label="卖点" prop="sellPoint">
-        <el-input v-model="queryParams.sellPoint" placeholder="请输入卖点" clearable @keyup.enter.native="handleQuery"/>
+        <el-input
+          v-model="queryParams.sellPoint"
+          placeholder="请输入卖点"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
       </el-form-item>
       </el-form-item>
       <el-form-item label="分类id" prop="categoryId">
       <el-form-item label="分类id" prop="categoryId">
-        <el-input v-model="queryParams.categoryId" placeholder="请输入分类id" clearable @keyup.enter.native="handleQuery"/>
+        <el-input
+          v-model="queryParams.categoryId"
+          placeholder="请输入分类id"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
       </el-form-item>
       </el-form-item>
       <el-form-item label="价格(分)" prop="price">
       <el-form-item label="价格(分)" prop="price">
-        <el-input v-model="queryParams.price" placeholder="请输入价格 单位使用:分" clearable @keyup.enter.native="handleQuery"/>
+        <el-input
+          v-model="queryParams.price"
+          placeholder="请输入价格 单位使用:分"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
       </el-form-item>
       </el-form-item>
       <el-form-item label="库存数量" prop="quantity">
       <el-form-item label="库存数量" prop="quantity">
-        <el-input v-model="queryParams.quantity" placeholder="请输入库存数量" clearable @keyup.enter.native="handleQuery"/>
+        <el-input
+          v-model="queryParams.quantity"
+          placeholder="请输入库存数量"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
       </el-form-item>
       </el-form-item>
       <el-form-item label="状态" prop="status">
       <el-form-item label="状态" prop="status">
-        <el-select v-model="queryParams.status" placeholder="请选择上下架状态" clearable size="small">
-          <el-option label="请选择字典生成" value=""/>
-          <el-option label="上架" value="0"/>
-          <el-option label="下架" value="1"/>
+        <el-select
+          v-model="queryParams.status"
+          placeholder="请选择上下架状态"
+          clearable
+          size="small"
+        >
+          <el-option label="请选择字典生成" value="" />
+          <el-option label="上架" value="0" />
+          <el-option label="下架" value="1" />
         </el-select>
         </el-select>
       </el-form-item>
       </el-form-item>
       <el-form-item label="创建时间">
       <el-form-item label="创建时间">
-        <el-date-picker v-model="dateRangeCreateTime" style="width: 240px" value-format="yyyy-MM-dd"
-                        type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"/>
+        <el-date-picker
+          v-model="dateRangeCreateTime"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        />
       </el-form-item>
       </el-form-item>
       <el-form-item>
       <el-form-item>
-        <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
+        <el-button type="primary" icon="el-icon-search" @click="handleQuery"
+          >搜索</el-button
+        >
         <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
         <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
       </el-form-item>
       </el-form-item>
     </el-form>
     </el-form>
@@ -38,257 +83,123 @@
     <!-- 操作工具栏 -->
     <!-- 操作工具栏 -->
     <el-row :gutter="10" class="mb8">
     <el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
       <el-col :span="1.5">
-        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
-                   v-hasPermi="['product:spu:create']">新增
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['product:spu:create']"
+          >新增
         </el-button>
         </el-button>
       </el-col>
       </el-col>
       <el-col :span="1.5">
       <el-col :span="1.5">
-        <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
-                   :loading="exportLoading"
-                   v-hasPermi="['product:spu:export']">导出
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          :loading="exportLoading"
+          v-hasPermi="['product:spu:export']"
+          >导出
         </el-button>
         </el-button>
       </el-col>
       </el-col>
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+      <right-toolbar
+        :showSearch.sync="showSearch"
+        @queryTable="getList"
+      ></right-toolbar>
     </el-row>
     </el-row>
 
 
     <!-- 列表 -->
     <!-- 列表 -->
     <el-table v-loading="loading" :data="list">
     <el-table v-loading="loading" :data="list">
-      <el-table-column label="主键" align="center" prop="id"/>
-      <el-table-column label="商品名称" align="center" prop="name"/>
+      <el-table-column label="主键" align="center" prop="id" />
+      <el-table-column label="商品名称" align="center" prop="name" />
       <!--      <el-table-column label="卖点" align="center" prop="sellPoint"/>-->
       <!--      <el-table-column label="卖点" align="center" prop="sellPoint"/>-->
       <!--      <el-table-column label="描述" align="center" prop="description"/>-->
       <!--      <el-table-column label="描述" align="center" prop="description"/>-->
-      <el-table-column label="分类id" align="center" prop="categoryId"/>
+      <el-table-column label="分类id" align="center" prop="categoryId" />
       <el-table-column label="商品主图地址" align="center" prop="picUrls">
       <el-table-column label="商品主图地址" align="center" prop="picUrls">
         <template slot-scope="scope">
         <template slot-scope="scope">
-          <img v-if="scope.row.picUrls" :src="scope.row.picUrls[0]" alt="分类图片" class="img-height"/>
+          <img
+            v-if="scope.row.picUrls"
+            :src="scope.row.picUrls[0]"
+            alt="分类图片"
+            class="img-height"
+          />
         </template>
         </template>
       </el-table-column>
       </el-table-column>
-      <el-table-column label="排序字段" align="center" prop="sort"/>
-      <el-table-column label="点赞初始人数" align="center" prop="likeCount"/>
-      <el-table-column label="价格 (分)" align="center" prop="price"/>
-      <el-table-column label="库存数量" align="center" prop="quantity"/>
+      <el-table-column label="排序字段" align="center" prop="sort" />
+      <el-table-column label="点赞初始人数" align="center" prop="likeCount" />
+      <el-table-column label="价格 (分)" align="center" prop="price" />
+      <el-table-column label="库存数量" align="center" prop="quantity" />
       <!--      <el-table-column label="状态" align="center" prop="status"/>-->
       <!--      <el-table-column label="状态" align="center" prop="status"/>-->
-      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        width="180"
+      >
         <template slot-scope="scope">
         <template slot-scope="scope">
           <span>{{ parseTime(scope.row.createTime) }}</span>
           <span>{{ parseTime(scope.row.createTime) }}</span>
         </template>
         </template>
       </el-table-column>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+      <el-table-column
+        label="操作"
+        align="center"
+        class-name="small-padding fixed-width"
+      >
         <template slot-scope="scope">
         <template slot-scope="scope">
-          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
-                     v-hasPermi="['product:spu:update']">修改
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['product:spu:update']"
+            >修改
           </el-button>
           </el-button>
-          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
-                     v-hasPermi="['product:spu:delete']">删除
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['product:spu:delete']"
+            >删除
           </el-button>
           </el-button>
         </template>
         </template>
       </el-table-column>
       </el-table-column>
     </el-table>
     </el-table>
     <!-- 分页组件 -->
     <!-- 分页组件 -->
-    <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
-                @pagination="getList"/>
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.pageNo"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
 
 
-    <!-- 对话框(添加 / 修改) -->
-    <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
-      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="商品名称" prop="name">
-          <el-input v-model="form.name" placeholder="请输入商品名称"/>
-        </el-form-item>
-        <el-form-item label="卖点" prop="sellPoint">
-          <el-input v-model="form.sellPoint" placeholder="请输入卖点"/>
-        </el-form-item>
-        <el-form-item label="描述">
-          <editor v-model="form.description" :min-height="192"/>
-        </el-form-item>
-        <el-form-item label="分类id" prop="categoryIds">
-          <el-cascader
-              v-model="form.categoryIds"
-              placeholder="请输入分类id"
-              style="width: 100%"
-              :options="categoryList"
-              :props="propName"
-              clearable></el-cascader>
-        </el-form-item>
-        <el-form-item label="商品主图地址" prop="picUrls">
-          <ImageUpload v-model="form.picUrls" :limit="10"/>
-        </el-form-item>
-        <el-form-item label="商品规格">
-          <el-button size="mini" @click="shopTagInput()">添加规格</el-button>
-          <div v-for="(tag, tagIndex) in skuTags" :key="tagIndex">
-            <span>{{ tag.name }}</span>
-            <el-button style="margin-left: 10px" class="button-new-tag" type="text" icon="el-icon-delete"
-                       @click="removeTag(tagIndex)">删除
-            </el-button>
-            <br/>
-            <el-tag
-                v-for="(tagItem, tagItemIndex) in tag.selectValues"
-                :key="tagItem"
-                style="margin-right: 10px"
-                :disable-transitions="false">
-              {{ tagItem }}
-            </el-tag>
-            <!--            <el-input-->
-            <!--              class="input-new-tag"-->
-            <!--              v-if="tagItemInputs[tagIndex] && tagItemInputs[tagIndex].visible"-->
-            <!--              v-model="tagItemInputs[tagIndex].value"-->
-            <!--              :ref="`saveTagInput${tagIndex}`"-->
-            <!--              size="small"-->
-            <!--              @keyup.enter.native="handleInputConfirm(tagIndex)"-->
-            <!--              @blur="handleInputConfirm(tagIndex)">-->
-            <!--            </el-input>-->
-          </div>
-        </el-form-item>
-        <el-form-item label="规格名" v-show="isShowTagInput">
-          <el-col :span="8">
-            <el-select v-model="addTagInput.name" filterable allow-create default-first-option placeholder="请选择"
-                       @change="handleTagClick">
-              <el-option
-                  v-for="item in unUseTags"
-                  :key="item.id"
-                  :label="item.name"
-                  :value="item.name">
-              </el-option>
-            </el-select>
-          </el-col>
-        </el-form-item>
-        <el-form-item label="规格值" v-show="isShowTagInput">
-          <el-col :span="8">
-            <el-select v-model="addTagInput.selectValues" multiple filterable allow-create default-first-option
-                       placeholder="请选择">
-              <el-option
-                  v-for="item in dbTagValues"
-                  :key="item.id"
-                  :label="item.name"
-                  :value="item.name">
-              </el-option>
-            </el-select>
-          </el-col>
-        </el-form-item>
-        <el-form-item>
-          <el-button size="mini" type="primary" @click="addTag()" v-show="isShowTagInput">确定</el-button>
-          <el-button size="mini" @click="hideTagInput()" v-show="isShowTagInput">取消</el-button>
-        </el-form-item>
-        <el-form-item v-if="form.skus.length>0">
-          <el-table
-              :data="form.skus"
-              border
-              style="width: 100%; margin-top: 20px"
-              :span-method="tableSpanMethod">
-            <el-table-column v-for="(leftTitle, index) in skuTags" :key="index" :label="leftTitle.name">
-              <template slot-scope="scope">
-                {{ scope.row.propertyChildNames[index] }}
-              </template>
-            </el-table-column>
-            <el-table-column v-if="skuTags.length"
-                             prop="picUrl"
-                             label="sku图片"
-                             width="180">
-              <template slot-scope="scope">
-                <ImageUpload v-model="scope.row.picUrl" :limit="1">
-                </ImageUpload>
-              </template>
-            </el-table-column>
-            <el-table-column
-                prop="prodName"
-                label="条形码"
-                width="250" v-if="skuTags.length">
-              <template slot-scope="scope">
-                <el-input v-model="scope.row.barCode" type="textarea" :disabled="scope.row.status==1"></el-input>
-              </template>
-            </el-table-column>
-            <el-table-column
-                prop="price"
-                label="销售价">
-              <template slot-scope="scope">
-                <el-input-number
-                    size="small"
-                    v-model="scope.row.price"
-                    controls-position="right"
-                    :precision="2"
-                    :max="1000000000"
-                    :min="0.01"
-                    :disabled="scope.row.status==1">
-                </el-input-number>
-              </template>
-            </el-table-column>
-            <el-table-column
-                prop="oriPrice"
-                label="成本价">
-              <template slot-scope="scope">
-                <el-input-number
-                    size="small"
-                    v-model="scope.row.costPrice"
-                    controls-position="right"
-                    :precision="2"
-                    :max="1000000000"
-                    :min="0.01"
-                    :disabled="scope.row.status==1">
-                </el-input-number>
-              </template>
-            </el-table-column>
-            <el-table-column
-                prop="oriPrice"
-                label="原价">
-              <template slot-scope="scope">
-                <el-input-number
-                    size="small"
-                    v-model="scope.row.originalPrice"
-                    controls-position="right"
-                    :precision="2"
-                    :max="1000000000"
-                    :min="0.01"
-                    :disabled="scope.row.status==1">
-                </el-input-number>
-              </template>
-            </el-table-column>
-            <el-table-column
-                label="操作">
-              <template slot-scope="scope">
-                <el-button type="text" size="small" @click="changeSkuStatus(`${scope.$index}`)"
-                           v-if="scope.row.status===0">
-                  正常
-                </el-button>
-                <el-button type="text" size="small" @click="changeSkuStatus(`${scope.$index}`)" v-else>已禁用</el-button>
-              </template>
-            </el-table-column>
-          </el-table>
-        </el-form-item>
-        <el-form-item label="排序字段" prop="sort">
-          <el-input v-model="form.sort" placeholder="请输入排序字段"/>
-        </el-form-item>
-        <el-form-item label="点赞初始人数" prop="likeCount">
-          <el-input v-model="form.likeCount" placeholder="请输入点赞初始人数"/>
-        </el-form-item>
-        <el-form-item label="价格 单位使用:分" prop="price">
-          <el-input v-model="form.price" placeholder="请输入价格 单位使用:分"/>
-        </el-form-item>
-        <el-form-item label="库存数量" prop="quantity">
-          <el-input v-model="form.quantity" placeholder="请输入库存数量"/>
-        </el-form-item>
-        <el-form-item label="上下架状态" prop="status">
-          <el-radio-group v-model="form.status">
-            <el-radio label="0">上架</el-radio>
-            <el-radio label="1">下架</el-radio>
-          </el-radio-group>
-        </el-form-item>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
-      </div>
+    <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body destroy-on-close>
+      <save @closeDialog="open = false; getList()"/>
     </el-dialog>
     </el-dialog>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
-import {createSpu, updateSpu, deleteSpu, getSpu, getSpuPage, exportSpuExcel} from "@/api/mall/product/spu";
+import {
+  createSpu,
+  updateSpu,
+  deleteSpu,
+  getSpu,
+  getSpuPage,
+  exportSpuExcel,
+} from "@/api/mall/product/spu";
 import {
 import {
   createProductCategory,
   createProductCategory,
   deleteProductCategory,
   deleteProductCategory,
   exportCategoryExcel,
   exportCategoryExcel,
   getProductCategory,
   getProductCategory,
   getProductCategoryList,
   getProductCategoryList,
-  updateProductCategory
+  updateProductCategory,
 } from "@/api/mall/product/category";
 } from "@/api/mall/product/category";
 import {
 import {
   createProperty,
   createProperty,
@@ -296,16 +207,19 @@ import {
   deleteProperty,
   deleteProperty,
   getProperty,
   getProperty,
   getPropertyPage,
   getPropertyPage,
-  exportPropertyExcel
+  exportPropertyExcel,
 } from "@/api/mall/product/property";
 } from "@/api/mall/product/property";
 
 
-import Editor from '@/components/Editor';
-import ImageUpload from '@/components/ImageUpload';
+import Editor from "@/components/Editor";
+import ImageUpload from "@/components/ImageUpload";
+import save  from "./save";
 
 
 export default {
 export default {
   name: "Spu",
   name: "Spu",
   components: {
   components: {
-    Editor, ImageUpload
+    Editor,
+    ImageUpload,
+    save,
   },
   },
   data() {
   data() {
     return {
     return {
@@ -316,17 +230,17 @@ export default {
       propertyPageList: [],
       propertyPageList: [],
       isShowTagInput: false,
       isShowTagInput: false,
       addTagInput: {
       addTagInput: {
-        name: '',
-        propertyId: '',
+        name: "",
+        propertyId: "",
         selectValues: [],
         selectValues: [],
         selectValueIds: [],
         selectValueIds: [],
-        selectObect:[],
+        selectObect: [],
       },
       },
       skuTags: [],
       skuTags: [],
       propName: {
       propName: {
         checkStrictly: true,
         checkStrictly: true,
-        label: 'name',
-        value: 'id'
+        label: "name",
+        value: "id",
       },
       },
       categoryList: [],
       categoryList: [],
       // 遮罩层
       // 遮罩层
@@ -378,91 +292,101 @@ export default {
       },
       },
       // 表单校验
       // 表单校验
       rules: {
       rules: {
-        sellPoint: [{required: true, message: "卖点不能为空", trigger: "blur"}],
-        description: [{required: true, message: "描述不能为空", trigger: "blur"}],
-        categoryIds: [{required: true, message: "分类id不能为空", trigger: "blur"}],
-        picUrls: [{required: true, message: "商品主图地址", trigger: "blur"}],
-        sort: [{required: true, message: "排序字段不能为空", trigger: "blur"}],
+        sellPoint: [
+          { required: true, message: "卖点不能为空", trigger: "blur" },
+        ],
+        description: [
+          { required: true, message: "描述不能为空", trigger: "blur" },
+        ],
+        categoryIds: [
+          { required: true, message: "分类id不能为空", trigger: "blur" },
+        ],
+        picUrls: [{ required: true, message: "商品主图地址", trigger: "blur" }],
+        sort: [
+          { required: true, message: "排序字段不能为空", trigger: "blur" },
+        ],
       },
       },
       tagIndex: 0,
       tagIndex: 0,
     };
     };
   },
   },
   created() {
   created() {
-
     this.getList();
     this.getList();
     this.getPropertyPageList();
     this.getPropertyPageList();
     this.getListCategory();
     this.getListCategory();
   },
   },
   methods: {
   methods: {
     getTableSpecData() {
     getTableSpecData() {
-      return this.value
-    },
-    tableSpanMethod({row, column, rowIndex, columnIndex}) {
-
+      return this.value;
     },
     },
+    tableSpanMethod({ row, column, rowIndex, columnIndex }) {},
     changeSkuStatus(tagIndex) {
     changeSkuStatus(tagIndex) {
       if (this.form.skus[tagIndex].status == 0) {
       if (this.form.skus[tagIndex].status == 0) {
         this.form.skus[tagIndex].status = 1;
         this.form.skus[tagIndex].status = 1;
       } else {
       } else {
         this.form.skus[tagIndex].status = 0;
         this.form.skus[tagIndex].status = 0;
       }
       }
-
     },
     },
     skuAddProdName() {
     skuAddProdName() {
       if (this.initing) {
       if (this.initing) {
-        return
+        return;
       }
       }
-      let skuList = []
+      let skuList = [];
       for (let i = 0; i < this.value.length; i++) {
       for (let i = 0; i < this.value.length; i++) {
-        const sku = Object.assign({}, this.value[i])
+        const sku = Object.assign({}, this.value[i]);
         if (!sku.properties) {
         if (!sku.properties) {
-          return
+          return;
         }
         }
-        sku.skuName = ''
-        let properties = sku.properties.split(';')
+        sku.skuName = "";
+        let properties = sku.properties.split(";");
         for (const propertiesKey in properties) {
         for (const propertiesKey in properties) {
-          sku.skuName += properties[propertiesKey].split(':')[1] + ' '
+          sku.skuName += properties[propertiesKey].split(":")[1] + " ";
         }
         }
-        sku.prodName = this.prodName + ' ' + sku.skuName
-        skuList.push(sku)
+        sku.prodName = this.prodName + " " + sku.skuName;
+        skuList.push(sku);
       }
       }
-      this.$emit('input', skuList)
-    },
-    handleTagClose(tagIndex, tagItemIndex) {
-
+      this.$emit("input", skuList);
     },
     },
+    handleTagClose(tagIndex, tagItemIndex) {},
     //确定添加sku规格
     //确定添加sku规格
     addTag() {
     addTag() {
-
       let skus = this.unUseTags.map(function (item, index) {
       let skus = this.unUseTags.map(function (item, index) {
-        return item.name
+        return item.name;
       });
       });
       let index = skus.indexOf(this.addTagInput.name);
       let index = skus.indexOf(this.addTagInput.name);
 
 
       this.addTagInput.propertyId = this.unUseTags[index].id;
       this.addTagInput.propertyId = this.unUseTags[index].id;
       for (let i = 0; i < this.addTagInput.selectValues.length; i++) {
       for (let i = 0; i < this.addTagInput.selectValues.length; i++) {
-        for (let j = 0; j < this.unUseTags[index].propertyValueList.length; j++) {
-          if (this.addTagInput.selectValues[i] === this.unUseTags[index].propertyValueList[j].name) {
-            this.addTagInput.selectValueIds.push(this.unUseTags[index].propertyValueList[j].id)
+        for (
+          let j = 0;
+          j < this.unUseTags[index].propertyValueList.length;
+          j++
+        ) {
+          if (
+            this.addTagInput.selectValues[i] ===
+            this.unUseTags[index].propertyValueList[j].name
+          ) {
+            this.addTagInput.selectValueIds.push(
+              this.unUseTags[index].propertyValueList[j].id
+            );
             this.addTagInput.selectObect.push({
             this.addTagInput.selectObect.push({
-              id:this.unUseTags[index].propertyValueList[j].id,
-              name:this.unUseTags[index].propertyValueList[j].name,
-            })
+              id: this.unUseTags[index].propertyValueList[j].id,
+              name: this.unUseTags[index].propertyValueList[j].name,
+            });
           }
           }
         }
         }
       }
       }
-      let addTagInput = JSON.parse(JSON.stringify(this.addTagInput))
+      let addTagInput = JSON.parse(JSON.stringify(this.addTagInput));
       this.skuTags.push(addTagInput);
       this.skuTags.push(addTagInput);
 
 
       // if (this.skuTags.length > 1) {
       // if (this.skuTags.length > 1) {
       this.skuTags = this.skuTags.sort((a, b) => a.propertyId - b.propertyId);
       this.skuTags = this.skuTags.sort((a, b) => a.propertyId - b.propertyId);
-      this.skuTags.forEach(function (item,index) {
-        item.selectObect = item.selectObect.sort((a, b) => a.id - b.id)
-      })
+      this.skuTags.forEach(function (item, index) {
+        item.selectObect = item.selectObect.sort((a, b) => a.id - b.id);
+      });
 
 
-      for (let i = 0; i <this.skuTags.length ; i++) {
-        let selectValueIds=[];
-        let selectValues=[];
+      for (let i = 0; i < this.skuTags.length; i++) {
+        let selectValueIds = [];
+        let selectValues = [];
         for (let j = 0; j < this.skuTags[i].selectObect.length; j++) {
         for (let j = 0; j < this.skuTags[i].selectObect.length; j++) {
           selectValueIds.push(this.skuTags[i].selectObect[j].id);
           selectValueIds.push(this.skuTags[i].selectObect[j].id);
           selectValues.push(this.skuTags[i].selectObect[j].name);
           selectValues.push(this.skuTags[i].selectObect[j].name);
@@ -489,17 +413,23 @@ export default {
         propertyNames.push(skuTags[i].name);
         propertyNames.push(skuTags[i].name);
       }
       }
 
 
-      let skuAll = sku1s.reduce((x, y) => {
-        let arr = [];
-        x.forEach(m => y.forEach(y => arr.push(m.concat([y]))))
-        return arr;
-      }, [[]])
+      let skuAll = sku1s.reduce(
+        (x, y) => {
+          let arr = [];
+          x.forEach((m) => y.forEach((y) => arr.push(m.concat([y]))));
+          return arr;
+        },
+        [[]]
+      );
 
 
-      let skuIdAll = skuIds.reduce((x, y) => {
-        let arr = [];
-        x.forEach(m => y.forEach(y => arr.push(m.concat([y]))))
-        return arr;
-      }, [[]])
+      let skuIdAll = skuIds.reduce(
+        (x, y) => {
+          let arr = [];
+          x.forEach((m) => y.forEach((y) => arr.push(m.concat([y]))));
+          return arr;
+        },
+        [[]]
+      );
 
 
       for (let i = 0; i < skuAll.length; i++) {
       for (let i = 0; i < skuAll.length; i++) {
         let han = {
         let han = {
@@ -508,33 +438,32 @@ export default {
           propertyChildNames: skuAll[i],
           propertyChildNames: skuAll[i],
           propertyChildIds: skuIdAll[i],
           propertyChildIds: skuIdAll[i],
           properties: [],
           properties: [],
-          picUrl: '',
-          costPrice: '',
-          originalPrice: '',
-          spuId: '',
-          prodName: '',
-          price: '',
-          barCode: '',
-          status: '0',
-        }
+          picUrl: "",
+          costPrice: "",
+          originalPrice: "",
+          spuId: "",
+          prodName: "",
+          price: "",
+          barCode: "",
+          status: "0",
+        };
         this.form.skus.push(han);
         this.form.skus.push(han);
       }
       }
-      this.form.skus.forEach(x => {
+      this.form.skus.forEach((x) => {
         x.properties = [];
         x.properties = [];
         for (let i = 0; i < x.propertyIds.length; i++) {
         for (let i = 0; i < x.propertyIds.length; i++) {
           x.properties.push({
           x.properties.push({
             propertyId: x.propertyIds[i],
             propertyId: x.propertyIds[i],
-            valueId: x.propertyChildIds[i]
-          })
+            valueId: x.propertyChildIds[i],
+          });
         }
         }
-      })
-
+      });
     },
     },
     hideTagInput() {
     hideTagInput() {
       this.isShowTagInput = false;
       this.isShowTagInput = false;
       this.addTagInput = {
       this.addTagInput = {
-        name: '',
-        propertyId: '',
+        name: "",
+        propertyId: "",
         selectValues: [],
         selectValues: [],
         selectValueIds: [],
         selectValueIds: [],
         selectObect: [],
         selectObect: [],
@@ -542,12 +471,12 @@ export default {
     },
     },
     shopTagInput() {
     shopTagInput() {
       if (this.unUseTags.length <= 0) {
       if (this.unUseTags.length <= 0) {
-        return this.$message.error("规格已经添加完毕")
+        return this.$message.error("规格已经添加完毕");
       }
       }
       this.isShowTagInput = true;
       this.isShowTagInput = true;
       this.addTagInput = {
       this.addTagInput = {
-        name: '',
-        propertyId: '',
+        name: "",
+        propertyId: "",
         selectValues: [],
         selectValues: [],
         selectValueIds: [],
         selectValueIds: [],
         selectObect: [],
         selectObect: [],
@@ -556,8 +485,8 @@ export default {
     //删除已选的规格
     //删除已选的规格
     removeTag(row) {
     removeTag(row) {
       let skus = this.allhistoryTags.map(function (item, index) {
       let skus = this.allhistoryTags.map(function (item, index) {
-        return item.name
-      })
+        return item.name;
+      });
       let index = skus.indexOf(this.skuTags[row].name);
       let index = skus.indexOf(this.skuTags[row].name);
       this.unUseTags.push(this.allhistoryTags[index]);
       this.unUseTags.push(this.allhistoryTags[index]);
       this.skuTags.splice(row, 1);
       this.skuTags.splice(row, 1);
@@ -566,38 +495,37 @@ export default {
     handleTagClick(row) {
     handleTagClick(row) {
       for (let i = 0; i < this.propertyPageList.length; i++) {
       for (let i = 0; i < this.propertyPageList.length; i++) {
         if (row == this.propertyPageList[i].name) {
         if (row == this.propertyPageList[i].name) {
-          this.dbTagValues = this.propertyPageList[i].propertyValueList
+          this.dbTagValues = this.propertyPageList[i].propertyValueList;
         }
         }
       }
       }
     },
     },
     /** 查询规格 */
     /** 查询规格 */
     getPropertyPageList() {
     getPropertyPageList() {
       // 执行查询
       // 执行查询
-      getPropertyPage().then(response => {
+      getPropertyPage().then((response) => {
         this.propertyPageList = response.data.list;
         this.propertyPageList = response.data.list;
 
 
         this.unUseTags = this.propertyPageList.map(function (item, index) {
         this.unUseTags = this.propertyPageList.map(function (item, index) {
-          return item
-        })
+          return item;
+        });
         this.allhistoryTags = JSON.parse(JSON.stringify(this.unUseTags));
         this.allhistoryTags = JSON.parse(JSON.stringify(this.unUseTags));
       });
       });
     },
     },
     /** 查询分类 */
     /** 查询分类 */
     getListCategory() {
     getListCategory() {
       // 执行查询
       // 执行查询
-      getProductCategoryList().then(response => {
+      getProductCategoryList().then((response) => {
         this.categoryList = this.handleTree(response.data, "id", "parentId");
         this.categoryList = this.handleTree(response.data, "id", "parentId");
-
       });
       });
     },
     },
     /** 查询列表 */
     /** 查询列表 */
     getList() {
     getList() {
       this.loading = true;
       this.loading = true;
       // 处理查询参数
       // 处理查询参数
-      let params = {...this.queryParams};
-      this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
+      let params = { ...this.queryParams };
+      this.addBeginAndEndTime(params, this.dateRangeCreateTime, "createTime");
       // 执行查询
       // 执行查询
-      getSpuPage(params).then(response => {
+      getSpuPage(params).then((response) => {
         this.list = response.data.list;
         this.list = response.data.list;
         this.total = response.data.total;
         this.total = response.data.total;
         this.loading = false;
         this.loading = false;
@@ -610,7 +538,6 @@ export default {
     },
     },
     /** 表单重置 */
     /** 表单重置 */
     reset() {
     reset() {
-
       this.form = {
       this.form = {
         id: undefined,
         id: undefined,
         name: undefined,
         name: undefined,
@@ -652,7 +579,7 @@ export default {
     handleUpdate(row) {
     handleUpdate(row) {
       this.reset();
       this.reset();
       const id = row.id;
       const id = row.id;
-      getSpu(id).then(response => {
+      getSpu(id).then((response) => {
         let dataSpu = response.data;
         let dataSpu = response.data;
         this.form = {
         this.form = {
           id: dataSpu.id,
           id: dataSpu.id,
@@ -680,11 +607,15 @@ export default {
     },
     },
     getDataHandle() {
     getDataHandle() {
       let that = this;
       let that = this;
-      let productPropertyViews = JSON.parse(JSON.stringify(this.form.productPropertyViews));
-      productPropertyViews = productPropertyViews.sort((a, b) => a.propertyId - b.propertyId);
-      productPropertyViews.forEach(item => {
+      let productPropertyViews = JSON.parse(
+        JSON.stringify(this.form.productPropertyViews)
+      );
+      productPropertyViews = productPropertyViews.sort(
+        (a, b) => a.propertyId - b.propertyId
+      );
+      productPropertyViews.forEach((item) => {
         item.propertyValues = item.propertyValues.sort((a, b) => a.v1 - b.v1);
         item.propertyValues = item.propertyValues.sort((a, b) => a.v1 - b.v1);
-      })
+      });
       let skuIds = [];
       let skuIds = [];
       for (let i = 0; i < productPropertyViews.length; i++) {
       for (let i = 0; i < productPropertyViews.length; i++) {
         let han = {
         let han = {
@@ -692,17 +623,21 @@ export default {
           propertyId: productPropertyViews[i].propertyId,
           propertyId: productPropertyViews[i].propertyId,
           selectValues: [],
           selectValues: [],
           selectValueIds: [],
           selectValueIds: [],
-        }
-        for (let j = 0; j < productPropertyViews[i].propertyValues.length; j++) {
+        };
+        for (
+          let j = 0;
+          j < productPropertyViews[i].propertyValues.length;
+          j++
+        ) {
           han.selectValues.push(productPropertyViews[i].propertyValues[j].v2);
           han.selectValues.push(productPropertyViews[i].propertyValues[j].v2);
           han.selectValueIds.push(productPropertyViews[i].propertyValues[j].v1);
           han.selectValueIds.push(productPropertyViews[i].propertyValues[j].v1);
         }
         }
-        skuIds.push(han)
+        skuIds.push(han);
       }
       }
       this.skuTags = skuIds;
       this.skuTags = skuIds;
       this.unUseTags = this.allhistoryTags.filter((v) =>
       this.unUseTags = this.allhistoryTags.filter((v) =>
-          skuIds.every((val) => val.name != v.name)
-      )
+        skuIds.every((val) => val.name != v.name)
+      );
       this.getHandleTable();
       this.getHandleTable();
     },
     },
     getHandleTable() {
     getHandleTable() {
@@ -718,17 +653,23 @@ export default {
         propertyIds.push(skuTags[i].propertyId);
         propertyIds.push(skuTags[i].propertyId);
         propertyNames.push(skuTags[i].name);
         propertyNames.push(skuTags[i].name);
       }
       }
-      let skuAll = sku1s.reduce((x, y) => {
-        let arr = [];
-        x.forEach(m => y.forEach(y => arr.push(m.concat([y]))))
-        return arr;
-      }, [[]])
+      let skuAll = sku1s.reduce(
+        (x, y) => {
+          let arr = [];
+          x.forEach((m) => y.forEach((y) => arr.push(m.concat([y]))));
+          return arr;
+        },
+        [[]]
+      );
 
 
-      let skuIdAll = skuIds.reduce((x, y) => {
-        let arr = [];
-        x.forEach(m => y.forEach(y => arr.push(m.concat([y]))))
-        return arr;
-      }, [[]])
+      let skuIdAll = skuIds.reduce(
+        (x, y) => {
+          let arr = [];
+          x.forEach((m) => y.forEach((y) => arr.push(m.concat([y]))));
+          return arr;
+        },
+        [[]]
+      );
 
 
       for (let i = 0; i < skuAll.length; i++) {
       for (let i = 0; i < skuAll.length; i++) {
         let han = {
         let han = {
@@ -745,33 +686,32 @@ export default {
           price: this.form.skusList[i].price,
           price: this.form.skusList[i].price,
           barCode: this.form.skusList[i].barCode,
           barCode: this.form.skusList[i].barCode,
           status: this.form.skusList[i].status,
           status: this.form.skusList[i].status,
-        }
+        };
         this.form.skus.push(han);
         this.form.skus.push(han);
       }
       }
-      this.form.skus.forEach(x => {
+      this.form.skus.forEach((x) => {
         x.properties = [];
         x.properties = [];
         for (let i = 0; i < x.propertyIds.length; i++) {
         for (let i = 0; i < x.propertyIds.length; i++) {
           x.properties.push({
           x.properties.push({
             propertyId: x.propertyIds[i],
             propertyId: x.propertyIds[i],
-            valueId: x.propertyChildIds[i]
-          })
+            valueId: x.propertyChildIds[i],
+          });
         }
         }
-      })
-
+      });
     },
     },
     /** 提交按钮 */
     /** 提交按钮 */
     submitForm() {
     submitForm() {
-
-      this.$refs["form"].validate(valid => {
+      this.$refs["form"].validate((valid) => {
         if (!valid) {
         if (!valid) {
           return;
           return;
         }
         }
-        this.form.picUrls = this.form.picUrls.split(',');
-        this.form.categoryId = this.form.categoryIds[(this.form.categoryIds.length - 1)];
+        this.form.picUrls = this.form.picUrls.split(",");
+        this.form.categoryId =
+          this.form.categoryIds[this.form.categoryIds.length - 1];
         this.form.status = Number(this.form.status);
         this.form.status = Number(this.form.status);
         // 修改的提交
         // 修改的提交
         if (this.form.id != null) {
         if (this.form.id != null) {
-          updateSpu(this.form).then(response => {
+          updateSpu(this.form).then((response) => {
             this.$modal.msgSuccess("修改成功");
             this.$modal.msgSuccess("修改成功");
             this.open = false;
             this.open = false;
             this.getList();
             this.getList();
@@ -779,7 +719,7 @@ export default {
           return;
           return;
         }
         }
         // 添加的提交
         // 添加的提交
-        createSpu(this.form).then(response => {
+        createSpu(this.form).then((response) => {
           this.$modal.msgSuccess("新增成功");
           this.$modal.msgSuccess("新增成功");
           this.open = false;
           this.open = false;
           this.getList();
           this.getList();
@@ -789,32 +729,38 @@ export default {
     /** 删除按钮操作 */
     /** 删除按钮操作 */
     handleDelete(row) {
     handleDelete(row) {
       const id = row.id;
       const id = row.id;
-      this.$modal.confirm('是否确认删除商品spu编号为"' + id + '"的数据项?').then(function () {
-        return deleteSpu(id);
-      }).then(() => {
-        this.getList();
-        this.$modal.msgSuccess("删除成功");
-      }).catch(() => {
-      });
+      this.$modal
+        .confirm('是否确认删除商品spu编号为"' + id + '"的数据项?')
+        .then(function () {
+          return deleteSpu(id);
+        })
+        .then(() => {
+          this.getList();
+          this.$modal.msgSuccess("删除成功");
+        })
+        .catch(() => {});
     },
     },
     /** 导出按钮操作 */
     /** 导出按钮操作 */
     handleExport() {
     handleExport() {
       // 处理查询参数
       // 处理查询参数
-      let params = {...this.queryParams};
+      let params = { ...this.queryParams };
       params.pageNo = undefined;
       params.pageNo = undefined;
       params.pageSize = undefined;
       params.pageSize = undefined;
-      this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
+      this.addBeginAndEndTime(params, this.dateRangeCreateTime, "createTime");
       // 执行导出
       // 执行导出
-      this.$modal.confirm('是否确认导出所有商品spu数据项?').then(() => {
-        this.exportLoading = true;
-        return exportSpuExcel(params);
-      }).then(response => {
-        this.$download.excel(response, '商品spu.xls');
-        this.exportLoading = false;
-      }).catch(() => {
-      });
-    }
-  }
+      this.$modal
+        .confirm("是否确认导出所有商品spu数据项?")
+        .then(() => {
+          this.exportLoading = true;
+          return exportSpuExcel(params);
+        })
+        .then((response) => {
+          this.$download.excel(response, "商品spu.xls");
+          this.exportLoading = false;
+        })
+        .catch(() => {});
+    },
+  },
 };
 };
 </script>
 </script>
 <style lang="scss">
 <style lang="scss">

+ 446 - 0
yudao-ui-admin/src/views/mall/product/spu/save.vue

@@ -0,0 +1,446 @@
+<template>
+  <div class="container">
+    <el-tabs v-model="activeName" class="tabs">
+      <!-- 基础设置 -->
+      <el-tab-pane label="基础设置" name="base">
+        <el-form ref="baseForm" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
+          <el-form-item label="商品名称" prop="name">
+            <el-input v-model="baseForm.name" placeholder="请输入商品名称"/>
+          </el-form-item>
+          <el-form-item label="卖点" prop="sellPoint">
+            <el-input v-model="baseForm.sellPoint" placeholder="请输入卖点"/>
+          </el-form-item>
+          <el-form-item label="分类id" prop="categoryIds">
+            <el-cascader
+              v-model="baseForm.categoryIds"
+              placeholder="请输入分类id"
+              style="width: 100%"
+              :options="categoryList"
+              :props="propName"
+              clearable
+            ></el-cascader>
+          </el-form-item>
+          <el-form-item label="商品主图" prop="bannerUrl">
+            <ImageUpload v-model="baseForm.bannerUrl" :limit="1"/>
+          </el-form-item>
+          <el-form-item label="商品轮播图" prop="picUrls">
+            <ImageUpload v-model="baseForm.picUrls" :limit="10"/>
+          </el-form-item>
+          <el-form-item label="排序字段" prop="sort">
+            <el-input v-model="baseForm.sort" placeholder="请输入排序字段"/>
+          </el-form-item>
+        </el-form>
+      </el-tab-pane>
+
+      <!-- 价格库存 -->
+      <el-tab-pane label="价格库存" name="rates" class="rates">
+        <el-form :model="ratesForm" :rules="rules.ratesForm">
+          <el-form-item label="商品规格">
+            <el-radio-group v-model="ratesForm.spec" @change="changeRadio">
+              <el-radio :label="0">单规格</el-radio>
+              <el-radio :label="1">多规格</el-radio>
+            </el-radio-group>
+          </el-form-item>
+
+          <!-- 动态添加规格属性 -->
+          <div v-show="ratesForm.spec === 1">
+            <div
+              v-for="(specs, index) in dynamicSpec"
+              :key="index"
+              class="dynamic-spec"
+            >
+              <!-- 删除按钮 -->
+              <el-button
+                type="danger"
+                icon="el-icon-delete"
+                circle
+                class="spec-delete"
+                @click="dynamicSpec.splice(index, 1)"
+              ></el-button>
+
+              <div class="spec-header">
+                规格项:
+                <el-select v-model="specs.specId" filterable placeholder="请选择" @change="changeSpec">
+                  <el-option
+                    v-for="item in propertyPageList"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id">
+                  </el-option>
+                </el-select>
+              </div>
+              <div class="spec-values">
+                <template v-for="(v, i) in specs.specValue">
+                  <el-input v-model="v.name" class="spec-value" :key="i"/>
+                </template>
+                <!-- <el-button
+                  type="primary"
+                  icon="el-icon-plus"
+                  circle
+                  @click="dialogForSpec = true; currentSpec = index"
+                ></el-button> -->
+              </div>
+            </div>
+            <el-button type="primary" @click="dynamicSpec.push({specValue: []}); ratesForm.rates = []"
+            >添加规格项目
+            </el-button
+            >
+          </div>
+
+          <!-- 规格明细 -->
+          <el-form-item label="规格明细">
+            <el-table :data="ratesForm.rates" border style="width: 100%" ref="rates">
+
+              <template v-if="ratesForm.spec == 1">
+                <el-table-column :key="index" v-for="(item, index) in dynamicSpec.filter(v=>v.specName != undefined)"
+                                 :label="item.specName">
+                  <template slot-scope="scope">
+                    <el-input
+                      v-if="scope.row.spec"
+                      v-model="scope.row.spec[index]"
+                      disabled
+                    ></el-input>
+                  </template>
+                </el-table-column>
+              </template>
+
+              <el-table-column label="规格图片" width="120px">
+                <template slot-scope="scope">
+                  <ImageUpload v-model="scope.row.picUrl" :limit="1" :isShowTip="false"
+                               style="width: 100px; height: 50px"/>
+                </template>
+              </el-table-column>
+              <el-table-column label="市场价(元)">
+                <template slot-scope="scope">
+                  <el-input
+                    v-model="scope.row.marketPrice"
+                    type="number"
+                  ></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="销售价(元)" re>
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.price" type="number"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="成本价">
+                <template slot-scope="scope">
+                  <el-input
+                    v-model="scope.row.costPrice"
+                    type="number"
+                  ></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="库存">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.stock" type="number"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="体积">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.volume" type="number"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="重量">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.weight" type="number"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="条码">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.barCode"></el-input>
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-form-item>
+        </el-form>
+      </el-tab-pane>
+
+      <!-- 商品详情 -->
+      <el-tab-pane label="商品描述" name="third">
+        <editor v-model="baseForm.description" :min-height="400"/>
+      </el-tab-pane>
+
+      <!-- 销售设置 -->
+      <el-tab-pane label="销售设置" name="fourth">
+        <el-form ref="baseForm" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
+          <el-form-item label="库存数量" prop="totalStock">
+            <el-input v-model="baseForm.totalStock" placeholder="请输入库存数量"/>
+          </el-form-item>
+          <el-form-item label="上下架状态" prop="status">
+            <el-radio-group v-model="baseForm.status">
+              <el-radio :label="0">上架</el-radio>
+              <el-radio :label="1">下架</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-form>
+      </el-tab-pane>
+    </el-tabs>
+
+    <div class="buttons">
+      <el-button type="info" round @click="cancel">取消</el-button>
+      <el-button type="success" round @click="submit">确认</el-button>
+    </div>
+
+  </div>
+</template>
+
+<script>
+import {getProductCategoryList} from "@/api/mall/product/category";
+import {createSpu,} from "@/api/mall/product/spu";
+import {getPropertyPage,} from "@/api/mall/product/property";
+import Editor from "@/components/Editor";
+import ImageUpload from "@/components/ImageUpload";
+
+export default {
+  components: {
+    Editor,
+    ImageUpload
+  },
+  data() {
+    return {
+      activeName: "base",
+      propName: {
+        checkStrictly: true,
+        label: "name",
+        value: "id",
+      },
+      // 基础设置
+      baseForm: {
+        name: null,
+        sellPoint: null,
+        categoryIds: null,
+        sort: null,
+        description: null,
+        bannerUrl: null,
+        picUrls: [],
+        totalStock: null,
+        status: 0
+      },
+      categoryList: [],
+      // 价格库存
+      ratesForm: {
+        spec: 0,
+        // 规格明细
+        rates: [{}]
+      },
+      dynamicSpec: [
+        // {
+        //   specId: 86,
+        //   specName: "颜色",
+        //   specValue:[{
+        //      name: "红色",
+        //      id: 225,
+        //   }]
+        // },
+      ],
+      propertyPageList: [],
+      specValue: null,
+
+      // 表单校验
+      rules: {
+        description: [
+          {required: true, message: "描述不能为空", trigger: "blur"},
+        ],
+        categoryIds: [
+          {required: true, message: "分类id不能为空", trigger: "blur"},
+        ],
+        bannerUrl: [{required: true, message: "商品主图地址", trigger: "blur"}],
+        sort: [
+          {required: true, message: "排序字段不能为空", trigger: "blur"},
+        ],
+      },
+    };
+  },
+  created() {
+    this.getListCategory();
+    this.getPropertyPageList();
+  },
+  methods: {
+    changeRadio() {
+
+      this.$refs.rates.doLayout()
+      if (this.ratesForm.spec == 0) {
+        this.ratesForm.rates = [{}]
+      } else {
+        this.ratesForm.rates = []
+        if (this.dynamicSpec.length > 0) {
+          this.buildRatesFormRates()
+        }
+      }
+    },
+    // 构建规格明细笛卡尔积
+    buildRatesFormRates() {
+      let rates = [];
+      this.dynamicSpec.map(v => v.specValue.map(m => m.name))
+        .reduce((last, current) => {
+          const array = [];
+          last.forEach(par1 => {
+            current.forEach(par2 => {
+              let v
+              if (par1 instanceof Array) {
+                v = par1.concat(par2)
+              } else {
+                v = [par1, par2];
+              }
+              array.push(v)
+            });
+          });
+          return array;
+        })
+        .forEach(v => {
+          rates.push({spec: v})
+        });
+      this.ratesForm.rates = rates
+    },
+    /** 查询分类 */
+    getListCategory() {
+      // 执行查询
+      getProductCategoryList().then((response) => {
+        this.categoryList = this.handleTree(response.data, "id", "parentId");
+      });
+    },
+    cancel() {
+      this.$emit("closeDialog");
+    },
+    submit() {
+      let rates = this.ratesForm.rates;
+      // 动态规格调整字段
+      if (this.ratesForm.spec == 1) {
+        rates.forEach(r => {
+          let properties = []
+          if(r.spec instanceof Array){
+            r.spec.forEach((v, i) => {
+              let specValue = this.dynamicSpec[i].specValue.find(o => o.name == v);
+              let propertie = {};
+              propertie.propertyId = this.dynamicSpec[i].specId;
+              propertie.valueId = specValue.id;
+              properties.push(propertie);
+            })
+          }else{
+              let specValue = this.dynamicSpec[0].specValue.find(o => o.name == r.spec);
+              let propertie = {};
+              propertie.propertyId = this.dynamicSpec[0].specId;
+              propertie.valueId = specValue.id;
+              properties.push(propertie);
+          }
+          r.properties = properties;
+        })
+      }
+      this.baseForm.skus = rates;
+      this.baseForm.specType = this.ratesForm.spec;
+      this.baseForm.categoryId = this.baseForm.categoryIds[this.baseForm.categoryIds.length - 1];
+      createSpu(this.baseForm).then((response) => {
+        console.log(response)
+        this.$modal.msgSuccess("新增成功");
+        this.$emit("closeDialog");
+      });
+
+    },
+    /** 查询规格 */
+    getPropertyPageList() {
+      // 执行查询
+      getPropertyPage().then((response) => {
+        this.propertyPageList = response.data.list;
+      });
+    },
+    changeSpec(val) {
+      let obj = this.propertyPageList.find(o => o.id == val);
+      let dynamicSpec = this.dynamicSpec;
+      let spec = dynamicSpec.find(o => o.specId == val)
+      spec.specId = obj.id;
+      spec.specName = obj.name;
+      spec.specValue = obj.propertyValueList;
+      this.dynamicSpec = dynamicSpec;
+      this.buildRatesFormRates();
+    }
+  },
+};
+</script>
+
+<style lang="scss">
+.spec-dialog {
+  width: 400px;
+  height: 300px;
+}
+
+.dynamic-spec {
+  background-color: #f2f2f2;
+  width: 85%;
+  margin: auto;
+  margin-bottom: 10px;
+
+  .spec-header {
+    padding: 30px;
+    padding-bottom: 20px;
+
+    .spec-name {
+      display: inline;
+
+      input {
+        width: 30%;
+      }
+    }
+  }
+
+  .spec-values {
+    width: 84%;
+    padding: 25px;
+    margin: auto;
+    padding-top: 5px;
+
+    .spec-value {
+      display: inline-block;
+      margin-right: 10px;
+      margin-bottom: 10px;
+      width: 13%;
+
+    }
+  }
+
+  .spec-delete {
+    float: right;
+    margin-top: 10px;
+    margin-right: 10px;
+  }
+}
+
+.tabs {
+  height: 500px;
+  border-bottom: 2px solid #f2f2f2;
+
+  .el-tab-pane {
+    height: 445px;
+    overflow-y: auto;
+  }
+}
+
+// 库存价格图片样式修改
+.rates {
+  .component-upload-image {
+    margin: auto;
+  }
+
+  .el-upload--picture-card {
+    width: 100px;
+    height: 50px;
+    line-height: 60px;
+    margin: auto;
+  }
+
+  .el-upload-list__item {
+    width: 100px !important;
+    height: 50px !important;
+  }
+}
+
+.buttons {
+  margin-top: 20px;
+  height: 36px;
+
+  button {
+    float: right;
+    margin-left: 15px;
+  }
+}
+</style>