VUE类似淘宝选择商品多规格(库存判断)

1.组件效果展示

也可访问链接查看网页效果... 


 后面又用uni-app写过一次,比这次稍微清晰一些

 uni-app类似淘宝选择商品多规格(库存判断)

瞎封装组件系列:

VUE简单提示框

VUE树形图(递归实现)

VUE多店铺购物车

 2.使用方法

引入组件:

import goodsspec from '../../../../components/ghGoodsSpec/ghGoodsSpec.vue'

在<template>合适的区域使用

<goodsspec :goods="goods" :isShow="modalIsShow" @closeModal="closeModal"></goodsspec>

组件规定需要从父组件传商品规格信息的JSON,和一个布尔值,控制规格选择弹出框显示状态。

商品规格信息JSON必须符合组件规定标准(下面是例子中的JSON,库存的json,规格json):


modalIsShow:false,
goods: {
	defaultimg:'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3457897174,1250176029&fm=26&gp=0.jpg',
	"priceInfo": [{
			"SKU": "1_1_5_11",
			"stock": 888,
			"price": 15535400.00,
			"difference": "黑色;34;帆布鞋",
			goodsimg:'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3323728883,1529837998&fm=26&gp=0.jpg'
		}, {
			"SKU": "1_3_5_12",
			"stock": 99,
			"price": 100.00,
			"difference": "黑色;34;凉鞋",
			goodsimg:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1006940732,1295991734&fm=26&gp=0.jpg'
		}, {
			"SKU": "1_4_6_13",
			"stock": 150,
			"price": 10011.00,
			"difference": "粉色;36;运动鞋",
			goodsimg:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1916398967,1152344977&fm=26&gp=0.jpg'
		},
		{
			"SKU": "1_3_5_12",
			"stock": 9999,
			"price": 100.00,
			"difference": "红色;35;凉鞋",
			goodsimg:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2755143450,1175568520&fm=26&gp=0.jpg'
		}, {
			"SKU": "1_4_5_13",
			"stock": 100000,
			"price": 10088.00,
			"difference": "粉色;35;运动鞋",
			goodsimg:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3305162035,1457776276&fm=26&gp=0.jpg'
		}
	],
	"SKUInfo": [{
		"name": "颜色",
		"sort": 1,
		"items": [{
				"name": "黑色",
				"value": 1,
				"sort": 1
			},
			{
				"name": "红色",
				"value": 3,
				"sort": 2
			},
			{
				"name": "粉色",
				"value": 4,
				"sort": 3
			}
		]
	}, {
		"name": "尺码",
		"sort": 2,
		"items": [{
				"name": "35",
				"value": 5,
				"sort": 1
			},
			{
				"name": "36",
				"value": 6,
				"sort": 2
			},
			{
				"name": "34",
				"value": 7,
				"sort": 3
			}
		]
	}, {
		"name": "分类",
		"sort": 4,
		"items": [{
				"name": "帆布鞋",
				"value": 11,
				"sort": 1
			},
			{
				"name": "凉鞋",
				"value": 12,
				"sort": 2
			},
			{
				"name": "运动鞋",
				"value": 13,
				"sort": 3
			}
		]
	}]
}

3.代码实现

实现原理:

点击规格按钮时,把所选规格装到一个数组中,最后把组合的规格代入商品数量的json中查找。

<template>
	<div class="ui-shade" v-show="isShow"  @click="changisshow">
		<div class="modal_cont" :class="isShow ? 'silderout':''" @click.stop="changisshow()">
			<div class="page_modal" @click.stop>
				<div id="geuige">
					<div class="cose_modal" @click="changisshow()"><span>×</span></div>
					<div class="goods_intro flex-star">
						<img :src="defaultimg">
						<div class="goods_info">
							<div class="goods_price">{{price}}</div>
							<div class="kucun">库存<span>{{num}}</span>件</div>
							<div class="checkre flex-star">
								<span>已选:</span>
								<div>{{showAttr}}</div>
							</div>
						</div>
					</div>

					<div v-for="(item,itemindex) in goods.SKUInfo" class="chose_item">
						<p class="chose_item_tit">{{item.name}}</p>
						<div class="chose_item_cont flex-star">
							<div v-for="(attr,attrindex) in item.items" v-on:click="specificationBtn(attr.name,itemindex,attrindex)"
							 v-bind:class="[attr.isShow?'':'noneActive',subIndex[itemindex] == attrindex?'productActive':'']">
								{{attr.name}}
							</div>
						</div>
					</div>

					<div class="num_opt flex-between">
						<span>购买数量</span>
						<div class="numopt flex-star">
							<div class="add">-</div>
							<div>2</div>
							<div class="reduce">+</div>
						</div>
					</div>
					<div class="btn_cont flex-star">
						<input type="button" name="" id="" value="加入购物车" />
						<input type="button" name="" id="" value="马上抢" />
					</div>
				</div>
			</div>
		</div>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				selectArr: [], //存放被选中的值
				showAttr: "",
				num: 0,
				shopItemInfo: {}, //存放要和选中的值进行匹配的数据
				subIndex: [], //是否选中 因为不确定是多规格还是单规格,所以这里定义数组来判断
				price: '', //选中规格的价钱
        defaultimg:''
			}
		},
		props:{
			isShow:{
				type:Boolean,
				default:false
			},
			goods:{
				type:Object,
				required:true
			}
		},
		created: function() {
			var self = this;
			for (var i in self.goods.priceInfo) {
				self.shopItemInfo[self.goods.priceInfo[i].difference] = self.goods.priceInfo[i]; //修改数据结构格式,改成键值对的方式,以方便和选中之后的值进行匹配
			}
      this.defaultimg = this.goods.defaultimg;
      console.log(this.defaultimg,66666);
			self.checkItem();
			// self.specificationBtn("黑色",0,0)
		},
		methods: {
			changisshow(e) {
				this.$emit("closeModal",false)
			},
			specificationBtn: function(item, n, index) {
				var self = this;

				console.info(self.selectArr[n] + "66666" + item)
				if (self.selectArr[n] != item) {
					self.selectArr[n] = item;
					self.subIndex[n] = index;

				} else {
					self.selectArr[n] = undefined;
					self.subIndex[n] = -1; //去掉选中的颜色
				}

				var showarr = self.selectArr;
				var tempTop = [];
				showarr.forEach((item, i, arr) => {
					if (item) {
						tempTop.push(item)
					}
				})

				self.showAttr = tempTop.join(";");
				self.checkItem();
				tempTop = []
			},
			checkItem: function() {
				var self = this;
				var option = self.goods.SKUInfo;
				var result = []; //定义数组存储被选中的值
				for (var i in option) {
					result[i] = self.selectArr[i] ? self.selectArr[i] : '';
				}
				console.log(result);

				if (self.shopItemInfo[result.join(";")]) {
					self.num = self.shopItemInfo[result.join(";")].stock;
					self.price = "¥" + (self.shopItemInfo[result.join(";")].price).toFixed(2);
          self.defaultimg = self.shopItemInfo[result.join(";")].goodsimg;
				} else {
					self.num = 0;
					self.price = "¥0.00";
          self.defaultimg = self.goods.defaultimg;
				}


				for (var i in option) {
					var last = result[i]; //把选中的值存放到字符串last去
					for (var k in option[i].items) {
						result[i] = option[i].items[k].name; //赋值,存在直接覆盖,不存在往里面添加name值
						option[i].items[k].isShow = self.isMay(result); //在数据里面添加字段isShow来判断是否可以选择
					}

					console.info(last)
					result[i] = last; //还原,目的是记录点下去那个值,避免下一次执行循环时避免被覆盖
				}
				self.$forceUpdate(); //重绘
			},
			isMay: function(result) {
				for (var i in result) {
					if (result[i] == '') {
						return true; //如果数组里有为空的值,那直接返回true
					}
				}
				if (this.shopItemInfo[result.join(";")]) { //匹配选中的数据的库存,若不为空返回true反之返回false
					return this.shopItemInfo[result.join(";")].stock == 0 ? false : true;
				}
			}
		}
	}
</script>

<style scoped="scopedss">
	.page_modal {
		width: 100%;
		background: linear-gradient(to right, #fff, #fff) 0 .2rem no-repeat;
		background-size: 100% auto;
		color: #222;
		position: absolute;
		bottom: 0;
	}

	.goods_intro {
		padding: 0 .25rem;
	}

	.goods_intro img {
		width: 2rem;
		height: 2rem;
		border: 3px solid #fff;
		border-radius: 4px;
		box-shadow: 0 0 6px rgba(0, 0, 0, .3);
	}

	.goods_info {
		margin-left: .15rem;
	}

	.goods_price {
		color: #feab27;
		font-size: .34rem;
	}

	.kucun {
		margin: .05rem 0;
	}

	.num_opt {
		width: calc(100% - .5rem);
		margin: 0 .25rem;
		padding: .25rem 0;
		border-bottom: 1px solid #eeeeee;
	}

	.numopt div {
		width: .7rem;
		height: .6rem;
		background: #f5f5f5;
		text-align: center;
		line-height: .6rem;
		color: #666;
	}

	.numopt div:nth-child(2) {
		margin: 0 .08rem;
		background: white;
		color: #333;
	}

	.chose_item {
		width: calc(100% - .5rem);
		margin: 0 .25rem;
		border-bottom: 1px solid #eeeeee;
		padding-top: .3rem;
	}

	.chose_item_cont {
		margin-top: .2rem;
	}

	.chose_item_cont div {
		padding: .12rem .24rem;
		background: #f6f4f5;
		border-radius: 5px;
		margin-bottom: .2rem;
	}

	.chose_item_cont div:nth-child(n + 2) {
		margin-left: .2rem;
	}

	.chose_item .chose_item_tit {
		font-size: .3rem;
		font-weight: bold;
	}

	.ui-shade {
		display: flex;
	}

	.silderout {
		display: block;
		bottom: 0;
		animation: silderout .25s;
	}

	.btn_cont input {
		height: .8rem;
		width: 50%;
		border: none;
		color: white;
	}

	.btn_cont input:nth-child(1) {
		background: #ff9e01;
		border-radius: .8rem 0 0 .8rem;
	}

	.btn_cont input:nth-child(2) {
		background: #ff6f00;
		border-radius: 0 .8rem .8rem 0;
	}

	.btn_cont {
		width: calc(100% - .5rem);
		margin: 0 .25rem;
		padding: .15rem 0;
	}

	.productActive {
		background: #feab27 !important;
		color: white;
	}

	.cose_modal {
		position: absolute;
		width: .4rem;
		top: .4rem;
		height: .4rem;

		border: 1px solid #615763;
		border-radius: 50%;
		right: .2rem;
		line-height: .34rem;
		text-align: center;
	}

	@keyframes silderout {
		from {
			bottom: -100vh;
		}

		to {
			bottom: 0;
		}
	}

	@keyframes silderin {
		from {
			bottom: vh;
		}

		to {
			bottom: -100vh;
		}
	}

	.modal_cont {
		position: absolute;
		height: 100vh;
		width: 100vw;
	}

	.noneActive {
		background-color: #ccc;
		opacity: 0.4;
		color: #000;
		pointer-events: none;
	}
</style>

写的不好,多多指教。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值