Przeglądaj źródła

feat: 完善商品编辑

Sun 3 lat temu
rodzic
commit
5d0d54d98a

+ 5 - 0
package-lock.json

@@ -1418,6 +1418,11 @@
       "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==",
       "dev": true
     },
+    "@tinymce/tinymce-vue": {
+      "version": "3.2.8",
+      "resolved": "https://registry.npmjs.org/@tinymce/tinymce-vue/-/tinymce-vue-3.2.8.tgz",
+      "integrity": "sha512-jEz+NZ0g+FZFz273OEUWz9QkwPMyjc5AJYyxOgu51O1Y5UaJ/6IUddXTX6A20mwCleEv5ebwNYdalviafx4fnA=="
+    },
     "@types/glob": {
       "version": "7.1.4",
       "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.4.tgz",

+ 1 - 0
package.json

@@ -44,6 +44,7 @@
     "@fullcalendar/timeline": "^4.3.0",
     "@fullcalendar/vue": "^4.3.1",
     "@riophae/vue-treeselect": "0.4.0",
+    "@tinymce/tinymce-vue": "^3.2.3",
     "axios": "^0.21.1",
     "clipboard": "2.0.6",
     "core-js": "3.8.1",

+ 1 - 0
public/index.html

@@ -6,6 +6,7 @@
     <meta name="renderer" content="webkit">
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <script src="<%= BASE_URL %>tinymce.min.js"></script>
     <title><%= webpackConfig.name %></title>
     <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
 	  <style>

Plik diff jest za duży
+ 8 - 0
public/tinymce.min.js


+ 72 - 0
src/components/TinyEditor.vue

@@ -0,0 +1,72 @@
+<template>
+  <TinyEditor v-model="content" :placeholder="placeholder" api-key="ushn75pw8xd7ec18wwdatp7bi75o98pm0ob10zncyyrbkj4k" :init="options" />
+</template>
+<script>
+import TinyEditor from '@tinymce/tinymce-vue'
+import { publicFileSaveAPI } from '@/api/common'
+import { publicFileGetUrl } from "@/api/common"
+import { getToken } from '@/utils/auth'
+export default {
+  components: {
+    TinyEditor
+  },
+  props: {
+    value: {
+      default: '',
+      type: String
+    },
+    height: {
+      default: 800,
+      type: Number
+    },
+    placeholder: {
+      default: '',
+      type: String
+    }
+  },
+  data() {
+    return {
+      options: {
+        base_url: '//itie-static.oss-cn-hangzhou.aliyuncs.com/assets/tinymce/',
+        language: 'zh_CN',
+        language_url: '//itie-static.oss-cn-hangzhou.aliyuncs.com/assets/tinymce/zh_CN.js',
+        height: 500,
+        resize: true,
+        statusbar: false,
+        // skin_url: '//itie-static.oss-cn-hangzhou.aliyuncs.com/assets/tinymce/oxide-dark', // 暗色皮肤
+        // content_css: '//itie-static.oss-cn-hangzhou.aliyuncs.com/assets/tinymce/content.css',
+        plugins: ['advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textpattern visualblocks visualchars wordcount placeholder indent2em'], // 插件
+        image_advtab: true,
+        images_upload_handler: this.updateImage,
+        imagetools_toolbar: '',
+        fontsize_formats: '8px 10px 12px 14px 16px 18px 20px 22px 24px 36px',
+        toolbar: ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen indent2em']
+      }
+    }
+  },
+  computed: {
+    content: {
+      get() {
+        return this.value
+      },
+      set(val) {
+        this.$emit('input', val)
+      }
+    }
+  },
+  methods: {
+    updateImage(blobInfo, success, failure) {
+      publicFileSaveAPI(
+        { file: blobInfo.blob() },
+        {
+          "Authorization": "Bearer " + getToken(),
+          "x-zz-timestamp": new Date().valueOf()
+        }).then(res => {
+        success(publicFileGetUrl + res.data.fileName)
+      }).catch(err => {
+        failure(err)
+      })
+    }
+  }
+}
+</script>

+ 48 - 302
src/views/business/goods/add.vue

@@ -34,7 +34,7 @@
       <el-row :gutter="40">
         <el-col :span="23">
           <el-form-item label="启用多SKU:" prop="multiSku">
-            <!-- <el-switch
+            <el-switch
               v-model="addData.multiSku"
               active-color="#13ce66"
               inactive-color="#ff4949"
@@ -42,15 +42,12 @@
               :inactive-value="0"
               active-text="是"
               inactive-text="否"
-            /> -->
-            <el-radio-group v-model="addData.multiSku">
-              <el-radio-button :label="0">单规格</el-radio-button>
-              <el-radio-button :label="1">多规格</el-radio-button>
-            </el-radio-group>
+            />
           </el-form-item>
         </el-col>
       </el-row>
-      <el-row v-if="!multipleSpec" :gutter="40">
+      <Spec :multiSku="addData.multiSku" />
+      <el-row v-if="addData.multiSku === 0" :gutter="40">
         <el-col :span="23">
           <el-form-item label="价格:" prop="value">
             <el-input v-model="addData.value" type="number" placeholder="请输入商品价格">
@@ -59,117 +56,36 @@
           </el-form-item>
         </el-col>
         <el-col :span="23">
-          <el-form-item label="成本:" prop="cost">
-            <el-input v-model.number="addData.cost" type="number" placeholder="请输入商品成本">
+          <el-form-item label="兑换价格:" prop="exchangePrice">
+            <el-input v-model="addData.exchangePrice" type="number" placeholder="请输入兑换价格">
               <template slot="append">元</template>
             </el-input>
           </el-form-item>
         </el-col>
-        <!-- <el-col :span="23">
-          <el-form-item label="原兑换价格:" prop="originPrice">
-            <el-input v-model="addData.originPrice" type="number" placeholder="请输入原兑换价格">
+        <el-col :span="23">
+          <el-form-item label="成本:" prop="cost">
+            <el-input v-model.number="addData.cost" type="number" placeholder="请输入商品成本">
               <template slot="append">元</template>
             </el-input>
           </el-form-item>
-        </el-col> -->
+        </el-col>
         <el-col :span="23">
-          <el-form-item label="兑换价格:" prop="exchangePrice">
-            <el-input v-model="addData.exchangePrice" type="number" placeholder="请输入兑换价格">
-              <template slot="append"></template>
+          <el-form-item label="库存:" prop="quantity">
+            <el-input v-model="addData.quantity" type="number" placeholder="请输入商品库存">
+              <template slot="append"></template>
             </el-input>
           </el-form-item>
         </el-col>
+      </el-row>
+      <el-divider content-position="left">商品详情</el-divider>
+      <el-row>
         <el-col :span="23">
-          <el-form-item label="库存:" prop="quantity">
-            <el-input-number v-model="addData.quantity" placeholder="请输入商品库存" />
+          <el-form-item prop="description">
+            <TinyEditor v-model="addData.description" />
           </el-form-item>
         </el-col>
       </el-row>
     </el-form>
-    <div v-if="multipleSpec">
-      <div style="margin-bottom: 16px">
-        <el-button :plain="true" type="primary" size="small" @click="handleSpecificationShow">添加规格</el-button>
-      </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>
-    <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>
     <br>
     <el-row>
       <el-col :span="24" style="text-align: right">
@@ -181,37 +97,50 @@
 </template>
 <script>
 import Upload from '@/components/ImageUpload'
+import Spec from './components/spec'
+import TinyEditor from '@/components/TinyEditor'
 import { getGoodsDetail, addGoods } from '@/api/business/goods'
 import { publicFileGetUrl } from "@/api/common"
 export default {
   components: {
-    Upload
+    TinyEditor,
+    Upload,
+    Spec
   },
   data() {
     return {
       IMG_URL: publicFileGetUrl,
       id: this.$route.query.id,
       addData: {
-        multiSku: 0
+        multiSku: 0,
+        description: ''
       },
       rules: {
-        title: [{ required: true, message: '请输入商品名称', trigger: 'blur' }]
-      },
-      dragOptions: {
-        animation: 100,
-      },
-      specifications: [],
-      products: [],
-      specVisiable: false, // 规格弹窗
-      specForm: {}, // 规格暂存
-      productVisiable: false,
-      productForm: {},
+        title: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],
+        picUrl: [{ required: true, message: '请上传商品图片', trigger: 'change' }],
+        exchangeShow: [{ required: true, message: '请选择是否支持盲豆兑换', trigger: 'change' }],
+        multiSku: [{ required: true, message: '请选择SKU类型', trigger: 'change' }],
+        value: [
+          { 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"] }
+        ],
+        exchangePrice: [
+          { 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"] }
+        ],
+        cost: [
+          { 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"] }
+        ],
+        quantity: [
+          { required: true, message: '请输入库存', trigger: 'blur' },
+          { pattern: /^([1-9]\d*)$/, message: "请输入正确的数字", trigger: ["blur", "change"] }
+        ],
+        description: [{ required: true, message: '请输入商品详情', trigger: 'blur' }]
+      }
     }
   },
   computed: {
-    multipleSpec() {
-      return this.addData.multiSku === 1
-    },
     mainPicUrl: {
       get() {
         return this.addData.picUrl ? this.addData.picUrl.split(',').map(item => {
@@ -233,136 +162,6 @@ export default {
     }
   },
   methods: {
-    beforeUpload(e) {
-      if (e.size > 1024000) {
-        this.$message({
-          message: '图片过大,请调整大小确保不超过1M!',
-          type: 'warning'
-        });
-        return false
-      }
-    },
-    handleSpecificationShow() {
-      this.specForm = {}
-      this.specVisiable = true
-    },
-    handleSpecificationDelete(row) {
-      const index = this.specifications.indexOf(row)
-      this.specifications.splice(index, 1)
-      this.specToProduct()
-    },
-    specToProduct() {
-      if (this.specifications.length === 0) {
-        return
-      }
-      // 根据specifications创建临时规格列表
-      var specValues = []
-      var spec = this.specifications[0].specification
-      var values = []
-      values.push(0)
-
-      for (var i = 1; i < this.specifications.length; i++) {
-        const aspec = this.specifications[i].specification
-
-        if (aspec === spec) {
-          values.push(i)
-        } else {
-          specValues.push(values)
-          spec = aspec
-          values = []
-          values.push(i)
-        }
-      }
-      specValues.push(values)
-
-      // 根据临时规格列表生产商品规格
-      // 算法基于 https://blog.csdn.net/tyhj_sf/article/details/53893125
-      var productsIndex = 0
-      var products = []
-      var combination = []
-      var n = specValues.length
-      for (var s = 0; s < n; s++) {
-        combination[s] = 0
-      }
-      var index = 0
-      var isContinue = false
-      do {
-        var specifications = []
-        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}`
-        }
-        products[productsIndex] = {
-          idx: productsIndex,
-          name: specifications.toString(),
-          picUrl: '',
-          originPrice: 0.00,
-          exchangePrice: 0.00,
-          value: 0.00,
-          cost: 0.00,
-          quantity: 0,
-          properties
-        }
-        productsIndex++
-
-        index++
-        combination[n - 1] = index
-        for (var j = n - 1; j >= 0; j--) {
-          if (combination[j] >= specValues[j].length) {
-            combination[j] = 0
-            index = 0
-            if (j - 1 >= 0) {
-              combination[j - 1] = combination[j - 1] + 1
-            }
-          }
-        }
-        isContinue = false
-        for (var p = 0; p < n; p++) {
-          if (combination[p] !== 0) {
-            isContinue = true
-          }
-        }
-      } while (isContinue)
-
-      this.products = products
-    },
-    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
-            }
-          }
-        }
-        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
-    },
     updateItem() {
       this.$refs.addItem.validate((valid, items) => {
         if (valid) {
@@ -383,12 +182,9 @@ export default {
               type: 'warning'
             })
           }
-          
         }
       })
-    },
-
-    
+    }
   }
 }
 </script>
@@ -406,55 +202,5 @@ export default {
       left: 10px;
     }
   }
-  .flip-list-move {
-    transition: transform 0.5s;
-  }
-  .no-move {
-    transition: transform 0s;
-  }
-  .ghost {
-    opacity: 0.5;
-    background: #c8ebfb;
-  }
-  .list-group {
-    min-height: 120px;
-    padding: 20px;
-    border: 1px solid #EEE;
-  }
-  .list-empty {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    font-size: 14px;
-    color: #888;
-    height: 100px;
-  }
-  .list-group-item, .list-group-header {
-    padding: 0 16px;
-    height: 40px;
-    border-bottom: 1px solid #EBEEF5;
-    cursor: move;
-    display: flex;
-    align-items: center;
-    font-size: 14px;
-    color: #606266;
-    justify-content: space-between;
-    .f1 {
-      flex: 1;
-    }
-    .f2 {
-      flex: 2;
-    }
-    .tr {
-      text-align: right;
-    }
-  }
-  .list-group-header {
-    cursor: default;
-    background: #EBEEF5;
-  }
-  .list-group-item:hover {
-    background: #F3F6F9;
-  }
 }
 </style>

+ 241 - 0
src/views/business/goods/components/spec.vue

@@ -0,0 +1,241 @@
+<template>
+  <div>
+    <div v-if="multipleSpec">
+      <div style="margin-bottom: 16px">
+        <el-button :plain="true" type="primary" size="small" @click="handleSpecificationShow">添加规格</el-button>
+      </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>
+    <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>
+  </div>
+</template>
+<script>
+import Upload from '@/components/ImageUpload'
+export default {
+  name: 'Spec',
+  components: {
+    Upload
+  },
+  props: {
+    multiSku: {
+      type: Number,
+      default: 0
+    }
+  },
+  data() {
+    return {
+      specifications: [],
+      products: [],
+      specVisiable: false, // 规格弹窗
+      specForm: {}, // 规格暂存
+      productVisiable: false,
+      productForm: {},
+    }
+  },
+  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()
+    },
+    specToProduct() {
+      if (this.specifications.length === 0) {
+        return
+      }
+      // 根据specifications创建临时规格列表
+      var specValues = []
+      var spec = this.specifications[0].specification
+      var values = []
+      values.push(0)
+
+      for (var i = 1; i < this.specifications.length; i++) {
+        const aspec = this.specifications[i].specification
+
+        if (aspec === spec) {
+          values.push(i)
+        } else {
+          specValues.push(values)
+          spec = aspec
+          values = []
+          values.push(i)
+        }
+      }
+      specValues.push(values)
+
+      // 根据临时规格列表生产商品规格
+      // 算法基于 https://blog.csdn.net/tyhj_sf/article/details/53893125
+      var productsIndex = 0
+      var products = []
+      var combination = []
+      var n = specValues.length
+      for (var s = 0; s < n; s++) {
+        combination[s] = 0
+      }
+      var index = 0
+      var isContinue = false
+      do {
+        var specifications = []
+        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}`
+        }
+        products[productsIndex] = {
+          idx: productsIndex,
+          name: specifications.toString(),
+          picUrl: '',
+          originPrice: 0.00,
+          exchangePrice: 0.00,
+          value: 0.00,
+          cost: 0.00,
+          quantity: 0,
+          properties
+        }
+        productsIndex++
+
+        index++
+        combination[n - 1] = index
+        for (var j = n - 1; j >= 0; j--) {
+          if (combination[j] >= specValues[j].length) {
+            combination[j] = 0
+            index = 0
+            if (j - 1 >= 0) {
+              combination[j - 1] = combination[j - 1] + 1
+            }
+          }
+        }
+        isContinue = false
+        for (var p = 0; p < n; p++) {
+          if (combination[p] !== 0) {
+            isContinue = true
+          }
+        }
+      } while (isContinue)
+
+      this.products = products
+    },
+    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
+            }
+          }
+        }
+        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>

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików