商品sku排列组合,uniapp小程序商品sku展示+vue2后台管理添加商品
·
为了符合各大网友的产品需求,此文档有两个版本规格效果
版本1.小程序商品进入商品详情自动选中第一个相应规格参数,价格原价库存
版本2.小程序商品进入详情,不选择任何规格,当库存为0,规格展示为0的相应规格参数无法选中和展示
太难了,如果该文档帮助到了你,那就麻烦你那单身二十年的手速双击二连一下,哈哈哈
- 2023.06.01修改 小程序展示和后台管理系统添加商品
- 2023.11.18修改 小程序展示和后台管理系统添加商品
- 2023.11.29修改 小程序展示和后台管理系统添加商品
- 2024.11.13修改,小程序规格展示逻辑版本2
版本1

版本2


vue2+Element UI 2023.11.18 修改
<template>
<div>
<div class="border-box">
<div class="border">
<div class="name">商品规格</div>
<div class="border-guige">
<div v-for="(item, index) in itemList" :key="index" style="margin-top: 20px">
<div class="flex-guige">
<div>规格名</div>
<div class="width-input">
<el-input v-model="item.specificationname" placeholder="请输入内容"></el-input>
</div>
</div>
<div class="flex-guige" style="margin-top: 20px">
<div>规格值</div>
<div class="width-tag">
<el-tag v-for="(tag, ins) in item.specificationvalue" :key="ins" closable
:disable-transitions="false" @close="handleClose(index, ins)">
{{ tag.value }}
</el-tag>
<el-input class="input-new-tag" v-if="inpurIndex == index && inputVisible"
v-model="inputValue" ref="saveTagInput" size="small"
@keyup.enter.native="handleInputConfirm" @blur="handleInputConfirm"
@focus="focusConfirm(index)">
</el-input>
<el-button v-else class="button-new-tag" size="small" @click="showInput(index)">+
规格值</el-button>
</div>
</div>
</div>
<div class="flex-guige" style="margin-top: 20px">
<el-button type="primary" plain size="small" @click="addValue">添加规格项</el-button>
<el-button type="danger" size="small" plain @click="deleteValue">清空规格项</el-button>
</div>
</div>
</div>
<div class="border" style="margin-top:20px">
<div class="name">规格明细</div>
<el-table :data="mapList" border style="width: 100%">
<el-table-column v-for="(item, index) in itemList" :key="index" :label="item.specificationname"
width='120'>
<!-- 分行显示 -->
<template slot-scope="scope">
<div v-if="scope.row.sku[index]">{{ scope.row.sku[index].value }}</div>
</template>
</el-table-column>
<el-table-column prop="groupName" label="库存" width='120'>
<template slot-scope="scope">
<el-input v-model="scope.row.stock" placeholder="请输入内容"></el-input>
</template>
</el-table-column>
<el-table-column prop="address" label="原价" width='120'>
<template slot-scope="scope">
<el-input v-model="scope.row.cprice" placeholder="请输入内容"></el-input>
</template>
</el-table-column>
<el-table-column prop="goodsCount" label="现价" width='140'>
<template slot-scope="scope">
<el-input v-model="scope.row.oprice" placeholder="请输入内容"></el-input>
</template>
</el-table-column>
<el-table-column prop="goodsCount" label="头图" width='190'>
<template slot-scope="scope">
<el-upload action="https://jsonplaceholder.typicode.com/posts/" :limit="1" list-type="picture-card"
@on-preview="handlePictureCardPreview" @on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="scope.row.img" alt="">
</el-dialog>
</template>
</el-table-column>
<el-table-column fixed="right" align="center" width="230" label="操作">
<el-button type="primary" size="small">打包</el-button>
</el-table-column>
</el-table>
</div>
</div>
<el-button type="primary" @click="onSave">保存</el-button>
<table border cellspacing="0" cellpadding="0" id="table"></table>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: "颜色",
item: ["黑", "金", "白"],
},
{
name: "内存",
item: ["16G", "32G"],
},
],
inputVisible: false,
inputValue: "",
itemList: [
{
specificationname: "",
specificationvalue: [],
},
],
inpurIndex: 0,
mapList: [],
dialogImageUrl: '',
dialogVisible: false
};
},
methods: {
// 保存
onSave(){
console.log("zzz",this.itemList,this.mapList)
},
// 添加
addValue() {
let itList = {
specificationname: "",
specificationvalue: [],
};
this.itemList.push(itList);
},
// 清空
deleteValue() {
this.itemList = [
{
specificationname: "",
specificationvalue: [],
},
];
this.mapList = []
},
// 聚焦判断是哪个数据
focusConfirm(index) {
this.inpurIndex = index;
},
// 删除
handleClose(index, ins) {
let skuList = []
this.itemList[index].specificationvalue.splice(ins, 1);
for (var { specificationvalue } of this.itemList) {
if(specificationvalue.length){
skuList.push(specificationvalue)
}
}
var att = this.composeTableData(skuList)
this.mapList = att
if(skuList.length == 0){
this.mapList = []
}
},
showInput(e) {
this.inpurIndex = e;
this.inputVisible = true;
},
//
handleInputConfirm(e) {
let skuList = []
let inputValue = this.inputValue;
if (inputValue) {
this.itemList[this.inpurIndex].specificationvalue.push({
value: inputValue,
});
for (var { specificationvalue } of this.itemList) {
skuList.push(specificationvalue)
}
var attList = this.composeTableData(skuList)
// console.log("dd",attList)
this.mapList = attList
}
this.inputVisible = false;
this.inputValue = "";
},
// 排列组合
cartesianProductOf() {
return Array.prototype.reduce.call(arguments, function (a, b) {
const result = [] // 存储拼接的结果
a.forEach(item_a => {
b.forEach(item_b => {
result.push(item_a.concat(item_b))
})
})
return result
}, [[]])
},
// 获取组合好的sku规格数据
composeTableData(sku_list) {
const arr = this.cartesianProductOf(...sku_list)
console.log("arr",arr,this.mapList)
return arr.map((item,index) => {
return {
sku: item, // 组合好的规格[{...},{...}]
oprice: '', // 销售价
cprice: '', // 成本价
stock: '', // 库存
img:'', //图片
}
})
},
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePictureCardPreview(file) {
console.log(file)
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
},
};
</script>
<style scoped >
.border-box {
width: 80%;
/* height: 500px; */
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin: auto;
padding: 30px;
}
.border {
display: flex;
}
.border-guige {
width: 100%;
border-radius: 2px;
border: 1px solid rgb(227, 223, 223);
margin-left: 10px;
padding: 20px;
}
.flex-guige {
display: flex;
}
.name {
width: 80px;
}
.width-input {
width: 30%;
margin-left: 20px;
}
.width-tag {
margin-left: 20px;
}
.el-tag+.el-tag {
margin-left: 10px;
}
.button-new-tag {
height: 32px;
line-height: 30px;
padding-top: 0;
padding-bottom: 0;
margin-left: 5px;
}
.input-new-tag {
width: 90px;
margin-left: 10px;
vertical-align: bottom;
}
.avatar-uploader .el-upload {
border: 1px dashed #846767;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
uniapp 小程序代码 版本1
<template>
<view class="box">
<view class="money">¥{{originalPrice}}<text>¥{{currentPrice}}</text></view>
<view class="flex-item">
<view v-for="(item,index) in skuName" :key="index">
<view class="title">{{item.specificationname}}:</view>
<view class="f-v">
<view @tap="activeSku(index,ins)" :class="el.type == true?'active_sku_color':'active_sku'"
v-for="(el,ins) in item.specificationvalue" :key="ins">{{el.value}}</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default{
data(){
return{
skuName:[],
//后端返回数据
skuList:[{originalPrice:299,currentPrice:199,inventory:100,img:'',sku:[{value:'黑色'},{value:'大'}]},
{originalPrice:399,currentPrice:8899,inventory:100,img:'',sku:[{value:'绿色'},{value:'大'}]},
{originalPrice:499,currentPrice:9999,inventory:100,img:'',sku:[{value:'黑色'},{value:'小'}]},
{originalPrice:599,currentPrice:4499,inventory:100,img:'',sku:[{value:'绿色'},{value:'小'}]}],
activeIndex:0, //规格
originalPrice:'',//现价
currentPrice:'',//原价
}
},
onLoad() {
this.onSaveSkuList()
},
methods:{
onSaveSkuList(){
//后端返回数据skuName
this.skuName = [
{specificationname:'颜色',specificationvalue:[{value:'黑色'},{value:'绿色'}]},
{specificationname:'型号',specificationvalue:[{value:'大'},{value:'小'}]}
],
this.skuName.forEach((el,index)=>{
el.specificationvalue.forEach((item,ins)=>{
this.skuName[index].specificationvalue[0].type = true
})
})
this.originalPrice = this.skuList[0].originalPrice
this.currentPrice = this.skuList[0].currentPrice
},
activeSku(i,is){
this.skuName[i].specificationvalue.forEach((item,ins)=>{
this.$set(this.skuName[i].specificationvalue[ins],'type',false)
})
this.$set(this.skuName[i].specificationvalue[is],'type',true)
let list = []
this.skuName.forEach((item,ins)=>{
item.specificationvalue.forEach((el,index)=>{
if(el.type == true){
list.push({value:el.value})
}
})
})
this.skuList.forEach((el,index)=>{
if(JSON.stringify(list) == JSON.stringify(el.sku)){
this.originalPrice = el.originalPrice
this.currentPrice = el.currentPrice
}
})
}
}
}
</script>
<style lang="scss" scoped>
.money{
display: flex;
align-items: center;
color: red;
margin-left: 30rpx;
margin-top: 30rpx;
text{
color: #999;
}
}
.flex-item{
width: 690rpx;
margin: auto;
margin-top: 40rpx;
.title{
font-size: 28rpx;
font-weight: bold;
margin: 20rpx 0rpx 20rpx 0rpx;
}
.f-v{
display: flex;
align-items: center;
flex-wrap: wrap;
.active_sku{
border: 1rpx solid #999;
padding: 3rpx 10rpx 3rpx 10rpx;
font-size: 24rpx;
margin-right: 10rpx;
border-radius: 5rpx;
}
.active_sku_color{
border: 1rpx solid #990002;
padding: 3rpx 10rpx 3rpx 10rpx;
font-size: 24rpx;
margin-right: 10rpx;
border-radius: 5rpx;
color: #990002;
}
}
}
</style>
uniapp 小程序代码 版本2
<template>
<view class="content">
<view class="box">
<view class="money" v-if="inventory != 0">当前价格:¥{{originalPrice}}<text> -- 原价:¥{{currentPrice}}</text></view>
<view v-else>库存为0,价格范围:¥{{originalPrice}} ---- ¥{{currentPrice}}</view>
<view class="flex-item">
<view class="title" v-if="inventory != 0">库存:{{inventory}}</view>
<view v-for="(item,index) in skuName" :key="index">
<view class="title">{{item.specificationname}}:</view>
<view class="f-v">
<view @tap="activeSku(index,ins,el)" :class="el.type == true?'active_sku_color':el.type == 'DOG'?'wsku':'active_sku'"
v-for="(el,ins) in item.specificationvalue" :key="ins">{{el.value}}</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
skuName:[],
// 后端返数据
skuList:[
{originalPrice:299,currentPrice:199,inventory:10,img:'',sku:[{value:'黑色'},{value:'大'},{value:'16G'}]},
{originalPrice:299,currentPrice:199,inventory:10,img:'',sku:[{value:'黑色'},{value:'大'},{value:'64G'}]},
{originalPrice:499,currentPrice:9999,inventory:9,img:'',sku:[{value:'黑色'},{value:'小'},{value:'16G'}]},
{originalPrice:499,currentPrice:9999,inventory:9,img:'',sku:[{value:'黑色'},{value:'小'},{value:'64G'}]},
{originalPrice:499,currentPrice:9999,inventory:9,img:'',sku:[{value:'黑色'},{value:'中'},{value:'16G'}]},
{originalPrice:499,currentPrice:9999,inventory:9,img:'',sku:[{value:'黑色'},{value:'中'},{value:'64G'}]},
{originalPrice:399,currentPrice:8899,inventory:1,img:'',sku:[{value:'绿色'},{value:'大'},{value:'16G'}]},
{originalPrice:399,currentPrice:8899,inventory:0,img:'',sku:[{value:'绿色'},{value:'大'},{value:'64G'}]},
{originalPrice:599,currentPrice:4499,inventory:66,img:'',sku:[{value:'绿色'},{value:'小'},{value:'16G'}]},
{originalPrice:599,currentPrice:4499,inventory:66,img:'',sku:[{value:'绿色'},{value:'小'},{value:'64G'}]},
{originalPrice:599,currentPrice:4499,inventory:66,img:'',sku:[{value:'绿色'},{value:'中'},{value:'16G'}]},
{originalPrice:599,currentPrice:4499,inventory:66,img:'',sku:[{value:'绿色'},{value:'中'},{value:'64G'}]},
{originalPrice:55,currentPrice:66,inventory:4,img:'',sku:[{value:'灰色'},{value:'小'},{value:'16G'}]},
{originalPrice:55,currentPrice:66,inventory:3,img:'',sku:[{value:'灰色'},{value:'小'},{value:'64G'}]},
{originalPrice:55,currentPrice:76,inventory:56,img:'',sku:[{value:'灰色'},{value:'大'},{value:'16G'}]},
{originalPrice:55,currentPrice:76,inventory:56,img:'',sku:[{value:'灰色'},{value:'大'},{value:'64G'}]},
{originalPrice:55,currentPrice:76,inventory:56,img:'',sku:[{value:'灰色'},{value:'中'},{value:'16G'}]},
{originalPrice:55,currentPrice:76,inventory:56,img:'',sku:[{value:'灰色'},{value:'中'},{value:'64G'}]},
{originalPrice:55,currentPrice:66,inventory:9,img:'',sku:[{value:'红色'},{value:'小'},{value:'16G'}]},
{originalPrice:55,currentPrice:66,inventory:999,img:'',sku:[{value:'红色'},{value:'小'},{value:'64G'}]},
{originalPrice:55,currentPrice:66,inventory:0,img:'',sku:[{value:'红色'},{value:'中'},{value:'16G'}]},
{originalPrice:55,currentPrice:66,inventory:8,img:'',sku:[{value:'红色'},{value:'中'},{value:'64G'}]},
{originalPrice:55,currentPrice:76,inventory:9,img:'',sku:[{value:'红色'},{value:'大'},{value:'16G'}]},
{originalPrice:55,currentPrice:76,inventory:56,img:'',sku:[{value:'红色'},{value:'大'},{value:'64G'}]}],
activeIndex:0,
originalPrice:'',
currentPrice:'',
inventory:0,//库存
// 后端返回数据
skuName : [
{specificationname:'颜色',specificationvalue:[{value:'黑色'},{value:'绿色'},{value:'灰色'},{value:'红色'},]},
{specificationname:'型号',specificationvalue:[{value:'大'},{value:'小'},{value:'中'}]},
{specificationname:'内存',specificationvalue:[{value:'16G'},{value:'64G'}]}
],
}
},
onLoad() {
this.getPice()
},
methods: {
getPice(){
// 获取最大价格
const maxObj = this.getMaxValue(this.skuList, 'currentPrice');
// 最小价格
const minObj = this.getMinValue(this.skuList, 'originalPrice');
this.currentPrice = maxObj.currentPrice
this.originalPrice = minObj.originalPrice
},
getMaxValue(arr, prop) {
const max = Math.max(...arr.map(item => item[prop]));
return arr.find(item => item[prop] === max);
},
getMinValue(arr, prop) {
const min = Math.min(...arr.map(item => item[prop]));
return arr.find(item => item[prop] === min);
},
// 比较对象是否相等
shallowEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (!obj1 || !obj2) return false;
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (let key of keys1) {
if (obj1[key] !== obj2[key]) return false;
}
return true;
},
activeSku(i,is,b){
if(b.type == 'DOG'){
return
}
if(this.skuName[i].specificationvalue[is].type == true){
this.$set(this.skuName[i].specificationvalue[is],'type',false)
// 取消选中,清空价格
this.inventory = 0
this.getPice()
}else{
this.$set(this.skuName[i].specificationvalue[is],'type',true)
this.skuName[i].specificationvalue.forEach((item,ins)=>{
if(ins != is){
this.$set(this.skuName[i].specificationvalue[ins],'type',false)
}
})
}
let list = []
this.skuName.forEach((item,ins)=>{
item.specificationvalue.forEach((el,index)=>{
if(el.type == true){
list.push({value:el.value})
}
})
})
let vsku = []
let simplesku = this.skuList.filter(item => item.inventory == 0)
simplesku.forEach((el,l)=>{
el.sku.forEach((m,k)=>{
list.forEach((p,j)=>{
if(this.shallowEqual(p,JSON.parse(JSON.stringify(el.sku[i])))){ //找出符合条件的规格
vsku.push(...el.sku)
}
})
})
})
// 去重筛选的数组
let result = Array.from(new Set(vsku.map(item => JSON.stringify(item)))).map(item => JSON.parse(item));
// 清除当前层的索引
result.splice(i,1)
let num = 0
this.skuName.forEach((el,index)=>{
el.specificationvalue.forEach((s,k)=>{
if(s.type == 'DOG'){
s.type = false
}
if(result.length){
result.forEach((ei,z)=>{
if(ei.value == s.value ){
this.$set(this.skuName[index].specificationvalue[k],'type','DOG')
}
})
}else{
if(this.skuName[index].specificationvalue[k].type == 'DOG'){
this.$set(this.skuName[index].specificationvalue[k],'type',false)
}
}
if(s.type == true){
num += 1
}
})
})
this.skuList.forEach((el,index)=>{
if(JSON.stringify(list) == JSON.stringify(el.sku)){
this.originalPrice = el.originalPrice
this.currentPrice = el.currentPrice
this.inventory = el.inventory //库存
}
})
if(num != this.skuName.length){ //未选完整规格清空
this.inventory = 0
this.getPice()
}
},
}
}
</script>
<style lang="scss">
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
.money{
display: flex;
align-items: center;
color: red;
// margin-left: 30rpx;
margin-top: 30rpx;
text{
color: #999;
}
}
.flex-item{
width: 690rpx;
margin: auto;
margin-top: 30rpx;
.title{
font-size: 28rpx;
font-weight: bold;
margin: 20rpx 0rpx 20rpx 0rpx;
}
.f-v{
display: flex;
align-items: center;
flex-wrap: wrap;
.active_sku{
border: 1rpx solid #999;
padding: 3rpx 10rpx 3rpx 10rpx;
font-size: 24rpx;
margin-right: 10rpx;
border-radius: 5rpx;
}
.active_sku_color{
border: 1rpx solid #990002;
padding: 3rpx 10rpx 3rpx 10rpx;
font-size: 24rpx;
margin-right: 10rpx;
border-radius: 5rpx;
color: #990002;
}
.cucunSku{
border: 1rpx solid #990002;
padding: 3rpx 10rpx 3rpx 10rpx;
font-size: 24rpx;
margin-right: 10rpx;
border-radius: 5rpx;
color: #990002;
}
.wsku{
border: 1rpx solid #131399;
padding: 3rpx 10rpx 3rpx 10rpx;
font-size: 24rpx;
margin-right: 10rpx;
border-radius: 5rpx;
color: #464a99;
background-color: #131399;
}
}
}
</style>
更多推荐



所有评论(0)