基于vue3 + element plus 的商品规格交互页面demo

13 阅读2分钟

效果图 4月11日.gif

demo 代码

<script setup lang="ts">
import { ElInput, ElMessage, ElMessageBox } from 'element-plus'

// 显示添加规格组表单
const showAddGroupForm = ref(false)
// 新增规格组值
const addSpec = {
  specName: '',
  specValue: '',
}
// 添加规格组
const addSpecGroup = ref([])

// 规格组合列表
function toggleAddSpecForm() {
  showAddGroupForm.value = !showAddGroupForm.value
}

function submitAddGroup() {
  if (addSpec.specName === '' || addSpec.specValue === '') {
    ElMessage.error('规格信息不能为空')
    return false
  }
  const index = addSpecGroup.value.length
  addSpecGroup.value.push({
    id: index,
    title: addSpec.specName,
    values: [addSpec.specValue],
    temp: '',
  })
  // 构建 sku 信息表格
  buildSkuTable()

  addSpec.specName = ''
  addSpec.specValue = ''
  toggleAddSpecForm()
}

/* 删除规格组事件 */
function deleteGroup(index) {
  ElMessageBox.confirm('删除后不可恢复,确认删除该记录吗?', '提示', {
    type: 'warning',
  }).then(() => {
    // 删除指定规则组
    addSpecGroup.value.splice(index, 1)
    // 构建规格组合列表
    buildSkuTable()
  })
}

function addSpecVal(spec) {
  if (spec.temp) {
    spec.values.push(spec.temp)
  }
  spec.temp = ''
  buildSkuTable()
}

function delSpecVal(specValues, index) {
  ElMessageBox.confirm('删除后不可恢复,确认删除该记录吗?', '提示', {
    type: 'warning',
  }).then(() => {
    specValues.splice(index, 1)
    buildSkuTable()
  })
}
// 构建sku表格
let tableData = []
function buildSkuTable() {
  const specKeys = addSpecGroup.value.map(spec => spec.title)
  const specCombinations = cartesianProduct(addSpecGroup.value.map(spec => spec.values))
  // 生成 SKU 列表
  tableData = specCombinations.map((spec) => {
    const specObj = []
    spec.forEach((value, index) => {
      const tmp = {
        title: specKeys[index],
        val: value,
      }
      specObj.push(tmp)
    })
    return specObj
  })
}
// 笛卡尔积处理规格
function cartesianProduct(arr) {
  return arr.reduce((total, currentValue) => {
    return total.flatMap(value => currentValue.map(v => [].concat(value, v)))
  }, [[]])
}

function changeVal() {
  console.log(tableData)
}
</script>

<template>
  <div class="add-spec-btn">
    <ElText size="default">
      产品规格:
    </ElText>
    <ElButton type="primary" size="default" @click="toggleAddSpecForm">
      添加规格
    </ElButton>
  </div>

  <!-- 规格 -->
  <div class="mb2">
    <!-- 添加规格 -->
    <div v-show="showAddGroupForm" class="spec-form mt-4 flex">
      <div class="ml-2">
        <ElText size="default">
          规格名:
        </ElText>
        <ElInput v-model="addSpec.specName" placeholder="请输入规格名称" @input="$forceUpdate()" />
      </div>
      <div class="ml-4">
        <ElText size="default">
          规格值:
        </ElText>
        <ElInput v-model="addSpec.specValue" placeholder="请输入规格名称" @input="$forceUpdate()" @keyup.enter="submitAddGroup" />
      </div>
      <div class="ml-4">
        <ElButton type="primary" @click="submitAddGroup">
          确定
        </ElButton>
        <ElButton @click="toggleAddSpecForm">
          取消
        </ElButton>
      </div>
    </div>
    <!-- 规格组 -->
    <div v-if="addSpecGroup.length > 0" class="spec-list ml-6 mt-4 flex">
      <div v-for="(spec, i) in addSpecGroup" :key="i" class="spec-info">
        <div class="spec-info-detail spec-info-key">
          <span>
            {{ spec.title }}
          </span>
          <a href="javascript:void(0)" @click="deleteGroup(i)">
            <SvgIcon name="i-ep:delete" />
          </a>
        </div>
        <div class="spec-info-detail spec-info-value flex-inline">
          <ElTag
            v-for="(spec_item, j) in spec.values"
            :key="j"
            class="mr-2"
            closable
            :disable-transitions="false"
            @close="delSpecVal(spec.values, j)"
          >
            {{ spec_item }}
          </ElTag>
          <ElInput
            v-model="spec.temp"
            class="mr-1 w-20 flex"
            size="small"
            @keyup.enter="addSpecVal(spec)"
          />
          <ElButton type="primary" class="button-new-tag" size="small" @click="addSpecVal(spec)">
            + 添加
          </ElButton>
        </div>
      </div>
    </div>
    <!-- 规格表格 -->
    <div v-if="tableData.length > 0" class="spec-table ml-6 mt-4">
      <ElTable :data="tableData" border style="width: 100%">
        <ElTableColumn v-for="(spec, index) in addSpecGroup" :key="index" :label="spec.title">
          <template #default="scope">
            <span>{{ scope.row[index].val }}</span>
          </template>
        </ElTableColumn>
        <ElTableColumn label="销售价">
          <template #default="scope">
            <ElInput v-model="scope.row.sale_price" @input="changeVal" />
          </template>
        </ElTableColumn>
        <ElTableColumn label="划线价">
          <template #default="scope">
            <ElInput v-model="scope.row.price" />
          </template>
        </ElTableColumn>
        <ElTableColumn label="库存">
          <template #default="scope">
            <ElInput v-model="scope.row.stock" />
          </template>
        </ElTableColumn>
      </ElTable>
    </div>
  </div>
</template>

<style>
.el-input {
  --el-input-width: 200px;
}
.spec-list {
  padding: 5px;
  display: flex;
  justify-content: flex-start;
  flex-wrap: wrap;
  border: 1px solid #dfecf8;
  background: #f6f9fc;
}
.spec-info {
  width: 90%;
  border: 1px solid #dfecf8;
  position: relative;
  padding: 5px;
  background: #ffffff;
  margin: 10px 10px;
}
.spec-table {
  padding: 5px;
  display: flex;
  justify-content: flex-start;
  flex-wrap: wrap;
  border: 1px solid #dfecf8;
}

.spec-info-detail {
  height: 36px;
  display: flex;
  margin-left: 6px;
}
.spec-info-key {
  padding: 4px;
  font-size: 12px;
  font-weight: bold;
  justify-content: space-between;
  border-bottom: 1px solid #dfecf8;
}
.spec-info-value {
  padding: 6px;
}
</style>