From 089bf5d2378b3c4a61d795b2a92bede2c193b771 Mon Sep 17 00:00:00 2001
From: admin <344137771@qq.com>
Date: Tue, 06 Jan 2026 11:22:58 +0800
Subject: [PATCH] 1

---
 src/views/modules/prod/sku-tag.vue |  464 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 464 insertions(+), 0 deletions(-)

diff --git a/src/views/modules/prod/sku-tag.vue b/src/views/modules/prod/sku-tag.vue
new file mode 100644
index 0000000..91236c0
--- /dev/null
+++ b/src/views/modules/prod/sku-tag.vue
@@ -0,0 +1,464 @@
+<template>
+  <div class="mod-prod-sku-tag">
+    <el-form-item label="商品规格">
+      <el-button size="mini" @click="shopTagInput()">添加规格</el-button>
+      <div v-for="(tag, tagIndex) in skuTags" :key="tagIndex">
+        <span>{{tag.tagName}}</span>
+        <el-button class="button-new-tag" type="text"  icon="el-icon-delete" @click="removeTag(tagIndex)">删除</el-button>
+        <br/>
+        <el-tag
+          v-for="(tagItem, tagItemIndex) in tag.tagItems"
+          :key="tagItem.valueId"
+          closable
+          :disable-transitions="false"
+          @close="handleTagClose(tagIndex, tagItemIndex)">
+          {{tagItem.propValue}}
+        </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>
+        <el-button v-else class="button-new-tag" size="small" @click="showTagInput(tagIndex)">+ 添加</el-button>
+      </div>
+    </el-form-item>
+    <el-form-item label="规格名" v-show="isShowTagInput">
+      <el-col :span="8">
+        <el-select v-model="addTagInput.propName" filterable allow-create default-first-option placeholder="请选择" @change="handleTagClick">
+          <el-option
+            v-for="item in unUseTags"
+            :key="item.propId"
+            :label="item.propName"
+            :value="item.propName">
+          </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.valueId"
+            :label="item.propValue"
+            :value="item.propValue">
+          </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>
+
+
+  </div>
+</template>
+
+<script>
+export default {
+  data () {
+    return {
+      value: [],
+      isShowTagInput: false,
+      addTagInput: {
+        propName: '',
+        selectValues: []
+      },
+      type: 0,
+      tagItemName: '',
+      tagName: '',
+      tagNameIndex: 0,
+      tagItemInputs: [],
+      // sku的标记
+      // tags: [],
+      // 数据库中的规格
+      dbTags: [],
+      // 根据选定的规格所查询出来的规格值
+      dbTagValues: [],
+      specs: [], // 使用的规格
+      maxValueId: 0, // 规格值id最大
+      maxPropId: 0, // 规格id 最大
+      initing: false
+    }
+  },
+  created: function () {
+    this.$http({
+      url: this.$http.adornUrl(`/prod/spec/list`),
+      method: 'get',
+      params: this.$http.adornParams()
+    }).then(({data}) => {
+      this.dbTags = data
+      if (data) {
+        this.maxPropId = Math.max.apply(Math, data.map(item => { return item.propId }))
+      } else {
+        this.maxPropId = 0
+      }
+    })
+    this.$http({
+      url: this.$http.adornUrl(`/prod/spec/listSpecMaxValueId`),
+      method: 'get',
+      params: this.$http.adornParams()
+    }).then(({ data }) => {
+      if (data) {
+        this.maxValueId = data
+      } else {
+        this.maxValueId = 0
+      }
+    })
+  },
+  props: {
+  //   tags: { // sku的标记
+  //     default: [],
+  //     type: Array
+  //   }
+  // },
+    skuList: {
+      default: []
+    }
+  },
+  computed: {
+    // 未使用的规格, 通过计算属性计算
+    unUseTags () {
+      let res = []
+      for (let i = 0; i < this.dbTags.length; i++) {
+        const dbTag = this.dbTags[i]
+        let specIndex = this.skuTags.findIndex(tag => tag.tagName === dbTag.propName)
+        if (specIndex === -1) {
+          res.push(dbTag)
+        }
+      }
+      return res
+    },
+    skuTags: {
+      get () { return this.$store.state.prod.skuTags },
+      set (val) { this.$store.commit('prod/updateSkuTags', val) }
+    },
+    defalutSku () {
+      return this.$store.state.prod.defalutSku
+    }
+  },
+  watch: {
+    skuTags: {
+      handler (val, oldVal) {
+        if (this.initing) {
+          this.initing = false
+          return
+        }
+        let skuList = []
+        if (this.type === 4) {
+        // 删除规格值
+          this.skuList.forEach(sku => {
+            let propertiesArray = sku.properties.split(';')
+            if (this.tagItemName !== propertiesArray[this.tagNameIndex].split(':')[1]) {
+              skuList.push(sku)
+            }
+          })
+        } else if (this.type === 2) {
+        // 添加规格值
+          var properties = this.tagName + ':' + this.tagItemName
+        // 增加或删除规格
+          let tempSkuList = []
+          val.forEach(tag => {
+            if (skuList.length === 0) {
+              if (this.tagName === tag.tagName) {
+                let sku = Object.assign({}, this.defalutSku)
+                sku.properties = properties // 销售属性组合字符串
+                skuList.push(sku)
+              } else {
+                tag.tagItems.forEach(tagItem => {
+                  let sku = Object.assign({}, this.defalutSku)
+                  sku.properties = `${tag.tagName}:${tagItem.propValue}` // 销售属性组合字符串
+                  skuList.push(sku)
+                })
+              }
+              if (val.length === 1) {
+                skuList = this.skuList.concat(skuList)
+              }
+            } else {
+              tempSkuList = []
+              if (this.tagName === tag.tagName) {
+                skuList.forEach(sku => {
+                  if (sku.properties.indexOf(this.tagName) === -1) {
+                    let newSku = Object.assign({}, sku)
+                    newSku.properties = `${sku.properties};${properties}`
+                    tempSkuList.push(newSku)
+                  }
+                })
+              } else {
+                tag.tagItems.forEach(tagItem => {
+                  skuList.forEach(sku => {
+                    if (sku.properties.indexOf(tag.tagName) === -1) {
+                      let newSku = Object.assign({}, sku)
+                      newSku.properties = `${sku.properties};${tag.tagName}:${tagItem.propValue}`
+                      tempSkuList.push(newSku)
+                    }
+                  })
+                })
+              }
+              skuList = this.skuList.concat(tempSkuList)
+              console.log('skuList', skuList)
+            }
+          })
+        } else {
+        // 增加或删除规格
+          let tempSkuList = []
+          val.forEach(tag => {
+          // console.log('tag', tag)
+            if (skuList.length === 0) {
+              tag.tagItems.forEach(tagItem => {
+                let sku = Object.assign({}, this.defalutSku)
+                sku.properties = `${tag.tagName}:${tagItem.propValue}` // 销售属性组合字符串
+                skuList.push(sku)
+              })
+            } else {
+              tempSkuList = []
+              tag.tagItems.forEach(tagItem => {
+                skuList.forEach(sku => {
+                  let newSku = Object.assign({}, sku)
+                  newSku.properties = `${sku.properties};${tag.tagName}:${tagItem.propValue}`
+                  tempSkuList.push(newSku)
+                })
+              })
+              skuList = tempSkuList
+            }
+          })
+        }
+        if (!skuList.length) {
+          skuList.push(Object.assign({}, this.defalutSku))
+        }
+      // debugger
+        this.$emit('change', skuList)
+      },
+      deep: true
+    }
+  },
+  methods: {
+    init (skuList) {
+      this.value = skuList
+      if (!skuList || !skuList.length) {
+        this.skuTags = []
+        this.$emit('change', [Object.assign({}, this.defalutSku)])
+        return
+      }
+      this.initing = true
+      let skuTags = []
+      for (let i = 0; i < skuList.length; i++) {
+        const sku = skuList[i]
+        if (!sku.properties) break
+        let propertiesArray = sku.properties.split(';')
+        for (let j in propertiesArray) {
+          let properties = propertiesArray[j].split(':')
+          if (!skuTags[j]) {
+            skuTags[j] = {
+              tagName: properties[0],
+              tagItems: []
+            }
+            this.tagItemInputs.push({ visible: false, value: '' })
+          }
+          let tagItemNameIndex = skuTags[j].tagItems.findIndex((tagItemName) => tagItemName.propValue === properties[1])
+          if (tagItemNameIndex === -1) {
+            // skuTags[j].tagItems.push(properties[1])
+            skuTags[j].tagItems.push({propValue: properties[1]})
+          }
+        }
+      }
+      this.skuTags = skuTags
+    },
+    // 显示规格名、规格值输入框
+    shopTagInput () {
+      this.isShowTagInput = !this.isShowTagInput
+    },
+    // 隐藏规格名、规格值输入框
+    hideTagInput () {
+      this.isShowTagInput = false
+      this.cleanTagInput()
+    },
+    addTag () {
+      let selectValues = this.addTagInput.selectValues
+      if (!this.addTagInput.propName) {
+        this.$message.error('请输入规格名')
+        return
+      }
+      if (!selectValues.length) {
+        this.$message.error('请输入规格值')
+        return
+      }
+      this.isShowTagInput = false
+      for (let i = 0; i < selectValues.length; i++) {
+        const element = selectValues[i]
+        let is = Object.prototype.toString.call(element) === '[object Object]'
+        if (!is) {
+          this.maxPropId = this.maxPropId + 1
+          break
+        }
+      }
+      let tagItems = []
+      for (let i = 0; i < selectValues.length; i++) {
+        const element = selectValues[i]
+        let is = Object.prototype.toString.call(element) === '[object Object]'
+        if (is) {
+          tagItems.push(element)
+        } else {
+          this.maxValueId = this.maxValueId + 1
+          tagItems.push({propId: this.maxPropId, propValue: element, valueId: this.maxValueId})
+        }
+      }
+      // 向规格中放入规格输入框内的数据
+      this.$store.commit('prod/addSkuTag', {
+        tagName: this.addTagInput.propName,
+        tagItems
+      })
+      this.type = 1
+      this.cleanTagInput()
+    },
+    // 清除规格值输入框内容
+    cleanTagInput () {
+      this.addTagInput = {
+        propName: '',
+        selectValues: []
+      }
+      this.dbTagValues = []
+    },
+      // 规格名输入框,选中规格事件
+    handleTagClick () {
+        // 清空规格值输入框
+      this.dbTagValues = []
+      this.addTagInput.selectValues = []
+        // 判断规格名输入的值是否为数据库中已有的值
+      let specsIndex = this.dbTags.findIndex(spec => spec.propName === this.addTagInput.propName)
+        // 如果不是,则说明为用户随便输入
+      if (specsIndex === -1) return
+        // 如果数据库已有该规格名,则获取根据id获取该规格名称含有的规格值
+      this.$http({
+        url: this.$http.adornUrl(`/prod/spec/listSpecValue/${this.dbTags[specsIndex].propId}`),
+        method: 'get',
+        params: this.$http.adornParams()
+      }).then(({data}) => {
+        this.dbTagValues = data
+      })
+    },
+    // 关闭标签 --删除
+    handleTagClose (tagIndex, tagItemIndex) {
+      this.tagName = this.skuTags[tagIndex].tagName
+      this.tagNameIndex = tagIndex
+      this.tagItemName = this.skuTags[tagIndex].tagItems[tagItemIndex].propValue
+      if (this.skuTags[tagIndex].tagItems.length === 1) {
+        return
+      }
+      this.type = 4
+      this.$store.commit('prod/removeSkuTagItem', { tagIndex, tagItemIndex })
+    },
+    // 标签输入框确定时调用
+    handleInputConfirm (tagIndex) {
+      if (this.checkTagItem(tagIndex)) {
+        return
+      }
+      var tagItems = this.skuTags[tagIndex].tagItems
+      var itemValue = this.tagItemInputs[tagIndex].value
+      let index = tagItems.length - 1
+      this.tagName = this.skuTags[tagIndex].tagName
+      this.tagItemName = this.tagItemInputs[tagIndex].value
+      let maxValue = this.getMaxValueId(this.skuTags[tagIndex].tagItems)
+      let tagItem = {propId: index === -1 ? 0 : this.skuTags[tagIndex].tagItems[index].propId, propValue: itemValue, valueId: index === -1 ? 0 : (maxValue + 1)}
+      if (tagItem) {
+        this.$store.commit('prod/addSkuTagItem', { tagIndex, tagItem })
+      }
+      this.tagItemInputs[tagIndex].visible = false
+      this.tagItemInputs[tagIndex].value = ''
+      this.type = 2
+    },
+    // 显示标签输入框
+    showTagInput (tagIndex) {
+      this.tagItemInputs.push({ visible: false, value: '' })
+      this.tagItemInputs[tagIndex].visible = true
+      this.$nextTick(() => {
+        this.$refs[`saveTagInput${tagIndex}`][0].$refs.input.focus()
+      })
+    },
+    // 获取数据集合所有对象中某个属性的最大值
+    getMaxValueId (list) {
+      let value = Math.max.apply(Math, list.map(item => { return item.valueId }))
+      return value
+    },
+    // 删除 规格
+    removeTag (tagIndex) {
+      this.type = 3
+      this.$store.commit('prod/removeSkuTag', tagIndex)
+    },
+    /**
+     * 新增规格值时,判断是否存在同名的规格值
+     */
+    checkTagItem (tagIndex) {
+      let tagItem = this.tagItemInputs[tagIndex].value
+      if (!tagItem) {
+        this.tagItemInputs[tagIndex].visible = false
+        this.tagItemInputs[tagIndex].value = ''
+        return true
+      }
+      var isSame = false
+      this.skuTags.forEach(tag => {
+        let arr = tag.tagItems.map((item, index) => {
+          return item.propValue
+        })
+        if (arr.indexOf(tagItem) > -1) {
+          isSame = true
+          this.$message.error('product.specificationValue')
+          return false
+        }
+      })
+      return isSame
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.mod-prod-sku-tag {
+  .el-tag + .el-tag {
+    margin-left: 10px;
+  }
+  .button-new-tag {
+    margin-left: 10px;
+    height: 32px;
+    line-height: 30px;
+    padding-top: 0;
+    padding-bottom: 0;
+  }
+  .input-new-tag {
+    width: 90px;
+    margin-left: 10px;
+    vertical-align: bottom;
+  }
+}
+
+// 新增规格外部边框
+.sku-border{
+  border: 1px solid #EBEEF5;
+  width:70%
+}
+.sku-background{
+  background-color: #F6f6f6;
+  margin: 12px 12px;
+  .el-button{
+    margin-left: 10px;
+    span{
+      color:#000 !important;
+    }
+  }
+  .el-form-item__label{
+    padding:0 24px 0 0
+  }
+}
+.sku-tag{
+  margin: 12px 12px;
+}
+.tagTree{
+  margin-left: 18px;
+  padding-bottom:8px;
+}
+</style>

--
Gitblit v1.9.3