封装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

相关文章

JAVA封装多线程实现的方式及原理

《JAVA封装多线程实现的方式及原理》:本文主要介绍Java中封装多线程的原理和常见方式,通过封装可以简化多线程的使用,提高安全性,并增强代码的可维护性和可扩展性,需要的朋友可以参考下... 目录前言一、封装的目标二、常见的封装方式及原理总结前言在 Java 中,封装多线程的原理主要围绕着将多线程相关的操

kotlin中的行为组件及高级用法

《kotlin中的行为组件及高级用法》Jetpack中的四大行为组件:WorkManager、DataBinding、Coroutines和Lifecycle,分别解决了后台任务调度、数据驱动UI、异... 目录WorkManager工作原理最佳实践Data Binding工作原理进阶技巧Coroutine

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx

Vue ElementUI中Upload组件批量上传的实现代码

《VueElementUI中Upload组件批量上传的实现代码》ElementUI中Upload组件批量上传通过获取upload组件的DOM、文件、上传地址和数据,封装uploadFiles方法,使... ElementUI中Upload组件如何批量上传首先就是upload组件 <el-upl

Vue3中的动态组件详解

《Vue3中的动态组件详解》本文介绍了Vue3中的动态组件,通过`component:is=动态组件名或组件对象/component`来实现根据条件动态渲染不同的组件,此外,还提到了使用`markRa... 目录vue3动态组件动态组件的基本使用第一种写法第二种写法性能优化解决方法总结Vue3动态组件动态

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前言