为了账号安全,请及时绑定邮箱和手机立即绑定

实现SKU功能

标签:
小程序

实现效果

图片描述

TODO

  • 获取服务端数据,分析数据结构
  • 构建 sku 组件,搭建基础框架
  • 编写页面交互逻辑代码

分析页面入口传递何种数据

  • 从主页面跳转到详情页面的 url :v1/spu/id/{{id}/detail
  • 所以跳转详情页面需求传递当前 spu 的 id,跳转到详情页面后向服务器获取当前 spu 数据填充到页面
  • 从服务器获取到的数据格式如下:
    图片描述

分析数据结构

  • 总体结构猜测
    图片描述

  • 重点结构(sku_list)

  • sku_list 是一个数组类型的数据,里面包含的是一个个对象,每个对象都是一个 sku
    图片描述

搭建容器框架

  • 组件是数据的载体,既然我们了解了数据结构那我们就根据数据结构来搭建框架
  • 我将 sku 作为一个组件封装起来,组件需要从外部传递 sku_list 以及 默认 sku_id 给组件调用
<m-sku-list
    list="{{sku_list}}"
    defaultSkuId="{{default_sku_id}}"></m-sku-list>
  • 我们已经有了数据,分析一下页面结构

  • 如下图:我们需要一个容器承接 sku详情数据,另个容器承接 sku 规格数据
    图片描述

  • 详情数据好说,都是现成直接往里填充就好,但是规格就没有现成的可以使用了,因此我们需通过分析规格数据格式抽象一个 用于一定规格的对象来帮助我们搭建容器

// 抽象规格数据
// 如上图,一共有三种分类(颜色、款式、尺码),三种分类各自组合成一个sku,这样多个数据的情况下适用数组管理
// 每种规格都被视作一个对象,对象包含 标题、分类id、组合合成的规格
component({
	data:{
		specsList:[] // 规格列表用于搭建框架
	}
})
  • 为了得的上述的数据结构,我们需要对外部传递给我们的数据进行二次封装
component({
	...,
	observers:{
		"list":function(list){
			// 防空
			if (list.length === 0) return
			
			// 搭建 spu 规格模板
			const specList = list[0].specs.map(item => {
			  return {
			      keyId:item.key_id,
			      key:item.key,
			      category:[]
			    }
			})
			
			// 填充 sku 规格模板
			 specList.map(m => {
			   // 获取各个规格
			   list.map(i => {
			     // 获取规则中一种分类对应的所有样式,例如:颜色:红、白、蓝
			     const category  = i.specs.find(spec => {
			       return m.keyId === spec['key_id']
			     })

			     // 防止重复保存相同的样式
			     if(m.category.length === 0) {
			       m.category.push(category)
			     } else {
			       const b = m.category.find(c => c["value_id"] === category['value_id'])
			       if(!b) m.category.push(category)
			     }
			   })
		    })
		}
	}
})
  • 好了,完成到这里我们就可以轻松有空的搭建好容器框架了,我们的 todo-list 也完成 70%,剩下的 30% 也是最难的了…

编写页面交互逻辑

  • 交互逻辑有很多,在显示之前我先将其一一列出来,以防后思维混乱

    • sku 规格选择,三种分类组成一种规格(一般):
      1. 实现选中与取消选中
      2. 同一分类不能个被选中,例如:颜色不能同时选中为红色之后有加选白色
      3. 同一分类点选未选中的样式,需要将当前样式取消,例如:当前红色为选中状态,点选白色后,选中白色取消红色
    • 规格可选后,我们对选中样式进行约束(最难)
      1. 当未选中任何样式时,所有样式都是可选状态
      2. 当选中样式仅有一个时,要开放同类样式,同时做逻辑判断哪些样式不可选,例如:我仅选了红色,那么颜色分类的其他选项对我来说是可选状态,而其他分类就不一定是可选状态
      3. 当选中样式已满时,其他样式都不可选
      4. 其他状态,直接做逻辑判断是否可选
    • 到这里已经实现了对 sku 的约束,最后是 sku 详情的绑定
      1. 当样式选满,自动切换到与当前 sku 匹配的详情(简简单单)
  • 分析选中与取消选中

    • 第一个想法就是通过 active 与 disable 两个样式来控制可选与不可选,那么问题来了,这么动态添加这两个样式呢???
    • 理所当然是通过点击事件,那么我们点击就给他上 active???,那点了同类其他样式,我要怎么取消 active ???
    • 最终,我使用数组来管理选中状态,分析一下吧,根据上述例子,最多能存在三个分类同时被选中,那么我们的selectedList 列表中最多就存在3个选中状态,
      • 当我们点选一个状态时,先判断这个状态是否已经被选中,若选中则将其取消选中
      • 未选中,有两种结果
        1. 我点的样式已选中,需要替换原选中样式
        2. 该分类样式未被选中,直接选中
    • 根据上限分析,我们来编写代码
component({
	data:{
		...,
		// 根据上述分析,我们需要一个数组来管理选中状态
		selectedList:[]
	},
	...
	methods:{
		// 点击触发切换样式
		selectedSku(e) {
			// 切换样式,我们也需要知道,我们当前点击的是哪个样式(category),disable是禁选
			const {category,disable} = e.currentTarget.dataset
			if (disable) return

			// 获取当前选中样式的列表
			const arr = this.data.currentSelect

			// 判断是否点选已选中的样式
			// 获取当前选中的样式分类
			const index = arr.findIndex((item,index) => {
				return item['key_id'] === sku['key_id']
			})

			// 简单逻辑判断
			if (index === -1) {
				// 列表中没有该分类
				arr.push(sku)
			}else {
				// 列表中已有该分类,那就是取消选中或,更换选中
				if (sku['value_id'] === arr[index]['value_id']) {
				  arr.splice(index,1)
				}else {
				  arr.splice(index,1,sku)
				}
			}

			this.setData({
			  currentSelect:arr
			})
	   },
	}
})
  • 分析样式约束
    • 这真令人头痛啊!!!,累了毁灭吧!!!
    • 回到正题,实现样式约束,核心是将当前选中的样式与 sku 组合的比较,当前选中的样式好说,我们完成上一步就已经获得了,sku组合就相对麻烦,因为他藏的很深啊,又是数组又是对象,对象里面还有数组,数组里面还有对象,你膈这套娃呢???
    • 话不多说,我们先重新组织一下 sku 的格式,以便我们调用
      图片描述
    • 解释一下,我已 key_id 为键 ,key 为其值,value 同理,顺便保存了 该 sku 的 id,以便在 sku 详情中获取原列表数据
    • 好了准备工作完成,那就开始分析约束条件(从简单的开始吧)
      1. 当未选中任何状态,那我们就不应该约束任何样式
      2. 当已选中最大可选状态,那其他样式均被禁用
      3. 当有且只有1个状态被选中,该分类的样式均可选,其他分类需要逻辑判断
      4. 其他状态,直接逻辑判断
    • 分析完毕上代码吧
<view
    class="
    category-item <!-- 默认样式 -->
    {{m.selected(item['key_id'],item['value_id'],currentSelect) ? 'active' : ''}}  <!-- 选中样式 -->
    {{m.isDisable(currentSelect,item,allSku,maxSelectSize) ? 'disable':''}}" <!-- 禁用样式 -->
    data-category="{{item}}" <!-- 当前 item 携带的样式信息 -->
    data-disable="{{m.isDisable(currentSelect,item,allSku,maxSelectSize)}}" <!-- 判断 item 是否已禁用 -->
    bind:tap="selectedSku"> <!-- 点击触发选中 -->
    <view class="category-item-inner">{{item.value}}</view>
</view>

<wxs module="m">
	...
	var isDisable = function (currentSelect,item,allSku,maxSize) {
	   var flag = true

	   // 当前选项长度是 0
	   if (currentSelect.length === 0) {
	       flag = false
	   }
	   else if (currentSelect.length === maxSize) {
	       // 当前选项长度是最大选项值时,item 若非选中则禁用
	       currentSelect.forEach(function (c){
	           // console.log("wx",c['key_id'] === item['key_id'])
	           if (c['key_id'] === item['key_id']){
	               if (c['value_id'] === item['value_id']) {
	                   flag = false
	               }
	           }
	       })
	   }
	   else if (currentSelect.length === 1) {
	       // 当前选项长度是只有 1 个时
	       // 相同类型可选
	       if (currentSelect[0]['key_id'] === item['key_id']){
	           flag = false
	           return
	       }
	       // 非相同类型需要判断
	       allSku.filter(function (s) {
	           var cv = currentSelect[0]['value_id'].toString()
	           // console.log(s[cv] , currentSelect[0]['value'])
	           return s[cv] === currentSelect[0]['value']
	       }).forEach(function (s) {
	           var v = item['value_id'].toString()
	           if (s[v] === item['value']) {
	               flag = false
	           }
	       })

	   }
	   else {
	       // 当前选项长度大于 1 小于 最大选项值时
	       // 先从所有 sku 中过滤出符合当前选中的 sku
	       var filterSku = []
	       currentSelect.forEach(function (cs) {
	           filterSku = allSku.filter(function (s) {
	               var cv = cs['value_id'].toString()
	               // console.log(s[cv] , currentSelect[0]['value'])
	               return s[cv] === cs['value']
	           })
	       })
	       // 当 item 与 选中列表中的样式组合 能在 sku 列表中找到
	       filterSku.forEach(function (fs) {
	           var v = item['value_id'].toString()
	           if (fs[v] === item['value']) {
	               flag = false
	           }
	       })
	   }
	   return flag
	}

module.exports = {
	...,
	isDisable:isDisable,
}
</wxs>
  • sku 详情的绑定
    • 能走到这里,这 sku 详情绑定就是洒洒水啦
    • 上面定义一个 currentSelect 管理当前选中状态,当选项满最大可选值时,触发重新设置当前状态
component({
...,
observers: {
	...,
	"currentSelect":function (currentSelect) {
	     if (currentSelect.length === this.data.maxSelectSize) {
			
            // 获取数据
	       const {allSku} = this.data
	       const {list} = this.properties
        
	       // 筛选sku
	       let filter = allSku
	       currentSelect.map(cs =>{
	         filter = filter.filter(f => {
	           return f[cs['value_id'].toString()] === cs['value']
	         })
	       })

	       // 未筛选中,返回
	       if (filter.length === 0) return

	       // 筛选中
	       const sku = list.find(item => {
	         return item.id === filter[0].id
	       })

	       this.setData({
	         currentSku:sku
	       })
	     }
	   }
	}
})
点击查看更多内容
3人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消