vue实现后台管理商品sku组合

article/2025/10/3 8:03:16

本人菜鸟一个,但最近要写一个后台管理前端部分,然后在写一个商品sku组合时遇到了点问题,所以在这里记录一下。在网上能找到很多方法,也有很简单的,但我看好像都是有一个问题,就是第一次生成之后,填写了数据但第二次重新生成表格时之前填写的数据就没了,这样用户体验很不好,于是我花了点时间写了这个demo,要注意的细节很多,我这写的不是很好,有什么可以优化的地方或者发现有bug欢迎大家提出来。
在这里插入图片描述

样式写的比较随意,大伙将就着看
以下是代码, 我引用了vue和element的cdn ,可以直接全部复制到html中打开

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><!-- import CSS --><link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"><style>.container {padding-top: 20px;text-align: center;}.sku-wrap {display: flex;flex-wrap: wrap;justify-content: center;}.table {margin: 30px auto;border-top: 1px solid #bbb;border-left: 1px solid #bbb;}.table td,.table th {padding: 4px 8px;border-right: 1px solid #bbb;border-bottom: 1px solid #bbb;}</style>
</head><body><div id="app"><div class="container"><el-input v-model="specification" placeholder="请输入规格名称(例如:颜色)" style="width:300px"@keyup.enter.native="addSpecification"></el-input><el-button type="primary" @click="addSpecification">添加规格</el-button><div v-for="(item, index) in specificationList" :key="index"><h4>{{ item.title }}<el-input v-model="item.value" size="small" placeholder="请输入规格值(例如:红色)" style="width:200px"@keyup.enter.native="addSpecificationValue(index)"></el-input><el-button type="primary" size="small" @click="addSpecificationValue(index)">添加值</el-button><el-button type="danger" size="small" @click="deleteSpecification(index)" icon="el-icon-delete" circle></el-button></h4><div class="sku-wrap"><div v-for="(sku, count) in item.list" :key="sku.id"><el-input size="mini" v-model="sku.value" @keyup.enter.native="handleValueInputBlur(sku)"@blur="handleValueInputBlur(sku)" placeholder="请输入内容" style="width:150px"></el-input><el-button style="margin-right: 20px;" type="danger" size="mini"@click="deleteSpecificationValue(index,count)" icon="el-icon-delete" circle></el-button></div></div></div><table class="table" border="0" cellspacing="0"><thead><tr><th v-for="(th, index) in tableHeadData" :key="index">{{th}}</th></tr></thead><tbody><tr v-for="(tr,index) in tableContentData" :key="index"><td v-for="(td,count) in tr" :key="count"><span v-if="td.type=='sku'">{{td.value}}</span><el-switch v-else-if="td.type==='上架'" v-model="td.value"></el-switch><el-select v-else-if="td.type==='标签'" v-model="td.value" placeholder="请选择标签"><el-option v-for="item in ['标签A','标签B']" :key="item" :label="item" :value="item"></el-option></el-select><el-input v-else v-model="td.value" style="width:100px"></el-input></td></tr></tbody></table></div></div>
</body>
<!-- import Vue before Element -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script>new Vue({el: '#app',data() {return {specification: "", // 每次添加的规格specificationList: [], //规格列表tableHeadData: [], //表头tableContentData: [], //生成后表中的数据delectFlag: false, //删除sku数据的标记};},methods: {// 规格值输入框失去焦点或者按回车会在表格中循环修改handleValueInputBlur(sku) {const { id, value } = skuthis.tableContentData.forEach(item => {item.forEach(v => {if (v.id === id) {v.value = value}})})},// 表格中的其他td数据staticData() {return [{type: "库存",value: "",}, {type: "价格",value: "",}, {type: "重量",value: "",}, {type: "描述",value: "",}, {type: "标签",value: "",}, {type: "上架",value: false,}]},// 添加规格addSpecification() {if (!this.specification) return this.$message.warning("请填写规格再添加");let staticSpecification = this.staticData().find(item => item.type === this.specification)if (staticSpecification !== undefined) return this.$message.warning(`规格不能为"${staticSpecification.type}"`)let repetition = this.specificationList.some(item => item.title === this.specification);if (repetition) return this.$message.warning("请勿添加重复的规格");this.specificationList.push({title: this.specification, //规格list: [], //规格值sort: this.specificationList.length, //用于后面生成数据时排序 也可以当作当前项的初始索引new: false, //用于判断是否有新添加规格 且这个规格添加规格值之后只有一个规格值才会等于true 用于后面表格列表添加一列数据value: "",// 输入框v-model绑定值});this.specification = "";},// 添加规格值addSpecificationValue(index) {if (!this.specificationList[index].value) return this.$message.warning("请填写规格值再添加");let repetition = this.specificationList[index].list.some(item => this.specificationList[index].value === item.value);if (repetition) return this.$message.warning("请勿添加重复的规格值");// 只有新添加了一个规格 且这个规格之前没有规格值(即下面push之后该规格只有一个规格值) new才会等于true// 用于后面表格列表添加一列数据// 必须tableContentData有长度才能为true 因为tableContentData没有数据的话 就不用添加一列 而是直接在tableContentData列表push一行数据就行了this.specificationList[index].new = (!this.specificationList[index].list.length && this.tableContentData.length) ? true : falsethis.specificationList[index].list.push({value: this.specificationList[index].value, //规格值id: Math.floor(new Date() / 1000) + Math.random().toFixed(8)});this.specificationList[index].value = "";this.generateData(index);},// 生成数据generateData(index) {if (!this.specificationList.length) return false;let flag = this.specificationList.some(item => item.list.length > 0);if (!flag) return false;this.generateTableHead();this.generateTableContent(index);},// 生成表头generateTableHead() {this.tableHeadData = [];this.specificationList.forEach(item => {if (item.list.length) {this.tableHeadData.push(item.title);}});this.tableHeadData.push(...(this.staticData().map(item => item.type)));},// 处理表格中的数据generateTableContent(idx) {const tempTitle = this.specificationList[idx].titlelet tempList = JSON.parse(JSON.stringify(this.specificationList)).filter(item => item.list.length); //简单的深拷贝 不影响原数据 然后过滤掉没有规格值的规格const index = tempList.findIndex(item => item.title === tempTitle)/*** idx的是this.specificationList中当前添加值的规格项的索引* index是tempList中的索引 即this.specificationList筛选后对应的索引* */const newItem = tempList[index].list[tempList[index].list.length - 1]; //newItem为新添加的规格值项tempList[index].list = [newItem]; //使当前新添加的规格值的规格项中只有新添加的规格值 不然会出现重复数据const hasNewSpecificationItem = tempList.find(item => item.new)/* * 如果当前添加规格值的规格项是第一次添加值 那么它的new为true  * hasNewSpecificationItem就为当前的那一项* 此时表格中的每行都需要添加一列新的规格数据 而不需要重新组合生成数据*  */if (hasNewSpecificationItem !== undefined) {for (let i = 0; i < this.tableContentData.length; i++) {//如果idx和index不等,证明出现没有值的规格 所以需要用筛选后的索引在表格中插入数据const spliceIndex = (idx === index) ? hasNewSpecificationItem.sort : index// 表格中的每行都插入一列新的规格数据this.tableContentData[i].splice(spliceIndex, 0, {type: "sku",value: hasNewSpecificationItem.list[0].value,sort: hasNewSpecificationItem.sort,key: hasNewSpecificationItem.title,id: hasNewSpecificationItem.list[0].id});}// 插入完数据后 使new标记为falsethis.specificationList.forEach(item => item.new = false)} else {// 这里改一下数据的结构const arr = tempList.map((item) => {return item.list.map((item2) => {return {type: "sku",value: item2.value,sort: item.sort,key: item.title,id: item2.id};});});// 数据改造成真正的表中数据的结构const tableContentData = this.cartesianProductOf(...arr);tableContentData.forEach(item => {// 这里给表中每行加一些静态数据,如输入框 下拉框等item.push(...this.staticData())});// 数据加入表格中this.tableContentData.push(...tableContentData);}},// 删除规格deleteSpecification(index) {const title = this.specificationList[index].title;const tempList = JSON.parse(JSON.stringify(this.specificationList)); //删之前先保存一份用于后面判断this.specificationList.splice(index, 1);//删除完让sort重新获取值  不然后面添加规格排序会乱this.specificationList.forEach((item, index) => {item.sort = index;});if (!this.tableContentData.length) return false;// 如果当前删除的规格里面没有值就不用在this.tableContentData中删除数据if (!tempList[index].list.length) return false;// 如果删除的是最后一个规格,则清空所有数据if (this.tableContentData[0][1].type != "sku") {this.tableContentData = [];this.tableHeadData = [];return false;}// 删除表中整列的数据this.deleteRowData(title);// 因为删除完一列数据的话 可能会有多行重复的数据 所以需要去重for (let i = 0; i < this.tableContentData.length; i++) {for (let j = i + 1; j < this.tableContentData.length; j++) {this.recursionJudgeValue(i, j, 0, true);if (this.delectFlag) {this.tableContentData.splice(j, 1);j--;}}}// 重新生成表头this.generateTableHead();},// 删除规格值deleteSpecificationValue(index, index2) {if (!this.tableContentData.length) return this.specificationList[index].list.splice(index2, 1)const length = this.specificationList[index].list.length;const id = this.specificationList[index].list[index2].id;const title = this.specificationList[index].title;this.specificationList[index].list.splice(index2, 1);if (length > 1) {// 如果有多个值时就删除this.tableContentData中所有带有这个值的那一行content: for (let i = 0; i < this.tableContentData.length; i++) {value: for (let j = 0; j < this.tableContentData[i].length; j++) {if (this.tableContentData[i][j].id == id) {this.tableContentData.splice(i, 1);i--;break value;}}}} else {// 如果只有一个值 那么就把值从所有数据中删除 就是删除列this.specificationList[index].new = true; //删除掉这个值 那么这个规格没有值了  就等同于是新添加的this.deleteRowData(title);if (this.tableContentData.length === 1 && this.tableContentData[0][0].type != "sku") {// 如果删除的是最后的规格和最后一个值就清空所有数据this.tableContentData = [];this.tableHeadData = [];// 如果没有数据就没有新值的概念this.specificationList.forEach((item) => {item.new = false;});return false;}// 重新生成表头this.generateTableHead();}},// 删除表中一列的数据deleteRowData(title) {for (let i = 0; i < this.tableContentData.length; i++) {value: for (let j = 0; j < this.tableContentData[i].length; j++) {if (this.tableContentData[i][j].key == title) {this.tableContentData[i].splice(j, 1);break value;}}}},// 递归判断两行的数据是否一样recursionJudgeValue(i, j, count, flag) {let tempFlag = this.tableContentData[i][count].value == this.tableContentData[j][count].value;let tempCount = count;const newFlag = flag && tempFlag;if (tempCount < this.specificationList.length) {tempCount++;this.recursionJudgeValue(i, j, tempCount, newFlag);} else {this.delectFlag = newFlag;}},// 笛卡尔积cartesianProductOf() {return Array.prototype.reduce.call(arguments,function (a, b) {var ret = [];a.forEach(function (a) {b.forEach(function (b) {ret.push(a.concat(b));// ret.push([...a, ...b]);});});return ret;},[[]]);},}})
</script></html>

http://chatgpt.dhexx.cn/article/llWWY6S7.shtml

相关文章

Geek-SKU 多商品sku组件支持v3、带图sku

如果您正在寻找一款支持v3、v2的多商品sku组件的话&#xff0c;那我想这款组件刚好可以满足各位的业务需求。 先来简单看一眼效果图: 一款轻量化、强大、拓展性强、可使用带图sku、可根据配置主题色自动生成相应的组件主题色的商品多规格sku&#xff0c;仅需要按照指定格式…

尚品汇_第5章_ 商品sku保存

尚品汇_第5章_ 商品sku保存 文章目录 尚品汇_第5章_ 商品sku保存一、业务介绍1.1 数据库表结构1.2 数据准备1.1 平台属性添加1.2 商品spu管理 二、保存skuInfo功能2.1 图片加载功能2.1.1 添加接口实现类2.1.2 添加控制器 2.2 销售属性2.2.1 编写接口以及实现类2.2.2 编写控制器…

js实现商品sku组合

const data [[红色,绿色,金色,青色,白色],[M,L,XL,XXL,XXXL],[男装,女装] ] var sku getSku(data)function getSku(data) {let newArr [];let delimiter "|(_##sku##_)|"; // 分隔符&#xff0c;避免sku中出现相同字符出现数据丢失情况function get(index, arr, …

java实现商品sku_jquery实现商品sku多属性选择功能(商品详情页)

SKUStock Keeping Unit(库存量单位)。即库存进出计量的基本单元&#xff0c;可以是以件&#xff0c;盒&#xff0c;托盘等为单位。 SKU是用来定价和管理库存的&#xff0c;比如一个产品有很多颜色&#xff0c;很多配置&#xff0c;每个颜色和配置的组合都会形成新的产品&#x…

商品sku 和批量编辑 js算法

这一段时间在搞商品后台的系统&#xff0c;其中关于通过商品的销售属性&#xff0c;生成SKU的处理&#xff0c;这里说明一下。 /*** 生成笛卡尔积* returns {*}*/ function descartes(array){if( array.length < 2 ) return array[0] || [];return [].reduce.call(array, fu…

laravel-admin 结合搭配商品sku

本文章基于jade 的基础上&#xff0c;完善部分功能。多谢jade提供sku方案 多个规格名&#xff0c;只显示2个添加规则值&#xff0c;保留已有的信息保存数据库后&#xff0c;json数据会自动排序 (2020-09-21 新增) Form 扩展 – 商品Sku 1.安装&#xff1a; composer requi…

商品SKU系统

如何设计库存&#xff0c;哪些库存呢&#xff1f;分类属性的库存&#xff1a;不同颜色 不同尺码的属性的库存 &#xff0c;这些就体现了商品的SKU&#xff0c;至于什么是SKU&#xff0c;自己去百度一下哈&#xff0c;所以首先我们就要设计商品Sku表以及关系表了 总体思路 1.…

java实现商品sku_商品SKU功能设计与优化

SpringBoot实战电商项目mall(30k+star)地址:github.com/macrozheng/… 摘要 原来的商品SKU设计存在着两个问题,一个是SKU表设计上面比较固化,无法扩展。另一个是当修改了商品信息之后,商品SKU的ID会发生变化,由于购物车表和订单商品表都关联了商品SKU的ID,这样就会导致匹…

sku mysql_商品sku处理

简单来说&#xff0c;一个电商类网站&#xff0c;根据平台的不同&#xff0c;商品的属性自然也就不同。 一般情况来说&#xff0c;一个商品 goods_id 对应多个 sku_id ; 设计表时我们会采取这样的方式&#xff1a; 产品表  ---   产品sku表  ---  产品基本属性表(产品属…

一个商品SKU是怎么生成的

首先说一说什么是SKU。。。。。。。自己百度去。。。 类似京东上面&#xff0c;未来人类S5这个台笔记本&#xff08;没错&#xff0c;我刚入手了&#xff09; 都是S5这个型号&#xff0c;但是因为CPU,显卡&#xff0c;内存&#xff0c;硬盘等不同&#xff0c;价格也不一样。CPU…

商品SKU

前言 最近项目开发涉及商品SKU&#xff0c;商品SKU计算原理就是笛卡尔积&#xff0c;下面对相关内容做一下总结。 一、什么是SKU SKUStock Keeping Unit(库存量单位)&#xff0c;即库存进出计量的单位&#xff0c;可以是以件&#xff0c;盒&#xff0c;托盘等为单位。针对电商而…

深度学习(二)---算法岗面试题

● 神经网络为什么用交叉熵 参考回答&#xff1a; 通过神经网络解决多分类问题时&#xff0c;最常用的一种方式就是在最后一层设置n个输出节点&#xff0c;无论在浅层神经网络还是在CNN中都是如此&#xff0c;比如&#xff0c;在AlexNet中最后的输出层有1000个节点&#xff0c…

深度学习面试题常见问答

目录 有哪些方法可以避免过拟合? 造成过拟合的原因&#xff1a; 解决办法&#xff1a; dropout在训练时和推理时的区别是什么&#xff1f; L1和L2正则化的区别&#xff1f;为什么L1比L2更稀疏&#xff1f; Batch Size大小如何影响收敛速度&#xff1f; BN的原理&#x…

机器学习、深度学习笔试题面试题整理

机器学习、深度学习笔试题 面试题总结 整理看到的内容&#xff0c;以免忘记 里面添加参考的链接&#xff0c;感谢各位大佬 感受野如何计算&#xff1f; 参考链接&#xff1a;https://blog.csdn.net/a841454735/article/details/88558906 感受野指的是一个特定的 CNN 特征&am…

深度学习(三)----算法岗面试题

● 神经网络为啥用交叉熵。 参考回答&#xff1a; 通过神经网络解决多分类问题时&#xff0c;最常用的一种方式就是在最后一层设置n个输出节点&#xff0c;无论在浅层神经网络还是在CNN中都是如此&#xff0c;比如&#xff0c;在AlexNet中最后的输出层有1000个节点&#xff0c;…

深度学习(二)-----算法岗面试题

● 深度学习了解多少&#xff0c;有看过底层代码吗&#xff1f;caffe,tf? ● 除了GMM-HMM&#xff0c;你了解深度学习在语音识别中的应用吗&#xff1f; 参考回答&#xff1a; 讲了我用的过DNN-HMM&#xff0c;以及与GMM-HMM的联系与区别&#xff1b;然后RNNCTC&#xff0c;这…

深度学习面试题-2

1. 下列哪一项属于特征学习算法&#xff08;representation learning algorithm&#xff09;&#xff1f; A K近邻算法 B 随机森林 C 神经网络 D 都不属于 正确答案是&#xff1a;C&#xff0c; 您的选择是&#xff1a;C 解析&#xff1a;神经网络会将数据转化为更适合解决目…

深度学习面试题总结1-20

1.CNN的卷积核是单层还是多层的&#xff1f; 描述网络模型中某层的厚度&#xff0c;通常用名词通道channel数或者特征图feature map数。不过人们更习惯把作为数据输入的前层的厚度称之为通道数&#xff08;比如RGB三色图层称为输入通道数为3&#xff09;&#xff0c;把作为卷积…

深度学习计算机视觉常见的29道面试题及解析

点击上方&#xff0c;选择星标或置顶&#xff0c;不定期资源大放送&#xff01; 阅读大概需要15分钟 Follow小博主&#xff0c;每天更新前沿干货 作者丨我要鼓励娜扎知乎 来源丨https://zhuanlan.zhihu.com/p/89587997 编辑丨极市平台 导读 正值秋招进行时&#xff0c;本文收集…

深度学习面试题-3

1. 梯度爆炸问题是指在训练深度神经网络的时候&#xff0c;梯度变得过大而损失函数变为无穷。在RNN中&#xff0c;下面哪种方法可以较好地处理梯度爆炸问题&#xff1f; A 用改良的网络结构比如LSTM和GRUs B 梯度裁剪 C Dropout D 所有方法都不行 正确答案是&#xff1a;B&am…