当前位置: 首页 > news >正文

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

1. 准备模板渲染规格数据 使用Vite快速创建一个Vue项目&#xff0c;在项目中添加请求插件axios&#xff0c;然后新增一个SKU组件&#xff0c;在根组件中把它渲染出来&#xff0c;下面是规格内容的基础模板 <script setup> import { onMounted, ref } from vue import axi…...

Unity笔记:相机移动

基础知识 鼠标输入 在Unity中&#xff0c;开发者在“Edit” > “Project Settings” > “Input Manager”中设置输入&#xff0c;如下图所示&#xff1a; 在设置了Mouse X后&#xff0c;Input.GetAxis("Mouse X")返回的是鼠标在X轴上的增量值。这意味着它会…...

Java项目管理01-Maven基础

一、Maven的常用命令和生命周期 1.Maven的常用命令使用方式 complie&#xff1a;编译&#xff0c;将java文件编译为class字节码文件 clean&#xff1a;清理&#xff0c;删除字节码文件 test&#xff1a;测试&#xff0c;运行项目中的test类 package&#xff1a;打包&#x…...

计算机网络(第六版)复习提纲30

B HTTP 名词解释&#xff1a;协议HTTP定义了浏览器怎样向万维网服务器请求万维网文档&#xff0c;以及服务器怎样把文档传给浏览器。从层次的角度看&#xff0c;HTTP是面向事务的应用层协议&#xff0c;它是万维网上可靠地交换文件的重要基础&#xff0c;不仅能够传送完成超文本…...

基于SSM的图书管理系统

点击以下链接获取资源&#xff1a; https://download.csdn.net/download/qq_64505944/88820548?spm1001.2014.3001.5503 Java项目-6 librarySystem 开发完毕 万一你要作为课程设计或者毕设&#xff0c;不太会配&#xff0c;可以到下面我博客中私信&#xff0c;我帮你远程部…...

【GAMES101】Lecture 19 相机

目录 相机 视场 Field of View (FOV) 曝光&#xff08;Exposure&#xff09; 感光度&#xff08;ISO&#xff09; 光圈 快门 相机 成像可以通过我们之前学过的光栅化成像和光线追踪成像来渲染合成&#xff0c;也可以用相机拍摄成像 今天就来学习一下相机是如何成像的…...

《走进科学》灵异事件:Nginx配置改了之后一直报错

想要安装WoWSimpleRegistration&#xff0c;就定下来要用nginxphp8 &#xff0c;结果nginx那里加上php的支持之后一直报错&#xff1a; $ sudo service nginx restart Job for nginx.service failed because the control process exited with error code. See "systemctl…...

Select 选择器 el-option 回显错误 value

离谱 回显的内容不是 label 而是 value 的值 返回官方看说明&#xff1a; v-model的值为当前被选中的el-option的 value 属性值 value / v-model 绑定值有3种类型 boolean / string / number 根据自身代码猜测是&#xff1a;tableData.bookId 与 item.id 类型不一致导致 &…...

【51单片机Keil+Proteus8.9】门锁控制电路

门锁控制电路 二、设计思路 电路设计 1.电源部分&#xff1a;使用BATTERY为整个电路提供电源&#xff0c;可以在电路中加入一个电 源开关&#xff0c;以便控制电源的开启和关闭。 2.处理器部分&#xff1a;使用AT89C51芯片作为主处理器&#xff0c;通过编写程序实现门锁的 …...

比较Kamailio和OpenSIPS的重写contact函数

Kamailio&#xff1a;调用set_contact_alias()之后&#xff0c;在原有的contact的后面增加参数&#xff0c;具体地说&#xff0c;就是网络地址&#xff0c;网络端口和transport&#xff0c;好处是收到后续请求之时可以恢复原有contact的内容&#xff08;当然也有坏处&#xff0…...

【ETOJ P1046】斐波那契数列 题解(数学+动态规划)

题目描述 给定一个整数 T T T&#xff0c;表示样例数。 对于每个样例&#xff0c;给定一个整数 n n n&#xff0c;求斐波那契数列的第 n n n 项。 斐波那契数列定义为 f ( 1 ) f ( 2 ) 1 f(1) f(2) 1 f(1)f(2)1&#xff0c; f ( n ) f ( n − 1 ) f ( n − 2 ) f(…...

编码技巧——基于RedisTemplate的RedisClient实现、操作Lua脚本

1. 背景 在新公司的脚手架中开发&#xff0c;需要用到redis&#xff0c;发现没有封装好一套能集成各种常用命令、包括Lua脚本的方便使用的RedisTemplateClient&#xff0c;于是自己来实现下&#xff1b; springboot整合redis之后&#xff0c;提供了操作redis的简便方式&#…...

Asp .Net Core 系列:Asp .Net Core 集成 Panda.DynamicWebApi

文章目录 简介Asp .Net Core 集成 Panda.DynamicWebApi配置原理什么是POCO Controller&#xff1f;POCO控制器原理ControllerFeatureProvider实现自定义判断规则IApplicationModelConventionPanda.DynamicWebApi中的实现ConfigureApiExplorer()ConfigureSelector()ConfigurePar…...

【PTA浙大版《C语言程序设计(第4版)》|编程题】习题7-3 判断上三角矩阵(附测试点)

目录 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 代码呈现 测试点 上三角矩阵指主对角线以下的元素都为0的矩阵&#xff1b;主对角线为从矩阵的左上角至右下角的连线。 本题要求编写程序&#xff0c;判断一个给定的方阵是否…...

JVM 性能调优 - 参数调优(3)

查看 JVM 内存的占用情况 编写代码 package com.test;public class PrintMemoryDemo {public static void main(String[] args) {// 堆内存总量long totalMemory Runtime.getRuntime().totalMemory();// jvm 试图使用的最大堆内存long maxMemory Runtime.getRuntime().maxM…...

Django(十)

1. Ajax请求 浏览器向网站发送请求时&#xff1a;URL 和 表单的形式提交。 GETPOST 特点&#xff1a;页面刷新。 除此之外&#xff0c;也可以基于Ajax向后台发送请求&#xff08;偷偷的发送请求&#xff09;。 依赖jQuery编写ajax代码 $.ajax({url:"发送的地址"…...

OpenHarmony开源鸿蒙开发之旅

文章目录 一、op系统架构二、op系统构建1. op源代码目录2. op系统构建3. op开发环境搭建 三、op系统的子系统四、op系统芯片移植五、op系统启动流程六、op系统组件七、驱动框架 一、op系统架构 二、op系统构建 1. op源代码目录 2. op系统构建 3. op开发环境搭建 三、op系统…...

SpringBoot之整合PageHelper分页插件

SpringBoot之整合PageHelper分页插件 文章目录 SpringBoot之整合PageHelper分页插件1. 引入坐标2. application.yml配置3. 基本使用4. 对多个查询执行分页1. 默认第一个Select语句会执行分页2. 让Pagehelper也能执行多个分页的方法3. 完整案例 详细配置请查看官网或MyBatis分页…...

Android java基础_类的封装

一.面向对象编程的引入 写一个简单的程序输出张三&#xff0c;李四的名字 class Person {String name;String getName() {return "guangdong "name;} };public class Oop {public static void main(String args[]) {Person p1 new Person();p1.name "zhangs…...

Vue-57、Vue技术路由的参数如何传递

query参数传递 1、传递参数 <!-- 跳转路由并携带query参数&#xff0c;to的字符串写法--> <router-link :to"/home/message/detail?id${p.id}&title${p.title}"> {{p.title}} </router-link><!-- 跳转路由…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...