Эх сурвалжийг харах

feat: 完善商品添加规格

Sun 3 жил өмнө
parent
commit
e31b943120

+ 28 - 6
src/views/business/goods/add.vue

@@ -31,7 +31,7 @@
         </el-col>
       </el-row>
       <el-divider content-position="left">价格库存</el-divider>
-      <el-row :gutter="40">
+      <el-row>
         <el-col :span="23">
           <el-form-item label="启用多SKU:" prop="multiSku">
             <el-switch
@@ -46,7 +46,14 @@
           </el-form-item>
         </el-col>
       </el-row>
-      <Spec :multiSku="addData.multiSku" />
+      <el-row>
+        <el-col :span="23">
+          <el-form-item prop="skuList">
+            <Spec ref="spec" @valid="update" :multiSku="addData.multiSku" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      
       <el-row v-if="addData.multiSku === 0" :gutter="40">
         <el-col :span="23">
           <el-form-item label="价格:" prop="value">
@@ -101,6 +108,7 @@ import Spec from './components/spec'
 import TinyEditor from '@/components/TinyEditor'
 import { getGoodsDetail, addGoods } from '@/api/business/goods'
 import { publicFileGetUrl } from "@/api/common"
+import { accDiv, accMul } from '@/utils/util'
 export default {
   components: {
     TinyEditor,
@@ -158,17 +166,31 @@ export default {
     if (this.id) {
       getGoodsDetail(this.id).then(res => {
         const { goodsId, title, picUrl, exchangeShow, multiSku, value, exchangePrice, cost, quantity, description } = res.data
-        this.addData = { goodsId, title, picUrl, exchangeShow, multiSku, value, exchangePrice, cost, quantity, description }
+        this.addData = {
+          value: accDiv(value, 100),
+          cost: accDiv(cost, 100),
+          goodsId, title, picUrl, exchangeShow, multiSku, exchangePrice, quantity, description }
       })
     }
   },
   methods: {
     updateItem() {
+      if (this.addData.multiSku == 1) {
+        this.$refs.spec.getSkuList()
+      } else {
+        this.update()
+      }
+    },
+    update(skuList) {
       this.$refs.addItem.validate((valid, items) => {
         if (valid) {
-          this.addData.skuList = this.products
-          const { description, ...rest } = this.addData
-          addGoods({...rest, ...{description: encodeURI(description) }}).then(res => {
+          this.addData.skuList = skuList
+          const { value, cost, description, ...rest } = this.addData
+          addGoods({...rest, ...{
+            value: accMul(value, 100),
+            cost: accMul(cost, 100),
+            description: encodeURI(description)
+          }}).then(res => {
             if (res.code === 0) {
               this.$message({
                 message: this.addData.goodsId ? '修改成功!' : '添加成功!',

+ 170 - 151
src/views/business/goods/components/spec.vue

@@ -1,89 +1,94 @@
 <template>
-  <div>
-    <div v-if="multipleSpec">
-      <div style="margin-bottom: 16px">
-        <el-button :plain="true" type="primary" size="small" @click="handleSpecificationShow">添加规格</el-button>
+  <div v-if="multiSku === 1" style="padding: 16px 0px">
+    <el-form ref="spec" :model="{ specList }" label-width="80px">
+      <div v-for="(spec, index) in specList" :key="index" style="margin-bottom: 20px">
+        <el-row style="margin-bottom: 10px">
+          <el-col :span="8">
+            <el-form-item :label="`规格项${index + 1}`" :prop="`specList.${index}.name`" :rules="{ required: true, message: '请输入规格名称', trigger: 'blur' }">
+              <el-input v-model="spec.name" :disabled="!spec.edit" @keyup.enter.native="spec.edit = false" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="16">
+            <el-button v-if="spec.edit" type="text" style="margin-left: 10px" @click="spec.edit = false">确定</el-button>
+            <template v-else>
+              <el-button type="text" style="margin-left: 10px" @click="spec.edit = true">编辑</el-button>
+              <el-button type="text" class="del" style="margin-left: 10px" @click="specList.splice(index, 1)">删除</el-button>
+            </template>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-form-item :prop="`specList.${index}.vals`" :rules="{ required: true, message: '请完善规格值', trigger: 'blur' }">
+            <el-col :span="24">
+              <el-tag v-for="(tag, index) in spec.vals" :key="index" closable style="margin-right: 10px" @close="spec.vals.splice(index, 1)">{{ tag }}</el-tag>
+              <el-input v-if="spec.editVals" v-model="specValTmp" style="display: inline-block;width: 120px; margin-right: 10px" size="mini" @keyup.enter.native="checkSpecVal(spec)" />
+              <el-button v-if="!spec.edit&&!spec.editVals" type="text" style="margin-left: 10px" icon="el-icon-plus" size="small" @click="spec.editVals = true">添加规格值</el-button>
+              <el-button v-if="spec.editVals" type="text" @click="checkSpecVal(spec)">确认</el-button>
+            </el-col>
+          </el-form-item>
+        </el-row>
       </div>
-      <el-table :data="specifications">
-        <el-table-column property="specification" label="规格名"/>
-        <el-table-column property="value" label="规格值">
-          <template slot-scope="scope">
-            <el-tag type="primary">
-              {{ scope.row.value }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column align="center" label="操作" width="120">
-          <template slot-scope="scope">
-            <el-button type="danger" size="small" @click="handleSpecificationDelete(scope.row)">删除</el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-    </div>
-    <el-dialog :visible.sync="specVisiable" title="设置规格" :close-on-click-modal="false" width="400px">
-      <el-form ref="specForm" :model="specForm" label-width="80px">
-        <el-form-item label="规格名:" prop="specification">
-          <el-input v-model="specForm.specification"/>
-        </el-form-item>
-        <el-form-item label="规格值:" prop="value">
-          <el-input v-model="specForm.value"/>
-          <div class="tip">多个规格值请用逗号隔开</div>
-        </el-form-item>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="specVisiable = false">取消</el-button>
-        <el-button type="primary" @click="handleSpecificationAdd">确定</el-button>
-      </div>
-    </el-dialog>
+    </el-form>
+    <el-button type="primary" size="small" plain @click="specList.push({ name: '', vals: [], edit: true, editVals: false })" style="margin-bottom: 10px">添加规格</el-button>
+    <el-button type="primary" size="small" style="margin-bottom: 10px;margin-left: 10px" :disabled="specList.length === 0" @click="genSku">重新生成SKU表格</el-button>
     <br>
-    <el-table v-if="multipleSpec" :data="products">
-      <el-table-column property="name" label="sku名称" />
-      <el-table-column property="picUrl" width="100" label="商品图片">
-        <template slot-scope="scope">
-          <img v-if="scope.row.picUrl" :src="IMG_URL+scope.row.picUrl" width="40">
-        </template>
-      </el-table-column>
-      <el-table-column property="value" label="价格"/>
-      <!-- <el-table-column property="originPrice" label="原兑换价格"/> -->
-      <el-table-column property="exchangePrice" label="兑换价格"/>
-      <el-table-column property="cost" label="成本"/>
-      <el-table-column property="quantity" label="库存数量"/>
-      <el-table-column align="center" label="操作" width="120">
-        <template slot-scope="scope">
-          <el-button type="primary" size="mini" @click="handleProductShow(scope.row)">设置</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <el-dialog :visible.sync="productVisiable" title="设置商品" :close-on-click-modal="false" width="400px">
-      <el-form ref="productForm" :model="productForm" label-width="100px">
-        <el-form-item label="sku名称:" prop="name">
-          <el-input v-model="productForm.name" />
-        </el-form-item>
-        <el-form-item label="价格:" prop="value">
-          <el-input-number v-model="productForm.value" />
-        </el-form-item>
-        <!-- <el-form-item label="原兑换价格:" prop="originPrice">
-          <el-input-number v-model="productForm.originPrice"/>
-        </el-form-item> -->
-        <el-form-item label="兑换价格:" prop="exchangePrice">
-          <el-input-number v-model="productForm.exchangePrice"/>
-        </el-form-item>
-        <el-form-item label="成本:" prop="cost">
-          <el-input-number v-model="productForm.cost"/>
-        </el-form-item>
-        <el-form-item label="库存数量:" prop="quantity">
-          <el-input-number v-model="productForm.quantity"/>
-        </el-form-item>
-        <el-form-item label="商品图片:" prop="picUrl">
-          <Upload :value="productForm.picUrl ? [{ fileName: productForm.picUrl }] : []" @input="productForm.picUrl = $event[0] ? $event[0].fileName : ''" :limit="1" />
-        </el-form-item>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="productVisiable = false">取消</el-button>
-        <el-button type="primary" @click="handleProductEdit">确定</el-button>
-      </div>
-    </el-dialog>
+    <el-form ref="sku" :model="{ skuList }">
+      <table v-if="skuList instanceof Array && skuList.length > 0" class="spec-table" border="1" bordercolor="#CCC">
+        <tr>
+          <th :colspan="specList.length">商品规格</th>
+          <th rowspan="2">SKU主图</th>
+          <th rowspan="2">名称</th>
+          <th rowspan="2">价格</th>
+          <th rowspan="2">兑换价格</th>
+          <th rowspan="2">成本</th>
+          <th rowspan="2">库存</th>
+          <th rowspan="2">启用</th>
+        </tr>
+        <tr>
+          <th v-for="(spec, index) in specList" :key="index" style="width: 80px">{{ spec.name }}</th>
+        </tr>
+        <!-- k1:v1;k2:v2 -->
+        <tr v-for="(sku, index) in skuList" :key="index">
+          <td v-for="(spec, i) in specList" :key="i" style="min-width: 50px">
+            {{ formatObj(sku.properties)[spec.name] }}
+          </td>
+          <td>
+            <el-form-item :prop="`skuList.${index}.picUrl`" :rules="{ required: true, message: '请上传SKU图片', trigger: 'blur' }">
+              <Upload v-model="sku.picUrl" :limit="1" style="height: 40px;overflow: hidden" />
+            </el-form-item>
+          </td>
+          <td>
+            <el-form-item :prop="`skuList.${index}.name`" :rules="{ required: true, message: '名称不能为空', trigger: 'blur' }">
+              <el-input v-model="sku.name" />
+            </el-form-item>
+          </td>
+          <td>
+            <el-form-item :prop="`skuList.${index}.value`" :rules="[{ required: true, message: '请输入价格', trigger: 'blur' }, { pattern: /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/, message: "请输入正确的金额,最多两位小数", trigger: ["blur", "change"] }]">
+              <el-input v-model="sku.value" />
+            </el-form-item>
+          </td>
+          <td>
+            <el-form-item :prop="`skuList.${index}.exchangePrice`" :rules="[{ required: true, message: '请输入兑换价格', trigger: 'blur' },{ pattern: /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/, message: "请输入正确的金额,最多两位小数", trigger: ["blur", "change"] }]">
+              <el-input v-model="sku.exchangePrice" />
+            </el-form-item>
+          </td>
+          <td>
+            <el-form-item :prop="`skuList.${index}.cost`" :rules="[{ required: true, message: '请输入成本', trigger: 'blur' },{ pattern: /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/, message: "请输入正确的金额,最多两位小数", trigger: ["blur", "change"] }]">
+              <el-input v-model="sku.cost" />
+            </el-form-item>
+          </td>
+          <td>
+            <el-form-item :prop="`skuList.${index}.quantity`" :rules="{ required: true, message: '请输入库存', trigger: 'blur' }">
+              <el-input-number v-model="sku.quantity" :min="0" />
+            </el-form-item>
+          </td>
+          <td style="padding: 0px 10px">
+            <el-form-item :prop="`skuList.${index}.status`" :rules="{ required: true, message: '请选择状态', trigger: 'blur' }">
+              <el-switch v-model="sku.status" active-color="#13ce66" inactive-color="#ff4949" />
+            </el-form-item>
+          </td>
+        </tr>
+      </table>
+    </el-form>
   </div>
 </template>
 <script>
@@ -101,41 +106,60 @@ export default {
   },
   data() {
     return {
-      specifications: [],
-      products: [],
-      specVisiable: false, // 规格弹窗
-      specForm: {}, // 规格暂存
-      productVisiable: false,
-      productForm: {},
+      specList: [],
+      skuList: [],
+      specValTmp: '',
+      specifications: []
     }
   },
-  computed: {
-    multipleSpec() {
-      return this.multiSku === 1
-    },
-  },
   methods: {
-    handleSpecificationShow() {
-      this.specForm = {}
-      this.specVisiable = true
-    },
-    handleSpecificationDelete(row) {
-      const index = this.specifications.indexOf(row)
-      this.specifications.splice(index, 1)
-      this.specToProduct()
+    formatObj(properties) {
+      if (properties) {
+        let obj = {}
+        properties.split(';').forEach(item => {
+          obj[item.split(':')[0]] = item.split(':')[1]
+        })
+        return obj
+      }
+      return {}
     },
-    specToProduct() {
-      if (this.specifications.length === 0) {
+    checkSpecVal(spec) {
+      if (!this.specValTmp) {
+        this.$message.warning('请输入规格值')
         return
       }
-      // 根据specifications创建临时规格列表
+      if (spec.vals.find(item => item === this.specValTmp)) {
+        this.$message.warning('规格值已存在')
+        return 
+      }
+      spec.vals.push(this.specValTmp)
+      this.specValTmp = ''
+      spec.editVals = false
+    },
+    genSku() {
+      this.$refs.spec.validate((valid, items) => {
+        if (valid) {
+          this.genSkuList()
+        }
+      })
+    },
+    genSkuList() {
+      let specificationsTmp = []
+      this.specList.forEach(spec => {
+        spec.vals.forEach(val => {
+          specificationsTmp.push({
+            specification: spec.name,
+            value: val
+          })
+        })
+      })
       var specValues = []
-      var spec = this.specifications[0].specification
+      var spec = specificationsTmp[0].specification
       var values = []
       values.push(0)
 
-      for (var i = 1; i < this.specifications.length; i++) {
-        const aspec = this.specifications[i].specification
+      for (var i = 1; i < specificationsTmp.length; i++) {
+        const aspec = specificationsTmp[i].specification
 
         if (aspec === spec) {
           values.push(i)
@@ -147,11 +171,8 @@ export default {
         }
       }
       specValues.push(values)
-
-      // 根据临时规格列表生产商品规格
-      // 算法基于 https://blog.csdn.net/tyhj_sf/article/details/53893125
       var productsIndex = 0
-      var products = []
+      var skuList = []
       var combination = []
       var n = specValues.length
       for (var s = 0; s < n; s++) {
@@ -164,10 +185,10 @@ export default {
         var properties = ''
         for (var x = 0; x < n; x++) {
           var z = specValues[x][combination[x]]
-          specifications.push(this.specifications[z].value)
-          properties = `${properties ? (properties + ';') : ''}${this.specifications[z].specification}:${this.specifications[z].value}`
+          specifications.push(specificationsTmp[z].value)
+          properties = `${properties ? (properties + ';') : ''}${specificationsTmp[z].specification}:${specificationsTmp[z].value}`
         }
-        products[productsIndex] = {
+        skuList[productsIndex] = {
           idx: productsIndex,
           name: specifications.toString(),
           picUrl: '',
@@ -176,6 +197,7 @@ export default {
           value: 0.00,
           cost: 0.00,
           quantity: 0,
+          status: true,
           properties
         }
         productsIndex++
@@ -198,44 +220,41 @@ export default {
           }
         }
       } while (isContinue)
-
-      this.products = products
+      this.skuList = skuList
     },
-    handleSpecificationAdd() {
-      const valueArr = this.specForm.value.replace(/,/ig,',').replace(/\s*/g,"").split(',') // 替换所有的中文逗号为英文逗号并去空格
-      valueArr.forEach(element => {
-        let index = this.specifications.length - 1
-        for (var i = 0; i < this.specifications.length; i++) {
-          const v = this.specifications[i]
-          if (v.specification === this.specForm.specification) {
-            if (v.value !== element) {
-              index = i
-            }
+    getSkuList() {
+      if (this.skuList.length === 0) {
+        this.$message.warning('请完善规格并生成sku表格')
+        return false
+      }
+      this.$refs.sku.validate((valid, items) => {
+        if (valid) {
+          this.$emit('valid', this.skuList)
+        } else {
+          if (items && Object.keys(items).length>0) {
+            this.$message({
+              message: items[Object.keys(items)[0]][0].message,
+              type: 'warning'
+            })
           }
         }
-        this.specifications.splice(index + 1, 0, {
-          specification: this.specForm.specification,
-          value: element
-        })
-        
-      });
-      this.specToProduct()
-      this.specVisiable = false
-    },
-    handleProductShow(row) {
-      this.productForm = Object.assign({}, row)
-      this.productVisiable = true
-    },
-    handleProductEdit() {
-      for (var i = 0; i < this.products.length; i++) {
-        const v = this.products[i]
-        if (v.idx === this.productForm.idx) {
-          this.products.splice(i, 1, this.productForm)
-          break
-        }
-      }
-      this.productVisiable = false
-    },
+      })
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.spec-table {
+  border-collapse: collapse;
+  width: 100%;
+  line-height: 32px;
+  color: #606266;
+  font-size: 14px;
+  th {
+    background-color: #ECECEC;
+  }
+  td {
+    text-align: center;
   }
 }
-</script>
+</style>