zoukankan      html  css  js  c++  java
  • 类似淘宝,京东sku选择,电商商品选择

    <template>
      <div class="wrap-sku">
        <div class="product-box">
          <div class="product-content">
            <div class="product-delcom" v-for="(productItem,indexs) in list" :key="indexs">
              <p>{{ productItem.name }}</p>
              <ul class="product-footerlist">
                <!--
                    currentChoose[ProductItem.name] === oItem.name // 选中项对象中Key是否存在规格属性 存在就选上red
                    否则oItem.hasStock代表是否可以被选择,不能被选择就直接置灰色
                -->
                <li v-for="(oItem,index) in productItem.items"
                    :key="index"
                    @click="clickSku(productItem.name, oItem)"
                    :class="[currentChoose[productItem.name] === oItem.name ? 'activeStock' : oItem.hasStock ? 'isStock' : 'noStock']">
                  {{ oItem.name }}
                </li>
              </ul>
            </div>
          </div>
          <div style="font-size: 20px;margin-top: 100px">选中的属性: 性别:{{ currentChoose.sex }}, 颜色:{{ currentChoose.color }}, 大小:{{ currentChoose.size }}</div>
        </div>
      </div>
    </template>
    
    <script>
      // 需要被找到的规格值数组 (创造子集数组是非常重要的一步事情)
      const getKey = (currentChoose, groupName, item) => {
        const obj = { ...currentChoose }
        // 这一步也是非常重要的
        // 很多人不懂的事,我点击男 groupName是sex 然后 item.name值是男
        // obj[groupName]是对象里面的sex值等于 刚刚传进来的 (item.name值是男)
        // 那么obj打出来为啥还有sex值为女的呢。这里按道理都是男啊,为啥呢
        // 理由如下: 当我点击规格事件的 currentChoose[对应的key值已经被我存起来了](具体请看点击事件)
        obj[groupName] = item.name
        // 建造子集数组(重要一步)它将是拿去去交集的一个点
        const arr = []
        // 找到存再的规格值,进行push新的子集中
        for (const i in obj) if (obj[i] !== '') arr.push(obj[i])
        return arr
      }
      // 一个数组是否为sku数组的子集(parent 单个sku的数组)
      const isChildrenArr = (parent, child) => {
        // every()方法是js中的迭代方法,用于检测数组中的元素是否满足指定条件。
        //     1、依次执行数组元素,如果一个元素不满足条件就返回false,不会继续执行后面的元素判断;所有数组元素都满足条件则返回true。
        //     2、不会改变原数组。
        // includes() 方法用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false。
        /** ********** 思路:非常重要也是最关键的一步*********/
        // 1. 找得到的情况:
        // 如: 当我开始没选中 child(建造子集数组的值)为 [男]
        // 然后遍历V等于男,当我 parent为['男', '白色', '100'],那么我的男在我里面存在,就返回为true
        // 2. 找不到的情况:
        // 如:  当我开始选中白色,那么我child(建造子集数组的值)是[男,白色]
        // 然后遍历V等于男,当我 parent为['男', '蓝色', '100']
        // 首先我第一次遍历男 确实在我数组里面存在,然后再找到白色进去遍历,那么此时白色在我parent数组里面不存在。
        // 那么就直接返回为false, 因为上面有说到 every()方法 如果一个元素不满足条件就返回false,不会继续执行后面的元素判断
        return child.every(v => {
          return parent.includes(v)
        })
      }
      // 获取规格值属性数组
      const getCheckedStockList = (list, stockList, currentChoose) => list.map((group) => {
        // 遍历规格值
        const items = group.items.map((item) => {
          // some()方法用于检测数组中的元素是否满足指定条件(函数提供)。
          // some()方法会依次执行数组的每个元素:
          // 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
          // 如果没有满足条件的元素,则返回false。
          // 注意some() 不会对空数组进行检测, 注意some() 不会改变原始数组。
          // 检测数组中的元素是否满足指定条件
          const hasStock = stockList.some(stock => {
            // 找出sku数组中符合条件的元素(子集节点找得到并且库存数量大于0)如果你的条件不是库存大于0 而是其它,请你自己改掉 后面的条件
            return isChildrenArr(stock.key, getKey(currentChoose, group.name, item)) && stock.stock > 0
          })
          return { ...item, hasStock }
        })
        return { ...group, items }
      })
    
      // 无限级SKU选择(把方法拷贝,不是vue项目也能用哦,思想一样)
      export default {
        data () {
          return {
            // sku列表
            stockList: [
              { key: ['', '白色', '100'], stock: 2 },
              { key: ['', '白色', '200'], stock: 0 },
              { key: ['', '红色', '100'], stock: 2 },
              { key: ['', '红色', '300'], stock: 1 },
              { key: ['', '红色', '200'], stock: 2 },
              { key: ['', '蓝色', '200'], stock: 2 },
              { key: ['', '白色', '200'], stock: 2 },
              { key: ['', '白色', '100'], stock: 2 }
            ],
            // 规格值数组(无限级SKU选择)
            list: [
              {
                name: 'sex',
                items: [
                  { name: '' },
                  { name: '' }
                ]
              },
              {
                name: 'color',
                items: [
                  { name: '白色' },
                  { name: '红色' },
                  { name: '蓝色' }
                ]
              },
              {
                name: 'size',
                items: [
                  { name: '100' },
                  { name: '200' },
                  { name: '300' }
                ]
              }
            ],
            // 选中对象
            currentChoose: {
              color: '',
              size: '',
              sex: ''
            }
          }
        },
        created () {
          // 初始化规格 sku列表查询对应的路径
          this.list = getCheckedStockList(this.list, this.stockList, this.currentChoose)
        },
        methods: {
          // 规格属性点击事件
          clickSku (groupName, item) {
            // 如果我当前的规格属性不能被选选中,那么直接返回
            if (!item.hasStock) return
            // 取出对应的数据
            const { currentChoose, stockList, list } = { stockList: this.stockList, list: this.list, currentChoose: this.currentChoose }
            // 如果我当前选中的规格值存在,在我当前选中项数组里面,那么就把选中项去掉,否则就把当前选中规格的key值赋值为当前选中的value值
            const nextChoose = currentChoose[groupName] === item.name
              ? { ...currentChoose, [groupName]: '' }
              : { ...currentChoose, [groupName]: item.name }
            // 每次点击都需要去sku列表重新查询对应的路径
            const checkedStockList = getCheckedStockList(list, stockList, nextChoose)
            // 赋值选中对象
            this.currentChoose = nextChoose
            // 赋值规格
            this.list = checkedStockList
          }
        }
      }
    </script>
    
    <style lang="less">
      .wrap-sku {
        .activeStock {background-color: red;}
        .isStock {background-color: #fff;}
        .noStock {background-color: #eee;cursor: default !important;}
        .product-box {
           1200px;
          display: block;
          margin: 0 auto;
        }
        .product-delcom {
          color: #323232;
          font-size: 26px;
          padding: 30px 0;
        }
        .product-footerlist li {
          border: 1px solid #606060;
          border-radius: 5px;
          color: #606060;
          text-align: center;
          padding: 10px 30px;
          list-style: none;
          float: left;
          margin-right: 20px;
          cursor: pointer;
        }
      }
    </style>

    效果图: 

  • 相关阅读:
    A Year Of Books
    Spring Boot 之 RESRful API 权限控制
    Git回滚远程版本
    初探设计:Java接口和抽象类何时用?怎么用?
    深入浅出: Java回调机制(异步)
    深入浅出: 大小端模式
    Java IO 之 FileInputStream & FileOutputStream源码分析
    Java IO 之 OutputStream源码
    软件测试--安装软件
    Mybatis 中$与#的区别
  • 原文地址:https://www.cnblogs.com/plBlog/p/12384361.html
Copyright © 2011-2022 走看看