电商平台spu和sku的完整设计

news2024/11/26 10:25:05

一、关于数据库表的设计

  • 1、商品属性表

    比如一个衣服有颜色、尺码、款式这个叫属性表

    -- ------------------------
    -- 商品属性表
    -- ------------------------
    DROP TABLE IF EXISTS `attribute`;
    CREATE TABLE `attribute` (
        `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT  COMMENT '主键id',
        `name` varchar(50) not null  COMMENT '属性名',
        `status` tinyint(4) DEFAULT 0 COMMENT '状态,0表示正常,1表示禁用',
        `remark` varchar(100) default  null comment '备注',
        `created_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
        `updated_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
        `deleted_at` timestamp(6) NULL DEFAULT NULL COMMENT '软删除时间',
        UNIQUE KEY `UK_name_deleted_at` (`name`,`deleted_at`)
    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT = "商品属性表";
    
  • 2、商品属性值表

    上面说的颜色,可能是红色、黄色、绿色,尺码可能是S、M、L

    -- ------------------------
    -- 商品属性值表
    -- ------------------------
    DROP TABLE IF EXISTS `attribute_value`;
    CREATE TABLE `attribute_value` (
        `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT  COMMENT '主键id',
        `attribute_id` int(11) not null comment '关联到attribute表主键id',
        `name` varchar(50) not null  COMMENT '属性值',
        `status` tinyint(4) DEFAULT 0 COMMENT '状态,0表示正常,1表示禁用',
        `remark` varchar(100) default  null comment '备注',
        `created_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
        `updated_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
        `deleted_at` timestamp(6) NULL DEFAULT NULL COMMENT '软删除时间',
        UNIQUE KEY `UK_attribute_id_name_deleted_at` (`attribute_id`,`name`,`deleted_at`)
    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT = "商品属性值表";
    
  • 3、spu表,或者直接叫商品表也可以的

    spu表比如我们说的苹果手机、华为手机、戴尔笔记本这样的叫spu

    -- ------------------------
    -- 商品spu表
    -- ------------------------
    DROP TABLE IF EXISTS `spu`;
    CREATE TABLE `spu` (
          `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT  COMMENT '主键id',
          `name` varchar(50) not null  COMMENT '商品名称',
          `keyword` varchar(50) not null  COMMENT '关键词',
          `introduction` varchar(100) not null  COMMENT '简介',
          `category_id` int(11) not null  COMMENT '关联到category表主键id',
          `brand_id` int(11) default null comment  '关联到brand表主键id',
          `pic_url` varchar(200) default  null comment  '封面图',
          `video_url` varchar(200) default  null comment  '视频地址',
          `slider_pic_urls` varchar(500) default null COMMENT '商品轮播图地址',
          `unit` int(11) default  null comment  '单位,关联到dict表主键id',
          `spec_type` tinyint(4) default 0 comment '单双规格,0表示单规格,1表示多规格(sku)',
          `price` decimal(10,2) default '0.00' comment '商品单价',
          `market_price` decimal(10,2) default '0.00' comment '商品市场价',
          `discount_price` decimal(10,2) default '0.00' comment '商品折扣价',
          `vip_price` decimal(10,2) default '0.00' comment '商品vip价',
          `cost_price` decimal(10,2) default '0.00' comment '商品成本价',
          `stock` int(11) default 0 comment '库存(如果是多规格求和)',
          `sort` int(11) DEFAULT 1 COMMENT '排序',
          `status` tinyint(4) DEFAULT 0 COMMENT '状态,0表示正常,1表示禁用',
          `is_hot` tinyint(4) DEFAULT 0 COMMENT '是否热销,0表示不是,1表示是',
          `is_benefit` tinyint(4) DEFAULT 0 COMMENT '是否优惠推荐,0表示不是,1表示是',
          `is_best` tinyint(4) DEFAULT 0 COMMENT '是否精品,0表示不是,1表示是',
          `is_new` tinyint(4) DEFAULT 0 COMMENT '是否新品,0表示不是,1表示是',
          `is_good` tinyint(4) DEFAULT 0 COMMENT '是否优品推荐,0表示不是,1表示是',
          `give_integral` int(11) DEFAULT 0 COMMENT '赠送积分',
          `sales_count` int(11) DEFAULT 0 COMMENT '销量',
          `browse_count` int(11) DEFAULT 0 COMMENT '浏览数',
          `content` text NOT NULL COMMENT '商品详情',
          `attribute` varchar(1000) default null COMMENT '销售属性数组,JSON 格式',
          `remark` varchar(100) default  null comment '备注',
          `created_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
          `updated_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
          `deleted_at` timestamp(6) NULL DEFAULT NULL COMMENT '软删除时间',
          UNIQUE KEY `UK_name_deleted_at` (`name`,`deleted_at`)
    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT = "商品spu表";
    
  • 4、sku

    上面说的苹果手机,可能是【黄色-128GB-大陆版】,【白色-256GB-港版】这样的叫sku

    -- ------------------------
    -- 商品sku表
    -- ------------------------
    DROP TABLE IF EXISTS `sku`;
    CREATE TABLE `sku` (
           `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT  COMMENT '主键id',
           `spu_id` int(11) NOT NULL comment '关联到spu主键id',
           `attribute_value` varchar(200) default null comment '销售属性值:用英文,拼接',
           `price` decimal(10,2) default '0.00' comment '商品单价',
           `market_price` decimal(10,2) default '0.00' comment '商品市场价',
           `discount_price` decimal(10,2) default '0.00' comment '商品折扣价',
           `vip_price` decimal(10,2) default '0.00' comment '商品vip价',
           `cost_price` decimal(10,2) default '0.00' comment '商品成本价',
           `bar_code` varchar(64)  DEFAULT NULL COMMENT 'SKU 的条形码',
           `pic_url` varchar(200)  NOT NULL COMMENT '图片地址',
           `stock` int DEFAULT NULL COMMENT '库存',
           `weight` double DEFAULT NULL COMMENT '商品重量,单位:kg 千克',
           `volume` double DEFAULT NULL COMMENT '商品体积,单位:m^3 平米',
           `sales_count` int DEFAULT NULL COMMENT '商品销量',
           `created_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
           `updated_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
           `deleted_at` timestamp(6) NULL DEFAULT NULL COMMENT '软删除时间'
    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT = "商品sku表";
    

二、前端选择销售属性

  • 1、最后效果图

    在这里插入图片描述

  • 2、上面的数据提前手动插入到数据库中的

在这里插入图片描述


在这里插入图片描述

  • 3、后端提供一个接口先查询全部的商品属性,在根据商品属性的id去查询商品属性值,这块可以自己见代码

三、选择商品属性生成SKU

  • 1、效果展示

在这里插入图片描述

  • 2、由销售属性来生成下面的表格的方法

    const checkPropertyList = ref([]);
      /**
       * 生成sku数据
       * @param {*} skuAttribute 选中的商品属性
       */
      const generateSku = (skuAttribute) => {
        console.log(JSON.stringify(skuAttribute));
        const attrValue = [];
        // 获取选中的属性
        const checkList = [];
        for (const item of skuAttribute) {
          attrValue.push(item.attributeItem.filter((it) => item.checkList.includes(it.id)));
          checkList.push(item.id); // 选中的主键id
        }
        checkPropertyList.value = checkList;
        console.log(attrValue, '???');
        if (attrValue.length == 0) {
          tableData.value = [];
          return;
        }
        // 处理添加一个属性的时候表格置空
        if (!attrValue[attrValue.length - 1].length) {
          return;
        }
        // 循环组成sku数据
        const skuList = attrValue
          .reduce((pre, cur) => {
            let res = [];
            for (const item of pre) {
              for (const it of cur) {
                let t = item.name + ',' + it.name;
                res.push({
                  name: t,
                  url: item.url || it.url || '',
                });
              }
            }
            return res;
          })
          .map((it) => {
            const oldData = afterSku.value.find((item) => item.name == it.name);
            return {
              ...it,
              id: oldData ? oldData.id : '',
              price: oldData ? oldData.price : '', // 单价
              stock: oldData ? oldData.stock : '', // 库存
            };
          });
        tableData.value = skuList;
      };
    
  • 3、在什么时候调用上面这个方法呢?,直接监听销售属性的变化就可以

    watch(
        () => skuAttributes.value,
        (newValue) => {
          generateSku(cloneDeep(newValue));
        },
        {
          deep: true,
        }
    );
    // 监听sku表格的变化,并将当前sku进行备份
    const afterSku = ref([]);
    watch(
        () => tableData.value,
        (value) => {
          afterSku.value = cloneDeep(value);
        },
        { deep: true }
    );
    
  • 4、点击按钮提交数据给后端

    const submitHandler = async () => {
        ElMessage.success('请查看浏览器控制台');
        console.log('销售属性:', JSON.stringify(skuAttributes.value));
        console.log('表格数据:', JSON.stringify(tableData.value));
        const postData = {
          name: '苹果13',
          keyword: '苹果手机',
          introduction: 'laboris sint in',
          categoryId: 10,
          brandId: 50,
          picUrl: 'http://dummyimage.com/400x400',
          videoUrl: 'http://voanozyj.vg/suuadzgv',
          sliderPicUrls: 'http://dummyimage.com/400x400',
          unit: 25,
          specType: 79,
          price: 90,
          marketPrice: 11,
          discountPrice: 82,
          vipPrice: 32,
          costPrice: 87,
          stock: 19,
          sort: 33,
          isHot: 0,
          isBenefit: 0,
          isBest: 0,
          isNew: 0,
          isGood: 0,
          giveIntegral: 43,
          content: '手机详情',
          remark: 'magna eu laboris',
          // 过滤掉没有选择的sku属性
          attribute: JSON.stringify(skuAttributes.value),
          skuList: tableData.value.map((item) => {
            return {
              ...item,
              attributeValue: item.name,
              barCode: item.name,
              discountPrice: item.price,
              costPrice: item.price,
              picUrl: 'http://dummyimage.com/400x400',
              vipPrice: item.price,
              volume: 85,
              marketPrice: 26,
              weight: 50,
            };
          }),
        };
        console.log(JSON.stringify(postData), '提交数据');
        const data = await SkuService.createSpuApi(postData);
        console.log(data);
      };
    
  • 5、完整代码如下

    <template>
      <div class="sku">
        <el-card shadow="never">
          <el-form>
            <el-form-item label="销售属性">
              <el-card
                shadow="never"
                v-for="(item, index) of skuAttributes"
                :key="index"
                :gutter="20"
                class="sku-row"
              >
                <el-button type="danger" class="delete-row-btn" @click="deleteRowHandler(index)"
                  >删除</el-button
                >
                <el-row :gutter="10">
                  <el-col :span="2">属性名称:</el-col>
                  <el-col :span="8">
                    <el-select
                      v-model="item.id"
                      placeholder="请选择属性"
                      @change="changeAttributeHandler"
                    >
                      <el-option
                        v-for="item in attributeItem"
                        :key="item.id"
                        :label="item.name"
                        :value="item.id"
                        :disabled="checkPropertyList.includes(item.id)"
                      />
                    </el-select>
                  </el-col>
                </el-row>
                <el-row :gutter="10">
                  <el-col :span="2"> 属性值: </el-col>
                  <el-col :span="22">
                    <el-checkbox-group v-model="item.checkList">
                      <el-checkbox
                        :label="value.id"
                        v-for="(value, i) of item.attributeItem"
                        :key="i"
                        >{{ value.name }}</el-checkbox
                      >
                    </el-checkbox-group>
                  </el-col>
                </el-row>
              </el-card>
              <div>
                <el-button type="primary" @click="addSkuAttrHandler">增加销售属性</el-button>
              </div>
            </el-form-item>
          </el-form>
        </el-card>
        <!-- 下面表格 -->
        <el-card shadow="never" style="margin-top: 20px">
          <el-table :data="tableData" style="width: 100%" border>
            <el-table-column prop="name" label="销售规格" width="180" />
            <el-table-column prop="price" label="单价">
              <template #default="scope">
                <el-input v-model="scope.row.price" placeholder="单价"></el-input>
              </template>
            </el-table-column>
            <el-table-column prop="stock" label="库存">
              <template #default="scope">
                <el-input v-model="scope.row.stock" placeholder="库存"></el-input>
              </template>
            </el-table-column>
          </el-table>
          <el-button type="primary" style="margin-top: 20px" @click="submitHandler">提交数据</el-button>
        </el-card>
      </div>
    </template>
    
    <script setup>
      import { SkuService } from '@/services';
      import { ref, onMounted, watch } from 'vue';
      import { cloneDeep } from 'lodash';
      import { ElMessage } from 'element-plus';
      const skuAttributes = ref([]);
      // 添加属性
      const addSkuAttrHandler = () => {
        skuAttributes.value.push({
          id: null,
          attributeItem: [],
          checkList: [],
        });
      };
      // 删除
      const deleteRowHandler = (index) => {
        skuAttributes.value.splice(index, 1);
      };
      // 1.获取全部的销售属性
      const attributeItem = ref([]);
      const getAllAttrApi = async () => {
        const data = await SkuService.getAttributeListApi();
        attributeItem.value = data.result;
      };
      // 切换的时候
      const changeAttributeHandler = async (item) => {
        let currentItem = skuAttributes.value.find((it) => it.id == item);
        const currentIndex = skuAttributes.value.findIndex((it) => it.id == item);
        const data = await SkuService.getAttributeValueByAttributeIdApi(item);
        currentItem.attributeItem = data.result;
        currentItem.name = attributeItem.value.find((it) => it.id == item)?.name;
        // 替换之前的
        skuAttributes.value.splice(currentIndex, 1, currentItem);
      };
      // 表格数据
      const tableData = ref([]);
      const submitHandler = async () => {
        ElMessage.success('请查看浏览器控制台');
        console.log('销售属性:', JSON.stringify(skuAttributes.value));
        console.log('表格数据:', JSON.stringify(tableData.value));
        const postData = {
          name: '苹果13',
          keyword: '苹果手机',
          introduction: 'laboris sint in',
          categoryId: 10,
          brandId: 50,
          picUrl: 'http://dummyimage.com/400x400',
          videoUrl: 'http://voanozyj.vg/suuadzgv',
          sliderPicUrls: 'http://dummyimage.com/400x400',
          unit: 25,
          specType: 79,
          price: 90,
          marketPrice: 11,
          discountPrice: 82,
          vipPrice: 32,
          costPrice: 87,
          stock: 19,
          sort: 33,
          isHot: 0,
          isBenefit: 0,
          isBest: 0,
          isNew: 0,
          isGood: 0,
          giveIntegral: 43,
          content: '手机详情',
          remark: 'magna eu laboris',
          // 过滤掉没有选择的sku属性
          attribute: JSON.stringify(skuAttributes.value),
          skuList: tableData.value.map((item) => {
            return {
              ...item,
              attributeValue: item.name,
              barCode: item.name,
              discountPrice: item.price,
              costPrice: item.price,
              picUrl: 'http://dummyimage.com/400x400',
              vipPrice: item.price,
              volume: 85,
              marketPrice: 26,
              weight: 50,
            };
          }),
        };
        console.log(JSON.stringify(postData), '提交数据');
        const data = await SkuService.createSpuApi(postData);
        console.log(data);
      };
      watch(
        () => skuAttributes.value,
        (newValue) => {
          generateSku(cloneDeep(newValue));
        },
        {
          deep: true,
        }
      );
      // 监听sku表格的变化,并将当前sku进行备份
      const afterSku = ref([]);
      watch(
        () => tableData.value,
        (value) => {
          afterSku.value = cloneDeep(value);
        },
        { deep: true }
      );
      const checkPropertyList = ref([]);
      /**
       * 生成sku数据
       * @param {*} skuAttribute 选中的商品属性
       */
      const generateSku = (skuAttribute) => {
        console.log(JSON.stringify(skuAttribute));
        const attrValue = [];
        // 获取选中的属性
        const checkList = [];
        for (const item of skuAttribute) {
          attrValue.push(item.attributeItem.filter((it) => item.checkList.includes(it.id)));
          checkList.push(item.id); // 选中的主键id
        }
        checkPropertyList.value = checkList;
        console.log(attrValue, '???');
        if (attrValue.length == 0) {
          tableData.value = [];
          return;
        }
        // 处理添加一个属性的时候表格置空
        if (!attrValue[attrValue.length - 1].length) {
          return;
        }
        // 循环组成sku数据
        const skuList = attrValue
          .reduce((pre, cur) => {
            let res = [];
            for (const item of pre) {
              for (const it of cur) {
                let t = item.name + ',' + it.name;
                res.push({
                  name: t,
                  url: item.url || it.url || '',
                });
              }
            }
            return res;
          })
          .map((it) => {
            const oldData = afterSku.value.find((item) => item.name == it.name);
            return {
              ...it,
              id: oldData ? oldData.id : '',
              price: oldData ? oldData.price : '', // 单价
              stock: oldData ? oldData.stock : '', // 库存
            };
          });
        tableData.value = skuList;
      };
      onMounted(() => {
        getAllAttrApi();
      });
    </script>
    
    <style lang="scss" scoped>
      .sku-row {
        width: 100%;
        margin-bottom: 10px;
        position: relative;
        .delete-row-btn {
          position: absolute;
          right: 10px;
          top: 10px;
          cursor: pointer;
          z-index: 30;
        }
        .sku-value-item {
          margin-bottom: 10px;
          position: relative;
          .close-icon {
            position: absolute;
            right: 0;
            top: 0;
            z-index: 10;
            cursor: pointer;
            opacity: 0;
          }
          &:hover {
            .close-icon {
              opacity: 1;
            }
          }
        }
      }
    </style>
    

四、前端实现sku选择

  • 1、效果图如下

    在这里插入图片描述

  • 2、实现代码见如下

    <template>
      <div class="sku">
        <h3>iphone 13</h3>
        <div v-for="(item, index) of processAttribute" :key="index">
          <div class="title" style="margin-bottom: 10px; margin-top: 10px">{{ item.title }}</div>
          <template v-for="(item1, index1) of item.attributeItem" :key="index1">
            <el-tag
              type="success"
              :class="[
                {
                  active: item1.activity,
                  disabled: item1.disabled,
                },
              ]"
              style="margin-right: 10px; cursor: pointer"
              @click="skuClickHandler(index, index1)"
              >{{ item1.name }}</el-tag
            >
          </template>
        </div>
        <div>当前选中的库存:{{ stock }}</div>
        <div
          >价格范围:
          <span v-if="minPrice == maxPrice">{{ maxPrice }}</span>
          <span v-else>{{ minPrice }}-{{ maxPrice }}</span>
        </div>
      </div>
    </template>
    
    <script setup>
      import { onMounted } from 'vue';
      import { SkuService } from '@/services';
      import { useRoute } from 'vue-router';
      const route = useRoute();
      const skuAttribute = ref([]);
      const skuList = ref([]);
      const processAttribute = ref([]);
      const processSkuMap = ref({});
      const initData = () => {
        for (const item of skuAttribute.value) {
          let temp = {
            id: item.id,
            title: item.title,
          };
          temp.attributeItem = item.attributeItem
            .filter((it) => item.checkList.includes(it.id)) // 过滤有的属性
            .map((it) => {
              return {
                ...it,
                activity: false,
                disabled: itemquantity(it.name) <= 0, // 判断当前是否小于0的库存
                stock: itemquantity(it.name),
              };
            });
          processAttribute.value.push(temp);
        }
        // 对 skuList 数据进行加工,并存入 processSkuMap 中
        for (const item of skuList.value) {
          let combArr = arrayCombine(item.attributeValue.split(','));
          for (let j = 0; j < combArr.length; j++) {
            var key = combArr[j].join(',');
            if (processSkuMap.value[key]) {
              // 库存累加,价格添加进数组
              processSkuMap.value[key].stock += +item.stock;
              processSkuMap.value[key].prices.push(item.price);
            } else {
              processSkuMap.value[key] = {
                stock: +item.stock,
                prices: [item.price],
              };
            }
          }
        }
        // 计算下
        // skuCheck();
      };
      // 计算当前sku的库存
      const itemquantity = (item) => {
        let quantity = 0;
        skuList.value.forEach((element) => {
          var skuArr = element.attributeValue.split(',');
          if (skuArr.indexOf(item) != -1) {
            quantity += +element.stock;
          }
        });
        return quantity;
      };
      const arrayCombine = (targetArr) => {
        let resultArr = [];
        for (var n = 0; n <= targetArr.length; n++) {
          var flagArrs = getFlagArrs(targetArr.length, n);
          while (flagArrs.length) {
            var flagArr = flagArrs.shift();
            var combArr = Array(targetArr.length);
            for (var i = 0; i < targetArr.length; i++) {
              if (flagArr[i]) {
                combArr[i] = targetArr[i];
              }
            }
            resultArr.push(combArr);
          }
        }
        return resultArr;
      };
    
      const getFlagArrs = (m, n) => {
        let flagArrs = [];
        let flagArr = [];
        let isEnd = false;
        for (let i = 0; i < m; i++) {
          flagArr[i] = i < n ? 1 : 0;
        }
        flagArrs.push(flagArr.concat());
        // 当n不等于0并且m大于n的时候进入
        if (n && m > n) {
          while (!isEnd) {
            var leftCnt = 0;
            for (var i = 0; i < m - 1; i++) {
              if (flagArr[i] == 1 && flagArr[i + 1] == 0) {
                for (var j = 0; j < i; j++) {
                  flagArr[j] = j < leftCnt ? 1 : 0;
                }
                flagArr[i] = 0;
                flagArr[i + 1] = 1;
                var aTmp = flagArr.concat();
                flagArrs.push(aTmp);
                if (aTmp.slice(-n).join('').indexOf('0') == -1) {
                  isEnd = true;
                }
                break;
              }
              flagArr[i] == 1 && leftCnt++;
            }
          }
        }
        return flagArrs;
      };
      // 点击sku
      const skuClickHandler = (key1, key2) => {
        console.log(key1, key2, '点击了', processAttribute.value[key1]);
        // 如果不是被禁用的时候才执行
        if (!processAttribute.value[key1].attributeItem[key2].disabled) {
          // 选择和取消选中
          processAttribute.value[key1].attributeItem.map((item, index) => {
            item.activity = index == key2 ? !item.activity : false;
          });
          // 检查当前的sku是否有库存
          skuCheck();
          // 每次点击的时候判断禁用
          getStockPrice();
        }
      };
      // 当前选中的sku的库存机最小单价最大单价
      const stock = ref(0);
      const minPrice = ref(null);
      const maxPrice = ref(null);
      const skuCheck = () => {
        let sku = [];
        processAttribute.value.map((attr) => {
          let name = '';
          attr.attributeItem.map((item) => {
            console.log(item, '111');
            if (item.activity) {
              name = item.name;
            }
          });
          sku.push(name);
        });
        console.log(sku, '选中的的值', sku.join(','));
        stock.value = processSkuMap.value[sku.join(',')].stock;
        minPrice.value = Math.min.apply(Math, processSkuMap.value[sku.join(',')].prices);
        maxPrice.value = Math.max.apply(Math, processSkuMap.value[sku.join(',')].prices);
      };
      // 点击的时候判断库存禁用
      const getStockPrice = () => {
        processAttribute.value.map((attr) => {
          attr.attributeItem.map((item) => {
            item.disabled = itemquantity(item.name) <= 0;
          });
        });
        let count = 0;
        let i = 0;
        processAttribute.value.map((attr, index) => {
          let flag = false;
          attr.attributeItem.map((item) => {
            if (item.activity) {
              flag = true;
            }
          });
          if (!flag) {
            count += 1;
            i = index;
          }
        });
        // 当只有一组规格没选时
        if (count == 1) {
          processAttribute.value[i].attributeItem.map((item) => {
            let sku = [];
            let text = item.name;
            processAttribute.value.map((attr, index) => {
              if (index != i) {
                attr.attributeItem.map((item2) => {
                  if (item2.activity) {
                    sku.push(item2.name);
                  }
                });
              } else {
                sku.push(text);
              }
            });
            if (processSkuMap.value[sku.join(',')].stock == 0) {
              item.disabled = true;
            }
          });
        }
        // 当所有规格都有选时
        if (count == 0) {
          processAttribute.value.map((attr, index) => {
            let i = index;
            processAttribute.value[index].attributeItem.map((item) => {
              if (!item.activity) {
                let sku = [];
                let text = item.name;
                processAttribute.value.map((list, index) => {
                  if (index != i) {
                    list.attributeItem.map((item2) => {
                      if (item2.activity) {
                        sku.push(item2.name);
                      }
                    });
                  } else {
                    sku.push(text);
                  }
                });
                if (processSkuMap.value[sku.join(',')].stock == 0) {
                  item.disabled = true;
                }
              }
            });
          });
        }
      };
      const initSkuData = async () => {
        const { result } = await SkuService.getSkuByIdApi(route.query.id);
        // console.log(data);
        console.log(result.skuAttribute, '111');
        skuAttribute.value = result.skuAttribute;
        skuList.value = result.skuList;
      };
      onMounted(async () => {
        await initSkuData();
        initData();
      });
    </script>
    
    <style lang="scss" scoped>
      .sku {
        .active {
          background: skyblue;
          color: #fff;
          border: none;
        }
        .disabled {
          background: #ddd;
        }
      }
    </style>
    
    

五、具体代码见

  • 1、[后端代码](shuiping.kuang/sku_demo (gitee.com))
  • 2、[前端代码](shuiping.kuang/vue3-sku (gitee.com))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1393417.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

复现PointNet++(语义分割网络):Windows + PyTorch + S3DIS语义分割 + 代码

一、平台 Windows 10 GPU RTX 3090 CUDA 11.1 cudnn 8.9.6 Python 3.9 Torch 1.9.1 cu111 所用的原始代码&#xff1a;https://github.com/yanx27/Pointnet_Pointnet2_pytorch 二、数据 Stanford3dDataset_v1.2_Aligned_Version 三、代码 分享给有需要的人&#xf…

阿里云国外服务器价格表

阿里云国外服务器优惠活动「全球云服务器精选特惠」&#xff0c;国外服务器租用价格24元一个月起&#xff0c;免备案适合搭建网站&#xff0c;部署独立站等业务场景&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云国外服务器优惠活动&#xff1a; 全球云服务器精选特惠…

Dubbo协议解析过程

Dubbo协议解析过程 Dubbo有哪些底层协议 同学们以为Dubbo只有一个RPC协议吗?非也&#xff0c;既然是阿里巴巴出品的开源项目&#xff0c;那自然秉承了“包罗万象”的一贯传统。Dubbo的底层有支持多达9种通信协议&#xff0c;并且他们都有各自的适用场景。我们快速的一扫而过…

阿赵UE学习笔记——10、Blender材质和绘制网格体

阿赵UE学习笔记目录   大家好&#xff0c;我是阿赵。   之前介绍了虚幻引擎的材质和材质实例。这次来介绍一个比较有趣的内置的Blender材质。   在用Unity的时候&#xff0c;我做过一个多通道混合地表贴图的效果&#xff0c;而要做过一个刷顶点颜色混合地表和水面的效果。…

Arm LDM和STM的寻址方式

A32指令集中包含多数据传输指令LDM和STM&#xff0c;也就是单条指令可以传输多个寄存器的值与内存交互&#xff0c;这对于数据块传输以及寄存器的压入栈很有帮助。LDM和STM指令可分别用于实现堆栈的pop和push操作。对于堆栈操作&#xff0c;基寄存器通常是堆栈指针(SP)。 LDM和…

uniapp写微信小程序实现电子签名

写电子签名一定要注意的是一切全部按照手机上的适配来&#xff0c;为啥这么说呢&#xff0c;因为你在微信开发者工具中调试的时候认为是好的&#xff0c;正常的非常nice,当你发布版本的时候你会发现问题出来了。我下边的写法你可以直接用很简单。就是要记住canvas的几个属性和用…

探索数据之美:深入Seaborn的数据可视化艺术与技巧【第26篇—python:Seaborn】

文章目录 1. 引言2. Seaborn基础2.1 安装和环境设置2.2 常用数据可视化函数2.3 设置样式和颜色主题 3. 数据准备与导入3.1 使用Pandas库加载和处理数据3.2 数据清理和缺失值处理 4. Seaborn中的常见图表4.1 折线图和散点图&#xff1a;展示趋势和变量关系4.2 条形图和箱线图&am…

【昕宝爸爸小模块】深入浅出之针对大Excel做文件读取问题

➡️博客首页 https://blog.csdn.net/Java_Yangxiaoyuan 欢迎优秀的你&#x1f44d;点赞、&#x1f5c2;️收藏、加❤️关注哦。 本文章CSDN首发&#xff0c;欢迎转载&#xff0c;要注明出处哦&#xff01; 先感谢优秀的你能认真的看完本文&…

Nestjs 图片下载

一、download直接下载 1、添加下载代码 uploadController.ts import { Controller, Get, Post, Body, Patch, Param, Delete, UseInterceptors, UploadedFile, Res } from nestjs/common; import { UploadService } from ./upload.service; import { CreateUploadDto } from…

STM32WLE5JC介绍

32位 ARM Cotrex-M4 CPU 32MHz晶体振荡器 32 kHz RTC振荡器与校准 20x32位备份寄存器 引导程序支持USART和SPI接口 介绍 STM32WLE5/E4xx远程无线和超低功耗器件嵌入了强大的超低功耗LPWAN兼容无线电解决方案&#xff0c;支持以下调制&#xff1a;LoRa&#xff0c;&#xff08…

C++类与对象【对象的初始化和清理】

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;C从基础到进阶 C类与对象&#x1f384;1 对象的初始化和清理&#x1f955;1.1 构造函数和析构函数&#x1f955;1.2 构造函数的分类及调用&#x1f955;1.3 拷贝构造函数调用时机&#x1f…

C# 读取ini文件示例

一般使用一个相关win32 api的封装类&#xff1b;我用的如下&#xff1b; using System; using System.Runtime.InteropServices; using System.Text;namespace DotNet.Utilities {/// <summary>/// INI文件读写类。/// </summary>public class INIFile{public str…

大数据毕业设计:基于python美食推荐系统+爬虫+Echarts可视化+协同过滤推荐算法+Django框架(源码)✅

毕业设计&#xff1a;2023-2024年计算机专业毕业设计选题汇总&#xff08;建议收藏&#xff09; 毕业设计&#xff1a;2023-2024年最新最全计算机专业毕设选题推荐汇总 &#x1f345;感兴趣的可以先收藏起来&#xff0c;点赞、关注不迷路&#xff0c;大家在毕设选题&#xff…

4D毫米波雷达——原理、对比、优势、行业现状

前言 4D 毫米波雷达是传统毫米波雷达的升级版&#xff0c;4D指的是速度、距离、水平角度、垂直高度四个维度。 相比传统 3D 毫米波雷达&#xff0c;4D 毫米波雷达增加了“高度”的探测&#xff0c;将第四个维度整合到传统毫米波雷达中。 4D毫米波雷达被视为未来车载雷达的一…

【计算机网络】【Python】【练习题】【新加坡南洋理工大学】【Computer Control Network】

一、题目描述 该题目描述一个网络中数据包交换&#xff08;Packet Switching&#xff09;的例子。题目如下&#xff1a; 二、问题解答&#xff08;使用Python&#xff09; Q1&#xff1a;如何求出0.0004这个值&#xff1f; &#xff08;1&#xff09;、公式推导过程&#xf…

072:vue+mapbox 点击某图层feature,高亮这部分

第072个 点击查看专栏目录 本示例是介绍如何在vue+mapbox中点击某图层feature,高亮这部分。思路是通过点击,获取点击部分的feature信息,生成一个新的source和layer,如果这个图层不为空,则清除之,相当于点击了别的地方,原有的高亮会删除掉,在别的地方高亮。 直接复制下…

ubuntu系统(10):使用samba共享linux主机中文件

目录 一、samba安装步骤 1、Linux主机端操作 &#xff08;1&#xff09;安装sabma &#xff08;2&#xff09;修改samba配置文件 &#xff08;3&#xff09;为user_name用户设置samba访问的密码 &#xff08;4&#xff09;重启samba服务 2、Windows端 二、使用 1、代码…

google网站流量怎么获取?

流量是一个综合性的指标&#xff0c;可以说做网站就是为了相关流量&#xff0c;一个网站流量都没有&#xff0c;那其实就跟摆饰品没什么区别 而想从谷歌这个搜索引擎里获取流量&#xff0c;一般都分为两种方式&#xff0c;一种是网站seo&#xff0c;另一种自然就是投广告&#…

《C++ Primer》第15章 面向对象程序设计(一)

参考资料&#xff1a; 《C Primer》第5版《C Primer 习题集》第5版 15.1 OOP&#xff1a;概述&#xff08;P526&#xff09; **面向对象程序设计&#xff08;object-oriented programming&#xff09;**的核心思想是数据抽象、继承和动态绑定。 继承 通过继承&#xff08;…

逆变器之变压器基础知识

1 基础知识 我们的磁芯要工作的话&#xff0c;必须要有磁场&#xff0c;有磁场就就会有磁力线。 磁场我们是看不着摸不见的&#xff0c;为了好的描述磁场&#xff0c;我们就用磁力线来表示&#xff0c; 磁力线是有方向的&#xff0c;在一个磁铁的内部磁力线是从 S指向N的&…