本人菜鸟一个,但最近要写一个后台管理前端部分,然后在写一个商品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>

vue实现后台管理商品sku组合相关推荐

  1. Vue实现后台管理案例

    Vue实现后台管理案例(★★★) 案例效果: 点击左侧的"用户管理","权限管理","商品管理","订单管理",&quo ...

  2. 仿淘宝后台设置商品SKU

    仿淘宝后台设置商品SKU 效果图 关键代码 function getVariantFun(doubelArrays) {let len = doubelArrays.length; //获取最外层的长 ...

  3. vue+elementUI 后台管理极简模板

    vue+elementUI 后台管理极简模板 写在前面 此篇文章为一篇说明文档,不是教你从零构建一个后台管理系统,而是基于一个实际项目,已经搭建好了一个后台管理系统的基础框架,教你如何在此基础上快速开 ...

  4. 商品sku算法php,笛卡尔乘积-电商网站商品sku组合算法应用

    笛卡尔乘积是指在数学中,两个集合X和Y的笛卡尓积(Cartesian product),又称直积,表示为X × Y,第一个对象是X的成员而第二个对象是Y的所有可能有序对的其中一个成员. 利用笛卡尔乘积 ...

  5. VUE搭建后台管理界面

    后台管理 一.前言 二.依赖配置 三.koa框架 四.数据库 五.路由 六.前端界面 6.1 登录界面 6.2 注册界面 6.3 展示界面 七.阿里云部署 7.1 前端项目 7.2 后端node 7. ...

  6. Vue _ 后台管理

    目录 一.后台管理 1.1.elmentui 安装与配置 1.2.后台登录 1.2.1.全局混入实现自定义验证提取 1.2.2.局部混入实现自定义验证规则 1.2.3.登录按钮实现防抖处理 1.3.后 ...

  7. vue+iview后台管理模板

    简介: iView管理是一个前端管理后台集成解决方案. 它基于Vue.js并使用UI工具包iView. 网盘下载地址: http://kekewangLuo.net/RSWYvbkjmPa0 图片:

  8. vue中后台管理登录后的token管理

    在做后台管理系统的时候,登录后token管理很重要.上代码,有瑕疵,有待改善,见谅. import Vue from 'vue' import Router from 'vue-router' imp ...

  9. vue通用后台管理(登录页面)

    1.这里是一个form表单,有两个表单域 一个是 用户名,一个是 密码 ,还有一个底部的 button 按钮,点击button会将表单的内容提交到我们的后端. 2.找到router下的index.js ...

最新文章

  1. 从《芈月传》看热门IP在互联网视频行业的“前世今生”
  2. 悉尼大学 伦敦大学联合出品:知识蒸馏最新综述
  3. sublime text3安装
  4. 利用js对table动态增加和删除行(附带table样式,鼠标滑过和点击样式)
  5. Java 8中的策略模式
  6. 基于链表的两个集合的差集
  7. 【Java】运用泽勒一致性计算某天是星期几
  8. scheme 中文教程
  9. 前端小白找工作日记(1)
  10. 100个Java经典例子(1-10)
  11. iPhone OS 4发布:支持多任务
  12. Day021 - HTML基础
  13. 什么是SEM?SEM是否包括SEO?
  14. 网络虚拟化NSX学习笔记
  15. Java websocket + redis 实现多人单聊天室,多人多聊天室, 一对一聊天
  16. 中图法检索计算机科学方面,信息检索 第一次上机答案lpar;南通大学rpar;
  17. 10个常用的数据分析商业模型之价值链分析模型(三)
  18. 【BP数据预测】布谷鸟算法优化BP神经网络数据预测【含Matlab源码 1121期】
  19. python+气象 | 在地图背景下绘制全国站点气温分布图
  20. IOS 10.2打开doubleh3lix闪退的解决方法

热门文章

  1. mysql 添加外键 完整_详解mysql添加外键的方法
  2. 【暗月内网靶场】项目七(不那么简单的内网靶场)
  3. 华为云爆出“神器”,助力游戏AI“闯关升级”
  4. 在C#中实现将PPT 转换为 PPTX,开发小白的Aspose操作指南
  5. 杭电4557-非诚勿扰
  6. 【GCN】: IntentGC算法框架
  7. 局长吃鱼的故事-太经典了
  8. ChatGPT的朋友们:大语言模型经典论文一次读到吐
  9. webp格式和jpeg格式有何不同
  10. catia如何单击停止捕获_catia怎么取消自动捕捉网格点 catia自动捕捉取消教程