封装sku组件

2024-02-10 08:04
文章标签 封装 组件 sku

本文主要是介绍封装sku组件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 准备模板渲染规格数据

使用Vite快速创建一个Vue项目,在项目中添加请求插件axios,然后新增一个SKU组件,在根组件中把它渲染出来,下面是规格内容的基础模板

image.png

<script setup>
import { onMounted, ref } from 'vue'
import axios from 'axios'
// 商品数据
const goods = ref({})
const getGoods = async () => {// 1135076  初始化就有无库存的规格// 1369155859933827074 更新之后有无库存项(蓝色-20cm-中国)const res = await axios.get('http://pcapi-xiaotuxian-front-devtest.itheima.net/goods?id=1369155859933827074')goods.value = res.data.result
}
onMounted(() => getGoods())</script><template><div class="goods-sku"><dl v-for="item in goods.specs" :key="item.id"><dt>{{ item.name }}</dt><dd><template v-for="val in item.values" :key="val.name"><!-- 图片类型规格 --><img v-if="val.picture" :src="val.picture" :title="val.name"><!-- 文字类型规格 --><span v-else>{{ val.name }}</span></template></dd></dl></div>
</template><style scoped lang="scss">
@mixin sku-state-mixin {border: 1px solid #e4e4e4;margin-right: 10px;cursor: pointer;&.selected {border-color: #27ba9b;}&.disabled {opacity: 0.6;border-style: dashed;cursor: not-allowed;}
}.goods-sku {padding-left: 10px;padding-top: 20px;dl {display: flex;padding-bottom: 20px;align-items: center;dt {width: 50px;color: #999;}dd {flex: 1;color: #666;>img {width: 50px;height: 50px;margin-bottom: 4px;@include sku-state-mixin;}>span {display: inline-block;height: 30px;line-height: 28px;padding: 0 20px;margin-bottom: 4px;@include sku-state-mixin;}}}
}
</style>

2. 选中和取消选中实现

基本思路:

  1. 每一个规格按钮都拥有自己的选中状态数据-selected,true为选中,false为取消选中
  2. 配合动态class,把选中状态selected作为判断条件,true让active类名显示,false让active类名不显示
  3. 点击的是未选中,把同一个规格的其他取消选中,当前点击项选中;点击的是已选中,直接取消
<script setup>
// 省略代码// 选中和取消选中实现
const changeSku = (item, val) => {// 点击的是未选中,把同一个规格的其他取消选中,当前点击项选中,点击的是已选中,直接取消if (val.selected) {val.selected = false} else {item.values.forEach(valItem => valItem.selected = false)val.selected = true}
}</script><template><div class="goods-sku"><dl v-for="item in goods.specs" :key="item.id"><dt>{{ item.name }}</dt><dd><template v-for="val in item.values" :key="val.name"><img v-if="val.picture" @click="changeSku(item, val)" :class="{ selected: val.selected }" :src="val.picture":title="val.name"><span v-else @click="changeSku(val)" :class="{ selected: val.selected }">{{ val.name }}</span></template></dd></dl></div>
</template>

3. 规格禁用功能实现

1.png

3.1 生成路径字典

幂等算法 power-set.js


export default function bwPowerSet (originalSet) {const subSets = []// We will have 2^n possible combinations (where n is a length of original set).// It is because for every element of original set we will decide whether to include// it or not (2 options for each set element).const numberOfCombinations = 2 ** originalSet.length// Each number in binary representation in a range from 0 to 2^n does exactly what we need:// it shows by its bits (0 or 1) whether to include related element from the set or not.// For example, for the set {1, 2, 3} the binary number of 0b010 would mean that we need to// include only "2" to the current set.for (let combinationIndex = 0; combinationIndex < numberOfCombinations; combinationIndex += 1) {const subSet = []for (let setElementIndex = 0; setElementIndex < originalSet.length; setElementIndex += 1) {// Decide whether we need to include current element into the subset or not.if (combinationIndex & (1 << setElementIndex)) {subSet.push(originalSet[setElementIndex])}}// Add current subset to the list of all subsets.subSets.push(subSet)}return subSets
}

获取有效路径字典:

<script setup>
import {onMounted, ref} from 'vue'
import axios from 'axios'
import powerSet from './power-set.js'
// 商品数据
const goods = ref({})
const getGoods = async () => {// 1135076  初始化就有无库存的规格// 1369155859933827074 更新之后有无库存项(蓝色-20cm-中国)const res = await axios.get('http://pcapi-xiaotuxian-front-devtest.itheima.net/goods?id=1369155859933827074')goods.value = res.data.resultconst pathMap = getPathMap(goods.value)console.log(pathMap)
}
onMounted(() => getGoods())
const changeSelectedStatus = (item, val) => {if (val.selected) {val.selected = false} else {item.values.forEach(val => val.selected = false)val.selected = true}
}
// 创建生成路径字典对象函数
const getPathMap = (goods) => {const pathMap = {}// 1. 得到所有有效的Sku集合const effectiveSkus = goods.skus.filter(sku => sku.inventory > 0)console.log(effectiveSkus)// 2. 根据有效的Sku集合// 使用powerSet算法得到所有子集 [1,2] => [[1], [2], [1,2]]effectiveSkus.forEach(sku => {// 2.1 获取可选规格值数组const selectedValArr = sku.specs.map(val => val.valueName)// 2.2 获取可选值数组的子集const valueArrPowerSet = powerSet(selectedValArr)// 3. 根据子集生成路径字典对象// 3.1 遍历子集 往pathMap中插入数据valueArrPowerSet.forEach(arr => {// 根据Arr得到字符串的key,约定使用-分割 ['蓝色','美国'] => '蓝色-美国'const key = arr.join('-')// 给pathMap设置数据if (pathMap[key]) {pathMap[key].push(sku.id)} else {pathMap[key] = [sku.id]}})})return pathMap
}
</script><template><div class="goods-sku"><dl v-for="item in goods.specs" :key="item.id"><dt>{{ item.name }}</dt><dd><template v-for="val in item.values" :key="val.name"><!-- 图片类型规格 --><img v-if="val.picture" :src="val.picture" :title="val.name"><!-- 文字类型规格 --><span :class="{selected: val.selected}" v-else @click="changeSelectedStatus(item,val)">{{ val.name }}</span></template></dd></dl></div>
</template><style scoped lang="scss">
@mixin sku-state-mixin {border: 1px solid #e4e4e4;margin-right: 10px;cursor: pointer;&.selected {border-color: #27ba9b;}&.disabled {opacity: 0.6;border-style: dashed;cursor: not-allowed;}
}.goods-sku {padding-left: 10px;padding-top: 20px;dl {display: flex;padding-bottom: 20px;align-items: center;dt {width: 50px;color: #999;}dd {flex: 1;color: #666;> img {width: 50px;height: 50px;margin-bottom: 4px;@include sku-state-mixin;}> span {display: inline-block;height: 30px;line-height: 28px;padding: 0 20px;margin-bottom: 4px;@include sku-state-mixin;}}}
}
</style>
console.log(effectiveSkus)

在这里插入图片描述

console.log(pathMap)

在这里插入图片描述

3.2 根据路径字典设置初始化状态

思路:判断规格的name属性是否能在有效路径字典中找到,如果找不到就禁用

// 1. 定义初始化禁用状态
// specs:商品源数据 pathMap:路径字典
const initDisabledState = (specs, pathMap) => {// 约定:每一个按钮的状态由自身的disabled进行控制specs.forEach(item => {item.values.forEach(val => {// 路径字典中查找是否有数据 有-可以点击 没有-禁用val.disabled = !pathMap[val.name]})})
}// 2. 在数据返回后进行初始化处理
let patchMap = {}
const getGoods = async () => {// 1135076  初始化就有无库存的规格// 1369155859933827074 更新之后有无库存项(蓝色-20cm-中国)const res = await axios.get('http://pcapi-xiaotuxian-front-devtest.itheima.net/goods?id=1135076')goods.value = res.data.resultpathMap = getPathMap(goods.value)// 初始化更新按钮状态initDisabledState(goods.value.specs, pathMap)
}// 3. 适配模板显示
<img :class="{ selected: val.selected, disabled: val.disabled }"/>
<span :class="{ selected: val.selected, disabled: val.disabled }">{{val.name }}</span>

3.3 根据路径字典设置组合禁用状态

思路:

  1. 根据当前选中规格,生成顺序规格数组 => [‘黑色’, undefined, undefined ]
  2. 遍历每一个规格按钮

如何规格按钮已经选中,忽略判断
如果规格按钮未选中,拿着按钮的name值按顺序套入匹配数组对应的位置,最后过滤掉没有值的选项,通过-进行拼接成字符串key, 去路径字典中查找,没有找到则把当前规格按钮禁用

// 获取选中匹配数组 ['黑色',undefined,undefined]
const getSelectedValues = (specs) => {const arr = []specs.forEach(spec => {const selectedVal = spec.values.find(value => value.selected)arr.push(selectedVal ? selectedVal.name : undefined)})return arr
}const updateDisabledState = (specs, pathMap) => {// 约定:每一个按钮的状态由自身的disabled进行控制specs.forEach((item, i) => {const selectedValues = getSelectedValues(specs)item.values.forEach(val => {if (val.selected) returnconst _seletedValues = [...selectedValues]_seletedValues[i] = val.nameconst key = _seletedValues.filter(value => value).join('*')// 路径字典中查找是否有数据 有-可以点击 没有-禁用val.disabled = !pathMap[key]})})  
}

4. 产出 prop 数据

const changeSku = (item, val) => {// 省略...// 产出SKU对象数据const index = getSelectedValues(goods.value.specs).findIndex(item => item === undefined)if (index > -1) {console.log('找到了,信息不完整')} else {console.log('没有找到,信息完整,可以产出')// 获取sku对象const key = getSelectedValues(goods.value.specs).join('*')const skuIds = pathMap[key]console.log(skuIds)// 以skuId作为匹配项去goods.value.skus数组中找const skuObj = goods.value.skus.find(item => item.id === skuIds[0])console.log('sku对象为', skuObj)}
}

这篇关于封装sku组件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/696574

相关文章

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

基于Qt Qml实现时间轴组件

《基于QtQml实现时间轴组件》时间轴组件是现代用户界面中常见的元素,用于按时间顺序展示事件,本文主要为大家详细介绍了如何使用Qml实现一个简单的时间轴组件,需要的可以参考下... 目录写在前面效果图组件概述实现细节1. 组件结构2. 属性定义3. 数据模型4. 事件项的添加和排序5. 事件项的渲染如何使用

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

vue2 组件通信

props + emits props:用于接收父组件传递给子组件的数据。可以定义期望从父组件接收的数据结构和类型。‘子组件不可更改该数据’emits:用于定义组件可以向父组件发出的事件。这允许父组件监听子组件的事件并作出响应。(比如数据更新) props检查属性 属性名类型描述默认值typeFunction指定 prop 应该是什么类型,如 String, Number, Boolean,

JavaSE——封装、继承和多态

1. 封装 1.1 概念      面向对象程序三大特性:封装、继承、多态 。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节 。     比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器, USB 插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU 、显卡、内存等一些硬件元件。