前端商城商品sku設(shè)計(jì)和實(shí)現(xiàn)

后臺模型


需求與實(shí)現(xiàn)(模擬實(shí)現(xiàn))

sku
tip:在后臺中添加的規(guī)格參數(shù),最終會在請求接口后返回規(guī)格參數(shù),本次實(shí)例有3個規(guī)格,每個規(guī)格分別有3個選項(xiàng),其中有一個sku組合是默認(rèn)項(xiàng),可隨意在后臺設(shè)置,還有就是禁用項(xiàng),禁用項(xiàng)主要的目的就是當(dāng)前規(guī)格可能緊急沒有庫存或其他原因下架的商品,用戶是無法選擇禁用的sku組合的。

后臺返回的規(guī)格列表參數(shù)數(shù)據(jù)(模擬數(shù)據(jù)):

//attributeList 為所有規(guī)格list   skuName為規(guī)格名,skuValue為規(guī)格值
//defaultSpec:默認(rèn)選中的規(guī)格
data:{
    attributeList: '[{"skuName":"顏色","skuValue":["紅色","白色","綠色"]},{"skuName":"大小","skuValue":["大","中","小"]},{"skuName":"規(guī)格","skuValue":["5L","10L","15L"]}]',
    defaultSpec: '{"顏色":"紅色","大小":"大","規(guī)格":"5L"}',
}

每次點(diǎn)擊規(guī)格項(xiàng)時調(diào)用接口,返回禁用的sku組合(模擬數(shù)據(jù)):

//禁用組合字段反參為數(shù)組,每一項(xiàng)是json格式的鍵值對
data:{
  cdMallSkusList: ['{"顏色":"紅色","大小":"小","規(guī)格":"5L"}', '{"顏色":"白色","大小":"中","規(guī)格":"10L"}', '{"顏色":"綠色","大小":"中","規(guī)格":"5L"}', '{"顏色":"綠色","大小":"小","規(guī)格":"10L"}']
}

基本實(shí)現(xiàn):

sku
html:
<view class="sku">
//外層遍歷規(guī)格名
        <view class="content1"  v-for="(item, index) in skuList" :key="item[id]">
            <text class="t1">{{ item.name }}</text>
            <view class="list flex-wrap">
              //內(nèi)層遍歷規(guī)格項(xiàng)名
                <text
                    v-for="(items, indexs) in item.list"
                    :key="indexs"
                    @tap="formatXY(index,indexs) ? '' : changeTab(index, indexs)"
                    :class="{ selectColor: indexs == item.sidx, disColor: formatXY(index,indexs) }"
                    >
                    {{ items }}
                </text>
            </view>
        </view>
</view>
js(核心):
data(){
  return {
       skuList:[],     //sku列表
       disSkus:[],    //禁用的sku組合
       disBtn:[]       //禁用組合的下標(biāo)集合
    }
},

//當(dāng)前使用的是uniapp的生命周期,vue原生項(xiàng)目可以做適當(dāng)修改。邏輯都是一樣的
onLoad(){
      this.getMallDetail()
},

//init初始數(shù)據(jù)
getMallDetail(){
        let selectSkus = [];               //得到默認(rèn)選中項(xiàng),并處理成 ['紅色', '大', '5L']格式
        for (let key in JSON.parse(res.data.defaultSpec)) {
            selectSkus.push(JSON.parse(res.data.defaultSpec)[key]);
        }
        //res.data.data.attributeList為調(diào)用后端接口返回的字段,字段內(nèi)容見上方模擬數(shù)據(jù)
         this.skuList = JSON.parse(res.data.attributeList).map((u, index) => {
         let sidx = u.skuValue.findIndex(item => item == selectSkus[index])   //計(jì)算出默認(rèn)規(guī)格的下標(biāo),做選中效果使用
        //處理成對象型數(shù)組格式,更好處理和數(shù)據(jù)渲染
                return {
                    name: u.skuName,    //規(guī)格名
                    id: index,          //每一項(xiàng)的key
                    list: u.skuValue,   //數(shù)組,規(guī)格項(xiàng)的名稱的集合
                    sidx:sidx           //主要為每一個規(guī)格做一個標(biāo)記,為實(shí)現(xiàn)后面的點(diǎn)擊變色并且不互相沖突的記錄值
                };
        });
        this.getSpecList(selectSkus)             //初始執(zhí)行一次處理禁用關(guān)系函數(shù)
 },

//點(diǎn)擊切換選項(xiàng)方法:
changeTab(index, indexs) {
            this.skuList[index].sidx = indexs;
        //得到點(diǎn)擊選擇的規(guī)格組合
            const selectInfo = this.skuList.reduce((prev, cur) => {
                if (prev) {
                    return prev + ',' + cur['list'][cur.sidx];
                } else {
                    return cur['list'][cur.sidx];
                }
            }, '');
        this.getSpecList(selectInfo.split(','));
},

// 獲取禁用的sku組合和點(diǎn)擊后得到的選中的組合
getSpecList(selectSkus) {
          //調(diào)用接口獲取禁用的sku組合
            this.api.getSpecList().then(res => {
                    this.disSkus = res.data.data.cdMallSkusList.map(a => {
                        let dis = [];
                        for (let key in JSON.parse(a)) {
                            dis.push(JSON.parse(a)[key]);
                        }
                        return dis;
                    });
              //調(diào)用下方格式化規(guī)格方法
                this.formatSkuValue(selectSkus);
            });
},

// 格式化規(guī)格,找出禁選坐標(biāo)
formatSkuValue(selectSkus) {
            this.disBtn = [];
            let noSelectSkusAll = []
            let skuGroupAll = []

            // selectSkus 默認(rèn)選中的規(guī)格組合
            // skuList 每個規(guī)格的各項(xiàng),所有的規(guī)格
            // noSelectSkus 每個規(guī)格沒有被選中的各項(xiàng)(二維數(shù)組,每一個數(shù)組對應(yīng)每一個規(guī)格)
            let noSelectSkus = this.skuList.map((a, index) => {
                return a.list
                    .map(b => {
                        if (b != selectSkus[index]) {
                            return b;
                        }
                    })
                    .filter(Boolean);
            });
            
            // noSelectSkusAll 二維數(shù)組,過濾出排除當(dāng)前規(guī)格項(xiàng)的其他已選中的規(guī)格項(xiàng).例如:
            // ['紅','大','5L']為選中項(xiàng),那么過濾后noSelectSkusAll的值就是:[['大','5L'],['紅','5L'],['紅','大']]
            
            for(let i = 0;i<selectSkus.length;i++){
                let arr = selectSkus.filter(item => {
                    return item != selectSkus[i];
                });
                noSelectSkusAll.push(arr)
            }
            
            // skuGroupAll 三維數(shù)組或二維數(shù)組,取決于規(guī)格有多少,大于2個規(guī)格時就是三維數(shù)組,
            // 組合出每一個規(guī)格選中的項(xiàng)和上述 noSelectSkusAll得到的值進(jìn)行組合.例如:
            // [['綠','紫'],['中','小'],['10L','15L']]是noSelectSkusAll的值,['紅','大','5L']是選中的組合項(xiàng),那么組合就是[[['綠','大','5L'],['紫','大','5L']],[['紅','小','5L'],['紅','中','5L']],[['紅','大','10L'],['紅','大','15L']]]
            
            for(let i = 0;i<noSelectSkus.length;i++){
                let arr = []
                noSelectSkus[i].forEach(item => {
                    noSelectSkusAll[i].splice(i, 0, item);
                    arr.push(JSON.parse(JSON.stringify(noSelectSkusAll[i])));
                    noSelectSkusAll[i].splice(i, 1);
                });
                skuGroupAll.push(arr)
            }
            
            //disSkus 是提前設(shè)定好的禁選的sku組合
            //把skuGroupAll中的組合和禁選的組合進(jìn)行比對,完全相同的就是需要禁選的,
            //通過遍歷skuList所有的規(guī)格的每一項(xiàng),比如第0下標(biāo)的規(guī)格和h[0]比對,一一對比,等到相同項(xiàng)就是最終的禁選項(xiàng),如果相同就可以得出0下標(biāo)的第幾項(xiàng)了,比如第2項(xiàng)是禁選的那么坐標(biāo)就是[0,1]
            //最終把有禁用項(xiàng)的下標(biāo)存儲到數(shù)組中,有多少個規(guī)格就存多少長度的數(shù)組,就可以準(zhǔn)確的知道每一個規(guī)格哪一項(xiàng)是禁選的準(zhǔn)確坐標(biāo)
            //例如:[[1],[0,1],[2]]為最終得到的禁選的下標(biāo),那么第1個規(guī)格的第2個禁選,第3個規(guī)格的第3個禁選,....
            for(let j = 0;j<skuGroupAll.length;j++){
                let arr = []
                skuGroupAll[j].map((v, i) => {
                    this.disSkus.map((h, s) => {
                        if (v.toString() == h.toString()) {
                            console.log('禁用');
                            this.skuList[j].list.map((a, index) => {
                                if (a == h[j]) {
                                    arr.push(index);
                                }
                            });
                        }
                    });
                });
                this.disBtn.push(arr)
            }
        console.log('disBtn',this.disBtn);
},

// index:每個規(guī)格的下標(biāo),相當(dāng)于y軸
// indexs:每個規(guī)格里每項(xiàng)的下標(biāo),相當(dāng)于x軸
// 獲取禁用項(xiàng)具體坐標(biāo)
formatXY(index,indexs){
        let disStr = ''
        this.disBtn.map((a, index1) => {
            // disBtn:保存的是最終需要被禁選的下標(biāo),二維數(shù)組組成,每個數(shù)組的小標(biāo)為y軸即規(guī)格,數(shù)組中的數(shù)字就是x軸即規(guī)格的每項(xiàng),例如:[[2],[0]] 就是(0,2)和(1,0)被禁選
            // 當(dāng)y軸的規(guī)格名和x軸的規(guī)格項(xiàng)名都相等時,就找到了需要禁選的那一項(xiàng)
                a.map(b => {
                    disStr = (index == index1 && indexs == b) || disStr
                })
            });
        console.log('disStr',disStr);
        // disStr返回true則當(dāng)前項(xiàng)是禁選的
        return disStr
},
css(scss):
//創(chuàng)建一個scss 的minix用作公用
@mixin str($size,$weight,$color) {
  font-size: $size;
  font-family: PingFang SC;
  font-weight: $weight;
  color:$color;
  opacity: 1;
}

.sku {
    .content1,
    .content2 {
      .t1 {
          @include str(28px, bold, #333);
      }

      .flex_warp {
          display: flex;
          flex-wrap: wrap;
      }

      .list {
          display: flex;
          flex-wrap: wrap;

        p {
          display: inline-block;
          width: 218px;
          height: 60px;
          border-radius: 8px;
          background: rgba(237, 237, 237, 1);
          text-align: center;
          line-height: 60px;
          cursor: pointer;
          @include str(28px, 400, #444);
        }

        p:nth-child(n + 4) {
          margin-top: 20px;
        }

        p:nth-child(n + 2) {
          margin-left: 10px;
        }
      }
    }
    //選擇時的按鈕顏色變化
    .selectColor {
      background: #fc6d18 !important;
      color: #fff !important;
    }

    //禁用項(xiàng)的樣式
    .disColor {
      text-decoration: line-through !important;
      background: rgba(237, 237, 237, 1) !important;
      color: #ccc !important;
      opacity: 0.1;
    }
  }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 文章目錄一、編程規(guī)約(一) 命名風(fēng)格(二) 常量定義(三) 代碼格式(四) OOP 規(guī)約(五) 日期時間(六) 集...
    wqjcarnation閱讀 232評論 0 0
  • 經(jīng)銷商走向強(qiáng)則更強(qiáng),弱則更弱的二級化! 疫情后,經(jīng)銷商在重新思考企業(yè)未來發(fā)展的定位,小編也和全國50多個城市的...
    d57c6d75c125閱讀 94評論 0 1
  • 目標(biāo):選擇的圖片文件,要給到 img 標(biāo)簽上做純前端的預(yù)覽 img 標(biāo)簽的 src 值 * 只能是圖片的 “鏈接地...
    小丸子_7043閱讀 366評論 0 0
  • 頭條 Roblox 正在整合生成式人工智能[https://archive.ph/pCSBd] 熱門在線游戲 Ro...
    數(shù)科每日閱讀 286評論 0 1
  • python找出某個文件夾下某個后綴的文件 該函數(shù)接受一個文件夾路徑和一個后綴名作為參數(shù),并返回一個包含所有以該后...
    人工zz研究員閱讀 78評論 0 1

友情鏈接更多精彩內(nèi)容