vue3 电商类网站实现规格的选择

发布于:2025-06-20 ⋅ 阅读:(22) ⋅ 点赞:(0)

目前有一个这样的需求 类似淘宝 京东选择 

但是在人家大厂给的数据我不清除是什么样子的 

我这边后端给的数据 一开始是想把规格全部显示出来的 发现实现不了 后端的数据有限

因为必须选择一个颜色 才可以对应的第二个规格 才知道有没有库存

因为这个库存 是由两个规格决定的 这种的话 要想 做成京东或者淘宝那种 

无库存无法显示的效果是不行的 看一下我的后端给的数据

 

[
    {
        "id": "1384947912936914944",
        "image": "",
        "stock": 0,
        "supplyCode": "",
        "sellerPrice": "8250",
        "masterAttrName": "颜色",
        "masterAttrValue": "紫绿色调00374",
        "slaveAttrName": "参考身高",
        "slaveAttrValue": "160cm"
    },
    {
        "id": "1384947912953692160",
        "image": "",
        "stock": 19,
        "supplyCode": "",
        "sellerPrice": "8250",
        "masterAttrName": "颜色",
        "masterAttrValue": "紫绿色调00374",
        "slaveAttrName": "参考身高",
        "slaveAttrValue": "165cm"
    },
    {
        "id": "1384947912970469376",
        "image": "",
        "stock": 50,
        "supplyCode": "",
        "sellerPrice": "8250",
        "masterAttrName": "颜色",
        "masterAttrValue": "红绿色调00364",
        "slaveAttrName": "参考身高",
        "slaveAttrValue": "160cm"
    },
    {
        "id": "1384947912987246592",
        "image": "",
        "stock": 100,
        "supplyCode": "",
        "sellerPrice": "8250",
        "masterAttrName": "颜色",
        "masterAttrValue": "红绿色调00364",
        "slaveAttrName": "参考身高",
        "slaveAttrValue": "165cm"
    }
]

这里面的数据 stock 是 0 的话 我前端自己又加了一个数据 isDisable字段 为true 无法选择

需要把这个数据整体更换成 这样的就好了

[
    {
        "value": "紫绿色调00374",
        "disabled": false,
        "heights": [
            {
                "value": "160cm",
                "stock": 0,
                "disabled": true
            },
            {
                "value": "165cm",
                "stock": 19,
                "disabled": false
            }
        ]
    },
    {
        "value": "红绿色调00364",
        "disabled": false,
        "heights": [
            {
                "value": "160cm",
                "stock": 50,
                "disabled": false
            },
            {
                "value": "165cm",
                "stock": 100,
                "disabled": false
            }
        ]
    }
]

最终我们需要这样的数据来显示 就好了

我认为这样的数据 才是 最符合 的 很明显 后端给的数据 差距太大 说实话 

我一下子没有想起来怎么把上面的数据处理成下面这样

我也是搜了搜

使用 new Map  映射一下 最后在Array.from(new Map.values)  映射出来就好了

我直接上代码吧

      colorOptions: [],
      heightOptions: [],
      filterOptions: [],  中转 相当于选择了规格1 的经过 去筛选规格2 的显示
      selectedColor: null, //规格1选择的结果
      selectedHeight: null    //规格1选择的结果



initOptions() {
      const colorMap = new Map();
      const heightMap = new Map();

      // 首先收集所有颜色和身高选项
      this.originalData.forEach(product => {
        // 处理颜色选项
        if (!colorMap.has(product.masterAttrValue)) {
          colorMap.set(product.masterAttrValue, {
            value: product.masterAttrValue,
            disabled: false, // 初始不禁用
            heights: new Map() // 存储身高和对应的库存
          });
        }
        const colorOption = colorMap.get(product.masterAttrValue);
        colorOption.heights.set(product.slaveAttrValue, product.stock);

        // 处理身高选项
        if (!heightMap.has(product.slaveAttrValue)) {
          heightMap.set(product.slaveAttrValue, {
            value: product.slaveAttrValue,
            disabled: product.stock === 0, // 身高选项的禁用状态基于库存
            colors: new Map() // 存储颜色和对应的库存
          });
        }
        const heightOption = heightMap.get(product.slaveAttrValue);
        heightOption.colors.set(product.masterAttrValue, product.stock);
      });

      // 转换颜色选项:只有当所有身高都缺货时才禁用颜色
      this.colorOptions = Array.from(colorMap.values()).map(color => {
        const hasStock = Array.from(color.heights.values()).some(stock => stock > 0);
        return {
          ...color,
          disabled: !hasStock,
          heights: Array.from(color.heights.entries()).map(([height, stock]) => ({
            value: height,
            stock,
            disabled: stock === 0
          }))
        };
      });

      // 转换身高选项
      this.heightOptions = Array.from(heightMap.values()).map(height => ({
        ...height,
        colors: Array.from(height.colors.entries()).map(([color, stock]) => ({
          value: color,
          stock,
          disabled: stock === 0
        }))
      }));
      console.log(this.colorOptions, 'this.colorOptions');
      console.log(this.heightOptions, 'this.colorOptions');
    },

上面的数据到下面 这个方法就够了

然后页面上显示我也拿出来

  <div class="cate-selected-box">
              <div class="cate-title"> {{ info.masterAttrName }}</div>
              <ul class="cate-list">
                <li class="level1-item" v-for="color in colorOptions" :key="color.value" @click="selectColor(color)"
                  :class="{ 'disabled': color.disabled, 'selected': selectedColor === color.value }">
                  {{ color.value }}
                  <span v-if="color.disabled">(缺货)</span>

                </li>
              </ul>
              <div class="cate-title"> {{ info.slaveAttrName }}</div>

              <ul class="cate-list" v-if="filterOptions.length">
                <li class="level1-item" v-for="item in filterOptions" v-if="item.value != '-'" :key="item.value"
                  @click.stop="selectHeight(item)"
                  :class="{ 'disabled': item.disabled, 'selected': selectedHeight === item.value }">
                  {{ item.value }}
                  <span v-if="item.disabled">(缺货)</span>
                </li>
              </ul>
              <div class="selected-info" v-if="info.productSpecials && info.productSpecials.length > 1">
                <div class="item">
                  <div class="label">
                    已选择:
                  </div>
                  <!-- &&  -->
                  <div class="value"
                    v-if="(info.slaveAttrValues && info.slaveAttrValues.length) && (info.masterAttrValues && info.masterAttrValues.length)">

                    <div v-if="selectedColor && selectedHeight">
                      {{ selectedColor }} - {{ selectedHeight }}
                    </div>
                  </div>
                  <div class="value"
                    v-if="(!info.slaveAttrValues) && (info.masterAttrValues && info.masterAttrValues.length)">
                    <div v-if="selectedColor">
                      {{ selectedColor }}
                    </div>
                  </div>
                </div>
              </div>
            </div>
li {
  list-style: none;
  padding: 0;
  margin: 0;
}

ul {
  padding: 0;
  margin: 0;
}

.cate-selected-box {
  .cate-title {
    font-weight: 700;
    margin: 10px 0;

  }

  .selected-info {
    margin: 15px;
    margin-top: 25px;

    .item {
      display: flex;
      align-items: center;

      .label {
        color: 858a99;

      }

      .value {
        color: #6034b9;
        margin-left: 10px;

      }
    }

  }


  .cate-list {
    cursor: pointer;
    display: flex;
    flex-wrap: wrap;
    align-items: center;


    .level1-item {
      padding: 10px 20px;
      border: 1px solid #f7f8f9;
      border-radius: 5px;
      transition: all 0.3s;
      margin: 6px;
    }

    .level1-item:hover {
      border: 1px solid #6034b9;
    }

    .level1-item:nth-child(n+2) {
      // margin-left: 10px;
      // margin: 10px;
    }




  }

}

.disabled {
  color: #ccc;
  cursor: not-allowed;
}

.selected {
  background-color: #6034b9;
  color: white;
  border-color: #6034b9;
}

这一部分的样式我也显示出来了

大家如果把这个放到vue 文件中 是直接能够显示效果的

这里面的难点其实是这个 new Map 的使用 我也使用的好少 Es6 的方法如果说不使用 这个 其实 也能够想办法 把这个处理好 但是方法不太好想

这些都是经验吗 每次记一点 下次遇到了就能有想法了

学习前端就是这样 哪怕你死记硬背 有时候 也有用 就会知道什么方法处理什么问题


网站公告

今日签到

点亮在社区的每一天
去签到