element根据输入,动态生成表格(版本二)

在这里插入图片描述

<template>
  <div class="sku-edit-simple">
    <el-form ref="skuForm" label-width="110px">
      <!-- SPU规格管理 -->
      <el-form-item label="spu规格" required>
        <div class="spec-container">
          <div 
            v-for="(spec, specIndex) in specifications" 
            :key="specIndex" 
            class="spec-row"
          >
            <!-- 规格名称 -->
            <div class="spec-header">
              <el-select
                v-model="spec.name"
                filterable
                allow-create
                placeholder="选择或输入规格名称"
                size="small"
                style="width: 200px;"
                @change="onSpecNameChange(specIndex, $event)"
                @blur="validateSpecName(specIndex)"
              >
                <el-option
                  v-for="option in specOptions"
                  :key="option.value"
                  :label="option.label"
                  :value="option.label"
                />
              </el-select>
              
              <!-- 添加规格按钮 -->
              <el-button
                type="text"
                icon="el-icon-circle-plus"
                size="small"
                @click="addSpecification"
                class="add-btn"
              />
              
              <!-- 删除规格按钮 -->
              <el-button
                v-if="specifications.length > 1"
                type="text"
                icon="el-icon-remove"
                size="small"
                @click="removeSpecification(specIndex)"
                class="remove-btn"
              />
            </div>
            
            <!-- 规格值列表 -->
            <div class="spec-values">
              <div 
                v-for="(value, valueIndex) in spec.values" 
                :key="valueIndex"
                class="value-item"
              >
                <el-input
                  v-model="spec.values[valueIndex]"
                  placeholder="输入规格值"
                  size="small"
                  style="width: 120px;"
                  @blur="validateSpecValue(specIndex, valueIndex)"
                />
                
                <!-- 添加规格值按钮 -->
                <el-button
                  type="text"
                  icon="el-icon-circle-plus"
                  size="small"
                  @click="addSpecValue(specIndex)"
                  class="add-btn"
                />
                
                <!-- 删除规格值按钮 -->
                <el-button
                  v-if="spec.values.length > 1"
                  type="text"
                  icon="el-icon-remove"
                  size="small"
                  @click="removeSpecValue(specIndex, valueIndex)"
                  class="remove-btn"
                />
              </div>
            </div>
          </div>
        </div>
      </el-form-item>

      <!-- SKU映射表格 -->
      <el-form-item label="映射sku" required>
        <div class="sku-table-container">
          <el-table
            :data="skuList"
            border
            size="small"
            style="width: 100%"
            empty-text="请先添加规格和规格值"
          >
            <!-- 动态规格列 -->
            <el-table-column
              v-for="(spec, index) in specifications"
              :key="index"
              :label="spec.name || `规格${index + 1}`"
              width="120"
            >
              <template slot-scope="scope">
                <span>{{ scope.row.specValues[index] || '-' }}</span>
              </template>
            </el-table-column>
            
            <!-- 商品条码 -->
            <el-table-column
              prop="barcode"
              label="商品条码"
              width="150"
            >
              <template slot-scope="scope">
                <el-input
                  v-model="scope.row.barcode"
                  size="small"
                  placeholder="请输入条码"
                />
              </template>
            </el-table-column>
            
            <!-- 售价 -->
            <el-table-column
              prop="price"
              label="售价"
              width="120"
            >
              <template slot-scope="scope">
                <el-input
                  v-model="scope.row.price"
                  size="small"
                  placeholder="请输入售价"
                  type="number"
                />
              </template>
            </el-table-column>
            
            <!-- 市场价 -->
            <el-table-column
              prop="marketPrice"
              label="市场价"
              width="120"
            >
              <template slot-scope="scope">
                <el-input
                  v-model="scope.row.marketPrice"
                  size="small"
                  placeholder="请输入市场价"
                  type="number"
                />
              </template>
            </el-table-column>
          </el-table>
        </div>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
import { getOptions } from '@/api/common/common'

export default {
  name: 'SkuEditSimple',
  props: {
    // 编辑模式下的初始数据
    initialData: {
      type: Object,
      default: () => ({
        specifications: [],
        skuList: []
      })
    },
    // 是否为编辑模式
    isEdit: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      // 规格选项
      specOptions: [],
      // 规格列表
      specifications: [
        {
          name: '',
          values: ['']
        }
      ],
      // SKU列表
      skuList: [],
      // 验证错误信息
      validationErrors: []
    }
  },
  watch: {
    // 监听规格变化,重新生成SKU列表
    specifications: {
      handler: 'generateSkuList',
      deep: true
    }
  },
  mounted() {
    this.initData()
    this.loadSpecOptions()
  },
  methods: {
    // 初始化数据
    initData() {
      if (this.isEdit && this.initialData.specifications.length > 0) {
        this.specifications = JSON.parse(JSON.stringify(this.initialData.specifications))
        this.skuList = JSON.parse(JSON.stringify(this.initialData.skuList))
      }
    },
    
    // 加载规格选项
    async loadSpecOptions() {
      try {
        const response = await getOptions({ option_names: ['spuSpecOptions'] })
        this.specOptions = response.data.spuSpecOptions || []
      } catch (error) {
        console.error('加载规格选项失败:', error)
        this.$message.error('加载规格选项失败')
      }
    },
    
    // 添加规格
    addSpecification() {
      this.specifications.push({
        name: '',
        values: ['']
      })
    },
    
    // 删除规格
    removeSpecification(index) {
      this.$confirm('确定要删除这个规格吗?删除后相关的SKU数据也会被删除。', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.specifications.splice(index, 1)
        this.$message.success('规格删除成功')
      }).catch(() => {
        // 用户取消删除
      })
    },
    
    // 添加规格值
    addSpecValue(specIndex) {
      this.specifications[specIndex].values.push('')
    },
    
    // 删除规格值
    removeSpecValue(specIndex, valueIndex) {
      this.$confirm('确定要删除这个规格值吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.specifications[specIndex].values.splice(valueIndex, 1)
        this.$message.success('规格值删除成功')
      }).catch(() => {
        // 用户取消删除
      })
    },
    
    // 规格名称变化处理
    onSpecNameChange(specIndex, value) {
      this.validateSpecName(specIndex)
      this.generateSkuList()
    },
    
    // 验证规格名称
    validateSpecName(specIndex) {
      const spec = this.specifications[specIndex]
      
      // 检查是否为空
      if (!spec.name || spec.name.trim() === '') {
        this.$message.warning('规格名称不能为空')
        return false
      }
      
      // 检查是否重复
      const duplicateIndex = this.specifications.findIndex((s, index) => 
        index !== specIndex && s.name === spec.name
      )
      
      if (duplicateIndex !== -1) {
        this.$message.warning('规格名称不能重复')
        return false
      }
      
      return true
    },
    
    // 验证规格值
    validateSpecValue(specIndex, valueIndex) {
      const spec = this.specifications[specIndex]
      const value = spec.values[valueIndex]
      
      // 检查是否为空
      if (!value || value.trim() === '') {
        this.$message.warning('规格值不能为空')
        return false
      }
      
      // 检查是否重复
      const duplicateIndex = spec.values.findIndex((v, index) => 
        index !== valueIndex && v === value
      )
      
      if (duplicateIndex !== -1) {
        this.$message.warning('规格值不能重复')
        return false
      }
      
      return true
    },
    
    // 生成SKU列表
    generateSkuList() {
      // 过滤有效的规格名(有名称且有值)
      const validSpecs = this.specifications.filter(spec =>
        // 规格名称不为空
        spec.name && spec.name.trim() !== '' && 
        // 规格值数组不为空
        spec.values.length > 0 && 
        // 至少有一个非空的规格值
        spec.values.some(value => value && value.trim() !== '')
      )
      
      if (validSpecs.length === 0) {
        this.skuList = []
        return
      }
      
      // 生成规格值组合
      const combinations = this.generateCombinations(validSpecs)
      
      // 生成SKU列表
      this.skuList = combinations.map(combination => {
        // 查找是否已存在相同的SKU(编辑模式)
        const existingSku = this.findExistingSku(combination)
        
        return {
          id: existingSku ? existingSku.id : this.generateId(),
          specValues: combination,
          barcode: existingSku ? existingSku.barcode : '',
          price: existingSku ? existingSku.price : '',
          marketPrice: existingSku ? existingSku.marketPrice : ''
        }
      })
    },
    
    // 生成规格值组合
    generateCombinations(specs, currentCombination = [], index = 0) {
      if (index >= specs.length) {
        return [currentCombination]
      }
      
      const combinations = []
      const spec = specs[index]
      const validValues = spec.values.filter(value => value && value.trim() !== '')
      
      for (const value of validValues) {
        const newCombination = [...currentCombination, value]
        const subCombinations = this.generateCombinations(specs, newCombination, index + 1)
        combinations.push(...subCombinations)
      }
      
      return combinations
    },
    
    // 查找已存在的SKU(编辑模式)
    findExistingSku(combination) {
      if (!this.isEdit) return null
      
      return this.skuList.find(sku => {
        if (sku.specValues.length !== combination.length) return false
        
        return combination.every((value, index) => 
          sku.specValues[index] === value
        )
      })
    },
    
    // 生成唯一ID
    generateId() {
      return Date.now() + Math.random().toString(36).substr(2, 9)
    },
    
    // 验证数据
    validate() {
      this.validationErrors = []
      
      // 验证规格
      for (let i = 0; i < this.specifications.length; i++) {
        const spec = this.specifications[i]
        
        if (!spec.name || spec.name.trim() === '') {
          this.validationErrors.push(`${i + 1}个规格名称不能为空`)
          continue
        }
        
        const validValues = spec.values.filter(value => value && value.trim() !== '')
        if (validValues.length === 0) {
          this.validationErrors.push(`规格"${spec.name}"至少需要一个规格值`)
          continue
        }
        
        // 检查规格值重复
        const uniqueValues = new Set(validValues)
        if (uniqueValues.size !== validValues.length) {
          this.validationErrors.push(`规格"${spec.name}"存在重复的规格值`)
        }
      }
      
      // 检查规格名称重复
      const specNames = this.specifications.map(spec => spec.name).filter(name => name && name.trim() !== '')
      const uniqueSpecNames = new Set(specNames)
      if (uniqueSpecNames.size !== specNames.length) {
        this.validationErrors.push('存在重复的规格名称')
      }
      
      return this.validationErrors.length === 0
    },
    
    // 获取数据
    getData() {
      if (!this.validate()) {
        this.$message.error('数据验证失败,请检查输入')
        return null
      }
      
      return {
        specifications: this.specifications.filter(spec => 
          spec.name && spec.name.trim() !== '' && 
          spec.values.some(value => value && value.trim() !== '')
        ),
        skuList: this.skuList
      }
    },
    
    // 重置数据
    reset() {
      this.specifications = [{ name: '', values: [''] }]
      this.skuList = []
      this.validationErrors = []
    }
  }
}
</script>

<style lang="scss" scoped>
.sku-edit-simple {
  padding: 20px;
  
  .spec-container {
    .spec-row {
      margin-bottom: 20px;
      padding: 15px;
      border: 1px solid #e4e7ed;
      border-radius: 4px;
      background-color: #fafafa;
      
      .spec-header {
        display: flex;
        align-items: center;
        margin-bottom: 10px;
        
        .add-btn {
          margin-left: 10px;
          color: #409eff;
          
          &:hover {
            color: #66b1ff;
          }
        }
        
        .remove-btn {
          margin-left: 5px;
          color: #f56c6c;
          
          &:hover {
            color: #f78989;
          }
        }
      }
      
      .spec-values {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
        
        .value-item {
          display: flex;
          align-items: center;
          
          .add-btn {
            margin-left: 5px;
            color: #409eff;
            
            &:hover {
              color: #66b1ff;
            }
          }
          
          .remove-btn {
            margin-left: 5px;
            color: #f56c6c;
            
            &:hover {
              color: #f78989;
            }
          }
        }
      }
    }
  }
  
  .sku-table-container {
    margin-top: 10px;
  }
}
</style> 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值