vue中SKU实现

通过发送请求获取商品详情数据,包括商品规格(specs)和库存信息(skus)。

选中状态更新:根据当前状态进行激活或取消激活的逻辑,通过为每个规格项添加的“selected”字段来标识是否激活,同时利用样式处理,通过动态类属性来直观地显示选中状态。

禁用状态更新:基于库存情况来确定规格是否禁用,通过生成有效路径字典来简化匹配过程。具体步骤包括获取有效 Sku 数组、生成子集以及构建路径字典对象。

初始化规格禁用:通过遍历规格对象,利用“name”字段与路径字典进行匹配来确定是否禁用,并通过增加“disabled”字段和动态类名来实现显示上的禁用效果。

点击时组合禁用更新:当用户点击规格时,通过特定步骤获取选中项数组并进行匹配来更新禁用状态。

产出有效 SKU 信息:以已选择项数组中不存在“undefined”来判断用户已选择所有有效规格,然后通过拼接已选数组为路径字典的键来获取相应的 SKU 信息对象。

vue

<script setup>
import { onMounted, ref } from "vue";
import axios from "axios";
// 导入幂级算法
import bwPowerSet from "./bwPowerSet";// 存储用于在页面中展示的规格数据
const specs = ref([]);
// 创建 UI 状态 (选中、禁用)
const UIState = ref([]);// 声明规格查询字典
let pathMap = {};//获取商品详情数据
function requestGoodsApi(id) {return axios.get("https://apipc-xiaotuxian-front.itheima.net/goods", {params: { id },});
}// 根据规格数据创建其对应的界面状态
function createUIStatus(specs) {// UI 状态数组const UIStatus = [];// 遍历源规格分组specs.forEach((spec) => {// 创建规格分组const group = [];// 遍历具体的规格选项spec.values.forEach(() => {// 设置每一个规格选项的选中状态和禁用状态(初始值)group.push({ selected: false, disabled: false });});// 将规格组对象添加到拷贝结果数组中UIStatus.push(group);});// 返回 UI 状态数组return UIStatus;}// 设置规格的选中状态
function setSelected(index, i) {// 获取当前用户点击的规格const current = UIState.value[index][i];// 获取当前用户点击的规格对应的规格组const group = UIState.value[index]; // 如果当前规格已经是禁用状态, 不能被选择, 所以阻止代码继续执行if (current.disabled) return;// 如果用户点击的规格已经是选中的if (current.selected) {// 让其取消选中current.selected = false;} else {// 先将该规格中的所有规格取消选中group.forEach((item) => (item.selected = false));// 将当前用户点击的规格设置为选中current.selected = true;}// 用户选择规格后更新规格的禁用状态setDisabled();
}// 设置规格的禁用状态
function setDisabled() {// 遍历每一组规格数据specs.value.forEach((spec, index) => {// 获取用户选择的规格名称数组const selected = getUserSelected();// 遍历这一组规格数据中的具体规格spec.values.forEach((value, i) => {// 如果当前规格已经被选中了, 说明它可以选, 不需要被禁用if (UIState.value[index][i].selected) return;// 将当前规格名称放入用户选择的规格数组名称中, 待匹配selected[index] = value.name;// 检测当前规格是否可以选择const key = selected.filter((name) => name).join("_");// 如不能选择, 设置当前规格的 disabled 为 trueUIState.value[index][i].disabled = !(key in pathMap);});});
}// 获取用户选择的规格名称数组
function getUserSelected() {// 声明用于存储用户选择的规格名称数组const names = [];// 遍历规格组specs.value.forEach((spec, index) => {// 查找当前规格组中被选中的规格的索引const selectedIndex = UIState.value[index].findIndex((item) => item.selected);// 如果找到了if (selectedIndex !== -1) {// 将该规格放在它自己的位置上names[index] = spec.values[selectedIndex].name;} else {// 如果没有找到, 当前规格使用 undefined 进行占位names[index] = undefined;}//获取到选中的规格console.log(names);});// 返回用户选择的规格名称数组return names;}// 创建规格查询字典
function createPathMap(skus) {// 过滤出有库存的商品规格组合skus.filter((sku) => sku.inventory > 0)// 遍历有库存的商品规格组合.forEach((sku) => {// 将当前遍历的规格组合中的规格名称临时存到一个数组中// ['蓝色', '20cm', '中国']const valueNames = sku.specs.map((spec) => spec.valueName);// 获取用户可以选择的所有可能的规格及规格组合// ['蓝色', '20cm']// [['蓝色'], ['20cm'], ['蓝色', '20cm']]const sets = bwPowerSet(valueNames).filter((set) => set.length > 0);// 获取当前商品的规格数量, 将用于判断某个规格是否是完整的const max = valueNames.length;// 遍历用户可以选择的所有可能的规格及规格组合sets.forEach((set) => {// 将规格名称以 _ 进行拼接const key = set.join("_");// 用于判断当前规格是否是完整的const isCompleted = set.length === max;// 判断规格查询对象中是否已经存储了当前规格if (!(key in pathMap)) {// 如果当前规格是完整的if (isCompleted) {// 将当前规格或规格组合添加到规格查询对象中并赋值 sku idpathMap[key] = sku.id;} else {// 将当前规格或规格组合添加到规格查询对象中pathMap[key] = null;}}});});return pathMap;}// 组件挂载完成之后
onMounted(async () => {// 获取商品详情数据const response = await requestGoodsApi("1369155859933827074");// 保存规格数据specs.value = response.data.result.specs;// 为规则数据附加 UI 状态UIState.value = createUIStatus(specs.value);// 创建规格查询对象pathMap = createPathMap(response.data.result.skus);// 设置规格的初始禁用效果setDisabled();
});
</script><template><div class="goods-sku"><dl v-for="(spec, index) in specs" :key="spec.id"><dt>{{ spec.name }}</dt><dd><template v-for="(item, i) in spec.values"><imgv-if="item.picture":class="{selected: UIState[index][i].selected,disabled: UIState[index][i].disabled,}"@click="setSelected(index, i)":src="item.picture":alt="item.name"/><spanv-else:class="{selected: UIState[index][i].selected,disabled: UIState[index][i].disabled,}"@click="setSelected(index, i)">{{ item.name }}</span></template></dd></dl></div>
</template><style scoped>
.goods-sku {padding-left: 10px;padding-top: 20px;
}.goods-sku dl {display: flex;align-items: center;
}.goods-sku dl dt {width: 50px;color: #999;
}.goods-sku dl dd {flex: 1;color: #666;
}.goods-sku dl dd > img {width: 50px;height: 50px;border: 1px solid #e4e4e4;margin-right: 10px;cursor: pointer;display: block;float: left;
}.goods-sku dl dd > img.selected {border-color: #27ba9b;
}.goods-sku dl dd > img.disabled {opacity: 0.6;border-style: dashed;cursor: not-allowed;
}.goods-sku dl dd > span {display: inline-block;height: 30px;line-height: 28px;padding: 0 20px;border: 1px solid #e4e4e4;margin-right: 10px;cursor: pointer;
}.goods-sku dl dd > span.selected {border-color: #27ba9b;
}.goods-sku dl dd > span.disabled {opacity: 0.6;border-style: dashed;cursor: not-allowed;
}
</style>

bowPowerSet.js

export default function bwPowerSet(originalSet) {// 初始化一个空数组用于存储子集const subSets = [];// 计算原始集合的元素个数const numberOfCombinations = 2 ** originalSet.length;// 循环生成所有可能的组合for (let combinationIndex = 0;combinationIndex < numberOfCombinations;combinationIndex += 1) {// 初始化一个空数组用于存储当前组合的子集const subSet = [];// 遍历原始集合的每个元素for (let setElementIndex = 0;setElementIndex < originalSet.length;setElementIndex += 1) {// 检查当前元素是否在当前组合中if (combinationIndex & (1 << setElementIndex)) {// 如果是,则将该元素添加到子集中subSet.push(originalSet[setElementIndex]);}}// 将当前子集添加到子集数组中subSets.push(subSet);}// 返回所有生成的子集return subSets;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/848764.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Shell脚本文本处理三剑客(grep、awk、sed)和正则表达式

一、正则表达式 1.正则表达式基础 正则表达式&#xff08;regular expression&#xff09;描述了一种字符串匹配的模式&#xff08;pattern&#xff09;&#xff0c;可以用来检查一个串是否含有某种子串&#xff0c;将匹配的子串替换或者从某个串中取出符号某个条件的子串等&…

青否数字人直播源码超级管理后台操作步骤!

青否数字人直播源码超级管理后台&#xff0c;我们将详细介绍一下数字人的管理后台的详细操作步骤&#xff01; 1.管理端入口 2.管理后台预览 账号管理&#xff0c;模特管理&#xff0c;声音管理&#xff0c;任务管理&#xff0c;卡类管理&#xff0c;代理商&#xff0c;克隆端 …

七月份大理站、ACM独立出版、高录用稳检索,2024年云计算与大数据国际学术会议(ICCBD 2024)

【ACM独立出版 | 高录用 | EI核心检索稳定】 2024年云计算与大数据国际学术会议&#xff08;ICCBD 2024) 2024 International Conference on Cloud Computing and Big Data (ICCBD 2024) 一、重要信息 大会官网&#xff1a;www.iccbd.net &#xff08;点击投稿/参会/了解会…

小程序 UI 风格,独具匠心

小程序 UI 风格&#xff0c;独具匠心

docker 下载镜像发现超时,加速加速方法

报错原因有可能旧的不能用了&#xff01;&#xff01;&#xff01;换下面的&#xff01;&#xff01;&#xff01; cat /etc/docker/daemon.json "registry-mirrors": ["https://bhu1x6ya.mirror.aliyuncs.com"] 编辑完成后执行以下命令重启docker即可&a…

python方法

目录 公共方法 1.容器类型之间的转化 ​2.运算符 ​3.通用函数 公共方法 1.容器类型之间的转化 # 类型转化 data_str itcast data_list [hadoop, spark, hive, python, hive] data_tupe (hadoop, spark, hive, python, hive) data_set {hadoop, spark, hive, python,…

Linux.小技巧快捷键

1. ctrl c 强制停止 终止某些程序的运行 也可以取消某行命令 2. ctrl d 退出或登出 进入python环境中&#xff0c;使用ctrl d 退出 3.history 查看历史使用了哪些命令 4. ! 历史最近使用的命令的开头 5.使用ctrl r 搜索历史使用的命令 按下 ctrl r 会进入 reverse -…

19 - 查询结果的质量和占比(高频 SQL 50 题基础版)

19 - 查询结果的质量和占比 -- round(avg(rating<3)*100,2)round(avg(if(rating<3,1,0))*100,2) select query_name,round(avg(rating/position),2) quality,round(avg(if(rating<3,1,0))*100,2) poor_query_percentage fromQueries group byquery_name;

vue 响应拦截器,针对某个接口的返回值做特殊处理

1、service.interceptors.response.use 接收两个参数&#xff1a; &#xff08;1&#xff09;参数一&#xff1a;接口成功的回调函数 &#xff08;2&#xff09;参数二&#xff1a;接口失败的回调函数 如要实现以下需求&#xff1a;匹配路径中包含 /api-special 的接口&…

企业微信hook接口协议,ipad协议http,一个用户多个标签

一个用户多个标签 参数名必选类型说明uuid是String每个实例的唯一标识&#xff0c;根据uuid操作具体企业微信 请求示例 {"uuid": "168885xxx599424","userid":0,//用户id"labelid_list":[ //标签id数组0] } 返回示例 {"data…

基于python的网上挂号预约系统-计算机毕业设计源码35796

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;医院当然也不例外。网上挂号预约系统是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;采用Py…

Mybatis及其他相关架构设计

目录 Mybatis 文件处理集成 Maven构建 工具包 Mybatis MybatisPlus多表关联分页查询_mybatisplus多表联查分页查询-CSDN博客 Mybatis-plus 批量插入实现_mybatis-plus 批量插入功能-CSDN博客 sharding-mybatis分库分表-CSDN博客 MybatisPlus分页插件配置_mybatisplus配置…

nesting in wrf

Choices for Nesting are:0 no nesting (only available for serial and smpar) 0. no nesting (only available for serial and smpar)1. basic2. preset moves preset moves3. vortex following • default is option 0 for serial/smpar, 1 for dmpar • smpar Shared Mem…

VS code上创建Vue项目详细化教程2-配置并创建Vue项目

目录 1. 环境准备 1.1 Node.js环境配置 1.1.1 安装Node.js 1.1.2 Node配置全局安装目录&#xff1a; 1.2 安装Vue-cli 2. 工程化Vue项目创建 2.1 命令行形式 2.2 UI 界面&#xff08;我们此处采用UI模式&#xff09; 2.2.1 在文件目录下终端输入 2.2.2 创建新项目 2…

源码文章上传无忧,论坛小程序支持

前言 在数字化时代&#xff0c;知识的分享与传播显得愈发重要。为了满足广大创作者和求知者的需求&#xff0c;我们推出了全新的论坛小程序&#xff0c;不仅支持文章、源码、链接等多样化内容的上传&#xff0c;还实现了付费观看功能&#xff0c;为创作者们提供了一个展示才华…

快速自定义表单开发的优势介绍

进行高效率的办公是很多职场人的梦想。借助什么样的软件平台可以提质增效&#xff1f;低代码技术平台是当前较为流行的办公软件平台产品&#xff0c;具有灵活性、易操作、好维护等多个优势特点&#xff0c;操作人员只需要像搭积木似地操作&#xff0c;就可以搭建属于客户的个性…

UnityXR Interactable Toolkit如何实现Climb爬梯子

前言 在VR中,通常会有一些交互需要我们做爬梯子,爬墙的操作,之前用VRTK3时,里面是还有这个Demo的,最近看XRI,发现也除了一个爬的示例,今天我们就来讲解一下 如何在Unity中使用XR Interaction Toolkit实现爬行(Climb)操作 环境配置 步骤 1:设置XR环境 确保你的Uni…

【ArcGISProSDK】 读取多面体信息并导出XML

结果展示 代码 using ArcGIS.Core.CIM; using ArcGIS.Core.Data; using ArcGIS.Core.Data.DDL; using ArcGIS.Core.Geometry; using ArcGIS.Core.Internal.CIM; using ArcGIS.Desktop.Catalog; using ArcGIS.Desktop.Core; using ArcGIS.Desktop.Editing; using ArcGIS.Deskto…

代码签名证书:软件安全的守护神

在数字化日益普及的今天&#xff0c;软件安全问题愈发受到人们的关注。而在这其中&#xff0c;一个常被提及但可能不为大众所熟知的名词——“代码签名证书”&#xff0c;实际上在软件安全领域扮演着举足轻重的角色。今天&#xff0c;我们就来聊聊代码签名证书对软件安全到底有…

mint linux下简单调整交换文件swapfile大小

默认交换文件位置/swapfile&#xff0c;大小2G&#xff0c;现在需要调整到10G&#xff0c;依次执行下面命令即可&#xff1a; 停止现用交换文件 sudo swapoff -a删除现用交换文件 sudo rm /swapfile建立新交换文件 sudo fallocate -l 10G /swapfile设置权限 sudo chmod 600…