首页 > 技术文章 > 笛卡尔积 SKU

xiao-chuan- 2021-02-09 16:40 原文

笛卡尔积 SKU

git 地址

代码

  • template

    <center><h1>笛卡尔积 sku</h1></center>
    <el-button type="primary" @click="showAddSkuAttrDialog">添加属性</el-button>
    <el-form>
      <el-form-item v-for="(item, i) in manyTableData" :key="item.attr_id">
        <el-row>
          <el-col :span="24">
            <span><span class="span-box"><span>{{ item.attr_name }}</span>
                 <el-tooltip class="item" effect="dark" content="删除" placement="top-start" :enterable="false"><el-button
                   @click="delAttrMany(i)"
                   size="mini"><i class="el-icon-delete"/></el-button></el-tooltip>
              </span></span>
          </el-col>
          <el-col :span="24">
            <!-- 复选框组 -->
            <el-checkbox-group v-model="item.cb" @change="setSkuTable">
              <el-checkbox
                v-for="cb in item.attr_vals"
                :label="cb.id"
                :key="cb.id"
                border
              >{{ cb.value }}
              </el-checkbox>
            </el-checkbox-group>
            <el-input
              class="input-new-tag"
              v-if="item.inputVisible"
              v-model="item.inputValue"
              ref="saveSkuAttrInput"
              size="small"
              @keyup.enter.native="handleInputConfirm(item)"
              @blur="handleInputConfirm(item)"
            >
            </el-input>
            <el-button v-else class="button-new-tag" size="small" @click="showInput(item)">+ New Tag
            </el-button>
          </el-col>
        </el-row>
      </el-form-item>
    </el-form>
    <el-table
      :data="tableData7"
      :span-method="objectSpanMethod"
      border
    >
      <el-table-column v-for="(item, i) in tableHeader" :key="i" :prop="item.prop"
                       :label="item.label"></el-table-column>
      <el-table-column prop="kucun" label="库存">
        <template slot-scope="scope">
          <el-input-number
            v-model="scope.row.kucun"
            :min="0"
            :step="1"
            label="描述文字"
            @blur="handlerBlur(scope.row)"
          ></el-input-number>
        </template>
      </el-table-column>
      <el-table-column prop="moany" label="金钱">
        <template slot-scope="scope">
          <el-input-number v-model="scope.row.moany" :precision="2" :step="0.01" :min="0.00"></el-input-number>
        </template>
      </el-table-column>
    </el-table>
    <!-- 添加属性对话框 -->
    <el-dialog
      title="提示"
      :visible.sync="addSkuAttrDialogVisible"
      width="50%"
      @close="addSkuAttrDialogClosed"
      v-if="addSkuAttrDialogVisible"
    >
      <el-form :model="addSkuAttrForm" :rules="addSkuAttrFormRule" ref="addSkuAttrFormRef" label-width="100px">
        <el-form-item label="属性名称" prop="name">
          <el-input v-model="addSkuAttrForm.name" ref="addSkuAttrInput"
                    @keyup.enter.native="submitAddSkuAttr"></el-input>
        </el-form-item>
        <el-form-item label="英文名称" prop="en_name">
          <el-input v-model="addSkuAttrForm.en_name"
                    @keyup.enter.native="submitAddSkuAttr"></el-input>
          <el-button @click="createEnNameBind()">随机生成</el-button>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
    <el-button @click="addSkuAttrDialogVisible = false">取 消</el-button>
    <el-button type="primary"
               @click="submitAddSkuAttr"
    >确 定</el-button>
    </span>
    </el-dialog>
    
  • vuex

    state: {
      // 参数源
      manyTableData: [
        {
          attr_id: 1,
          attr_name: '颜色',
          attr_vals: [
            {
              id: 1,
              value: '白色'
            },
            {
              id: 2,
              value: '黑色'
            },
            {
              id: 3,
              value: '红色'
            }
          ],
          cb: [],
          en_name: 'a',
          inputValue: '',
          inputVisible: false
        },
        {
          attr_id: 2,
          attr_name: '型号',
          attr_vals: [
            {
              id: 4,
              value: 'X'
            },
            {
              id: 5,
              value: 'L'
            },
            {
              id: 6,
              value: 'XL'
            }
          ],
          cb: [],
          en_name: 'b',
          inputValue: '',
          inputVisible: false
        },
        {
          attr_id: 3,
          attr_name: '大小',
          attr_vals: [
            {
              id: 7,
              value: '大'
            },
            {
              id: 8,
              value: '中'
            },
            {
              id: 9,
              value: '小'
            }
          ],
          cb: [],
          en_name: 'c',
          inputValue: '',
          inputVisible: false
        }
      ],
      // 选中的参数
      shopType: [],
      // 动态参数表头
      tableHeader: [],
      tableData7: [],
      // 参数属性
      skuAttr: [],
      spanArr: [],
      // 添加属性对话框的显示与隐藏
      addSkuAttrDialogVisible: false,
      // 添加属性 表单对象
      addSkuAttrForm: {
        name: '',
        en_name: ''
      },
      // 添加属性 验证对象
      addSkuAttrFormRule: {
        name: [
          {
            required: true,
            message: '属性名必须',
            trigger: 'blur'
          }
        ],
        en_name: [
          {
            required: true,
            message: '英文名称必须',
            trigger: 'change'
          },
          {
            required: true,
            message: '英文名称必须',
            trigger: 'blur'
          }
        ]
      },
      // 获取英文名的初始设置
      i: 0,
      flag: true
    }
    
  • methods

    methods: {
      addSkuAttrInputFocus () {
        this.$nextTick(_ => {
          this.$refs.addSkuAttrInput.$refs.input.focus()
        })
      },
      descartes,
      // 删除参数 - 动态参数
      delAttrMany (key) {
        this.$confirm('是否删除?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.manyTableData.splice(key, 1)
          this.$message({
            type: 'success',
            message: '删除成功!'
          })
          this.setSkuTable()
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          })
        })
      },
      // 设置 sku 表格
      setSkuTable () {
        this.shopType = []
        this.manyTableData.forEach(item => {
          // item
          //   {
          //     id: 1,
          //     prop: "color",
          //     name: "颜色",
          //     typeNames: [
          //       {type: "红色", id: 301},
          //       {type: "白色", id: 302},
          //       {type: "蓝色", id: 303},
          //       {type: "粉色", id: 304}
          //     ]
          //   }
          if (item.cb.length > 0) {
            // 有选中才进来
            const obj = {
              id: item.attr_id,
              prop: item.en_name,
              name: item.attr_name,
              typeNames: []
            }
            item.attr_vals.forEach(item2 => {
              if (item.cb.includes(item2.id)) {
                obj.typeNames.push({
                  type: item2.value,
                  id: item2.id
                })
              }
            })
            this.shopType.push(obj)
          }
        })
        this.setTableHeader()
        this.processing()
      },
      handleInputConfirm (row) {
        const inputValue = row.inputValue.trim().toUpperCase()
        if (inputValue) {
          // 判断是否存在
          const res = row.attr_vals.find(item => item.value.toUpperCase() === inputValue)
          console.log(res)
          if (res) {
            this.$message.error('已存在')
            row.inputVisible = false
            row.inputValue = ''
            return false
          }
          // id 集合
          const ids = []
          this.manyTableData.forEach(item => {
            item.attr_vals.forEach(item2 => {
              ids.push(item2.id)
            })
          })
          // 向后端请求添加属性值
          let newParams = {}
          newParams = {
            id: Math.max(...ids) + 1,
            value: inputValue
          }
          this.$message.success('ok')
          row.attr_vals.push(newParams)
        }
        row.inputVisible = false
        row.inputValue = ''
      },
      showInput (row) {
        row.inputVisible = true
        this.$nextTick(_ => {
          this.$refs.saveSkuAttrInput[0].$refs.input.focus()
        })
      },
      // 处理数据
      processing () {
        this.newData = []
        for (
          let i = 0;
          i < this.shopType.length;
          i++
        ) {
          var newlist = []
          for (
            let index = 0;
            index < this.shopType[i].typeNames.length;
            index++
          ) {
            const currArr = {
              id: this.shopType[i].typeNames[index].id,
              name: this.shopType[i].typeNames[index].type
            }
            newlist.push(currArr)
          }
          this.newData.push(newlist)
        }
        this.getList()
      },
      // 转换数据
      getList () {
        this.tableData7 = []
        this.newList = this.descartes(this.newData)
        for (let index = 0; index < this.newList.length; index++) {
          const obj = {}
          for (var key in this.skuAttr) {
            obj[this.skuAttr[key]] = this.newList[index].name[key]
            obj.skuids = this.newList[index].id
          }
          Object.assign(obj, {
            kucun: 0,
            moany: 0.00
          })
          this.tableData7.push(obj)
        }
        this.getSpanArr(this.shopType)
      },
      // 计算位置的方法
      getSpanArr (data) {
        const it = data[Symbol.iterator]()
        let currIt = it.next()
        var i = 0
        const res = []
        while (!currIt.done) {
          res[i] = currIt.value.typeNames.length
          currIt = it.next()
          i++
        }
        for (const i in res) {
          let cj = 1
          delete res[i]
          for (const j of res) {
            cj = cj * (j === undefined ? 1 : j)
          }
          this.spanArr[i] = cj
        }
      },
      // 合并行数
      objectSpanMethod ({
        row,
        column,
        rowIndex,
        columnIndex
      }) {
        for (const i in this.spanArr) {
          if (columnIndex === parseInt(i)) {
            if (rowIndex % this.spanArr[i] === 0) {
              return [
                this.spanArr[i],
                1
              ]
            }
            return [0, 0]
          }
        }
      },
      // 设置库存校验
      handlerBlur (row) {
        const kucun = row.kucun
        if (kucun === undefined) {
          row.kucun = 0
        } else if (!/^\d+$/.test(kucun)) row.kucun = 0
      },
      // 设置表头
      setTableHeader () {
        this.tableHeader = []
        this.skuAttr = []
        this.shopType.forEach(item => {
          this.tableHeader.push({
            label: item.name,
            prop: item.prop
          })
          this.skuAttr.push(item.prop)
        })
      },
      showAddSkuAttrDialog () {
        this.addSkuAttrDialogVisible = true
        this.addSkuAttrInputFocus()
      },
      submitAddSkuAttr () {
        this.$refs.addSkuAttrFormRef.validate((valid, e) => {
          if (!valid) {
            if (JSON.stringify(e) !== {}) {
              const errMsg = Object.values(e)[0][0].message
              return this.$message.error(errMsg)
            }
          } else {
            const obj = this.addSkuAttrForm
            obj.name = obj.name.trim().toUpperCase()
            const res = this.manyTableData.find(item => item.attr_name === obj.name)
            if (res) return this.$message.error('已存在')
            // 获取当前最大的 ID 值
            const ids = []
            this.manyTableData.forEach(item => ids.push(item.attr_id))
            const newId = Math.max(...ids) + 1
            // 获取英文名
            const enName = this.getCharacterOnly()
            const newSkuAttrObj = {
              attr_id: newId,
              attr_name: obj.name,
              attr_vals: [],
              cb: [],
              en_name: enName,
              inputValue: '',
              inputVisible: false
            }
            this.manyTableData.push(newSkuAttrObj)
          }
        })
      },
      // 生成随机英文并绑定
      createEnNameBind (prefix = '') {
        this.addSkuAttrForm.en_name = prefix + this.getCharacter().toLowerCase()
      },
      // 生成唯一的英文名
      getCharacterOnly () {
        while (this.flag) {
          if (this.i < 3) {
            this.createEnNameBind()
          } else {
            this.createEnNameBind(this.addSkuAttrForm.en_name)
          }
          const res = this.checkEnNameExists()
          if (!res) {
            this.flag = false
          }
          this.i++
        }
        this.initEnNameJishu()
        return this.addSkuAttrForm.en_name
      },
      // 重置获取英文名的配置
      initEnNameJishu () {
        this.i = 0
        this.flag = true
      },
      // 判断英文名是否已经存在
      checkEnNameExists () {
        return this.manyTableData.find(item => item.en_name.toLowerCase() === this.addSkuAttrForm.en_name.toLowerCase())
      },
      /**
       * 随机生成一个字母(可设置大小写)
       * @param flag
       * @returns {string}
       */
      getCharacter (flag = 'upper') {
        let res
        switch (flag) {
          case 'upper':
            res = String.fromCharCode(Math.floor(Math.random() * 26) + 'A'.charCodeAt(0))
            break
          case 'lower':
            res = String.fromCharCode(Math.floor(Math.random() * 26) + 'a'.charCodeAt(0))
            break
          default:
            res = ''
            break
        }
        return res
      },
      addSkuAttrDialogClosed () {
        if (this.$refs.addSkuAttrFormRef) this.$refs.addSkuAttrFormRef.resetFields()
      }
    }
    
  • computed

    computed: {
      ...mapState([
        'manyTableData',
        'spanArr'
      ]),
      tableHeader: {
        get () {
          return this.$store.state.tableHeader
        },
        set (val) {
          this.$store.state.tableHeader = val
        }
      },
      skuAttr: {
        get () {
          return this.$store.state.skuAttr
        },
        set (val) {
          this.$store.state.skuAttr = val
        }
      },
      tableData7: {
        get () {
          return this.$store.state.tableData7
        },
        set (val) {
          this.$store.state.tableData7 = val
        }
      },
      shopType: {
        get () {
          return this.$store.state.shopType
        },
        set (val) {
          this.$store.state.shopType = val
        }
      },
      addSkuAttrDialogVisible: {
        get () {
          return this.$store.state.addSkuAttrDialogVisible
        },
        set (val) {
          this.$store.state.addSkuAttrDialogVisible = val
        }
      },
      addSkuAttrForm: {
        get () {
          return this.$store.state.addSkuAttrForm
        },
        set (val) {
          this.$store.state.addSkuAttrForm = val
        }
      },
      addSkuAttrFormRule: {
        get () {
          return this.$store.state.addSkuAttrFormRule
        },
        set (val) {
          this.$store.state.addSkuAttrFormRule = val
        }
      },
      i: {
        get () {
          return this.$store.state.i
        },
        set (val) {
          this.$store.state.i = val
        }
      },
      flag: {
        get () {
          return this.$store.state.flag
        },
        set (val) {
          this.$store.state.flag = val
        }
      }
    }
    

效果图

alt 笛卡尔积 sku

写博客只是记录学习过程,如果有错误,请各位大佬批评更正。谢谢!

推荐阅读