实现效果
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 规格选择,三种分类组成一种规格(一般):
- 实现选中与取消选中
- 同一分类不能个被选中,例如:颜色不能同时选中为红色之后有加选白色
- 同一分类点选未选中的样式,需要将当前样式取消,例如:当前红色为选中状态,点选白色后,选中白色取消红色
- 规格可选后,我们对选中样式进行约束(最难)
- 当未选中任何样式时,所有样式都是可选状态
- 当选中样式仅有一个时,要开放同类样式,同时做逻辑判断哪些样式不可选,例如:我仅选了红色,那么颜色分类的其他选项对我来说是可选状态,而其他分类就不一定是可选状态
- 当选中样式已满时,其他样式都不可选
- 其他状态,直接做逻辑判断是否可选
- 到这里已经实现了对 sku 的约束,最后是 sku 详情的绑定
- 当样式选满,自动切换到与当前 sku 匹配的详情(简简单单)
- sku 规格选择,三种分类组成一种规格(一般):
-
分析选中与取消选中
- 第一个想法就是通过 active 与 disable 两个样式来控制可选与不可选,那么问题来了,这么动态添加这两个样式呢???
- 理所当然是通过点击事件,那么我们点击就给他上 active???,那点了同类其他样式,我要怎么取消 active ???
- 最终,我使用数组来管理选中状态,分析一下吧,根据上述例子,最多能存在三个分类同时被选中,那么我们的selectedList 列表中最多就存在3个选中状态,
- 当我们点选一个状态时,先判断这个状态是否已经被选中,若选中则将其取消选中
- 未选中,有两种结果
- 我点的样式已选中,需要替换原选中样式
- 该分类样式未被选中,直接选中
- 根据上限分析,我们来编写代码
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个状态被选中,该分类的样式均可选,其他分类需要逻辑判断
- 其他状态,直接逻辑判断
- 分析完毕上代码吧
<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人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦