小程序H5通用商品SKU小组件

277 阅读1分钟

本组件开发基本本身项目需求,通用只是指适用于需求类似的项目。具体可以直接看代码,就算用不上,希望也能提供对应的逻辑给你们。基于Uni框架,不同平台换个标签代码就好了

最终效果图如下:

好了,话不多说,直接上代码(回头有时间再讲解为什么这么写)

组件代码:

<template>
	<view class="sku-wrapper" @tap.stop>
		<view class="content">
			<scroll-view scroll-y="true">
				<view class="grace-product-attr-list" v-for="(item, index) in skuData" :key="index">
					<view class="title">{{ item.name }}</view>
					<view class="grace-select-tags">
						<view class="group">
							<view
								v-for="(i, sonIndex) in item.leaf"
								:data-isclick="i.is_click ? 0 : 1"
								:data-disabled="i.disabled ? 0 : 1"
								:data-value="i.id"
								:data-curindex="index"
								:key="sonIndex"
								@tap.stop="colorChange"
								:class="[i.disabled ? 'disabled' : i.is_click ? 'grace-checkeds' : '', 'label unchecked']"
							>
								{{ i.name }}
							</view>
						</view>
					</view>
				</view>
			</scroll-view>
		</view>
	</view>
</template>

<script>
export default {
	props: {
		// 规格的数据
		specData: {
			type: Array,
			default: () => []
		},
		// 规格组
		skuGroup: {
			type: Array,
			default: () => []
		}
	},
	data() {
		return {
			skuChooseId: [],
			currentSkuGroup: {
				name: '请选择商品规格'
			},
			skuData: [],
			skuGroupData: [],
			specIdArr: [],
			skuAllData: {}
		}
	},

	watch: {
		specData: {
			handler(newVal) {
				if (newVal.length !== 0 && this.skuGroup.length !== 0) {
					this.handleData()
				}
			},
			deep: true
		}
	},

	mounted() {
		if (this.specData.length !== 0 && this.skuGroup.length !== 0) {
			this.handleData()
		}
	},

	methods: {
		// 处理传入的规格数据
		handleData() {
			this.skuData = JSON.parse(JSON.stringify(this.specData))
			this.skuGroupData = JSON.parse(JSON.stringify(this.skuGroup))
			this.skuChooseId = []
			for (let i of this.skuGroupData) {
				let current = i.key.split('_')
				// 关联KEY只有一个时默认禁用库存为零的
				if (current.length == 1) {
					for (let j of this.skuData) {
						if (j.leaf.length == 1) {
							// 如果规格只有一个,则默认选择第一个
							setTimeout(() => {
								// 组件第一次挂载是拿不到值会报错
								this.colorChange({
									// 调用函数,模拟点击选择第一个
									value: j.leaf[0].id,
									isclick: 1,
									disabled: j.leaf[0].disabled ? 0 : 1,
									curindex: 0
								})
							}, 300)
						}
					}
				}
			}
		},

		// 点击选中的规格item
		colorChange(e) {
			let _this = this
			this.specIdArr = []
			this.currentSkuGroup = {}
			let parms = {}
			if (e.currentTarget) {
				parms = e.currentTarget.dataset
			} else {
				parms = e
			}
			let checkVal = String(parms.value)
			let isClick = parms.isclick
			let disabled = parms.disabled
			let curIndex = parms.curindex
			let speclength = _this.skuData.length // 获取规格总长度

			if (parms.disabled == 0) return

			for (let i of this.skuGroupData) {
				let current = i.key.split('_') // 关联当前规格的ID
				if (current.includes(checkVal)) {
					this.setSkuData(current, checkVal, false)
				}
			}
			for (let i = 0; i < _this.skuData[curIndex].leaf.length; i++) {
				if (checkVal == _this.skuData[curIndex].leaf[i].id) {
					let isClick = _this.skuData[curIndex].leaf[i].is_click;
					_this.skuData[curIndex].leaf[i].is_click = !isClick;
					if (isClick) {
						_this.skuChooseId[curIndex] = '';
					} else {
						_this.skuChooseId[curIndex] = Number(checkVal);
					}
					let skuChoose = _this.skuChooseId.filter(item => item);
					if (skuChoose.length == speclength) {
						// 当选中所有规格时候,遍历规格组取值
						let groupId = _this.skuChooseId.join('_');
						console.info('groupId=>', groupId);
						_this.skuGroupData.map((item, index) => {
							let thisArr = item.key.split('_');
							thisArr = thisArr.map((item, index) => {
								item = Number(item);
								return item;
							});
							const subtraction = _this.skuChooseId.concat(thisArr).filter(v => {
								return _this.skuChooseId.includes(v) ^ thisArr.includes(v);
							});
							// 若差集为空数组[],则两个数组一样
							if (subtraction.length == 0) {
								_this.currentSkuGroup = item; // 存储当前已选中的规格组的所有信息
							}
						});
					} else {
						_this.currentSkuGroup = {};
					}
				} else {
					_this.skuData[curIndex].leaf[i].is_click = false;
				}
			}

			_this.affirmSku();
		},

		// 设置SKU
		setSkuData(current, checkVal, flag) {
			this.skuData = this.skuData.map(i => {
				i.leaf.map(j => {
					// filter过滤自身ID,查找关联当前ID的规格ID是否存在
					if (current.filter(item => item != checkVal).includes(j.id.toString())) {
						// 当前规格ID不存在数据则设置&&未选择
						if (!this.specIdArr.includes(j.id) && !j.is_click) {
							j.disabled = flag;
						}
						// 防止覆盖
						this.specIdArr.push(j.id);
					}
					return j;
				});
				return i;
			});
			console.info('skuData', this.skuData);
		},

		// 确定规格
		affirmSku() {
			this.$emit('affirmSku', this.currentSkuGroup); // 选择结束后,抛出已选出来的规格数据
		}
	}
};
</script>

<style lang="scss" scoped>
.grace-product-attr-list {
	.title {
		font-size: 24rpx;
		margin-bottom: 20rpx;
		color: #333333;
	}

	.grace-select-tags {
		.group {
			width: 100%;
			display: flex;
			flex-wrap: wrap;

			.label {
				display: block;
				font-size: 24rpx;
				height: 60rpx;
				line-height: 60rpx;
				border-radius: 8rpx;
				padding: 0 20rpx;
				margin-bottom: 20rpx;
				margin-right: 20rpx;
				border: 2rpx solid transparent;
				color: #333;
				background-color: #f5f5f5;
				overflow: hidden;
			}

			.disabled {
				opacity: 0.4;
			}

			.grace-checkeds {
				color: #6d69f7;
				background-color: #f0efff;
				border: 2rpx solid #6d69f7;
			}
		}
	}
}
</style>

使用:(案例是商品详情,只贴使用的地方出来哦)

<!-- 规格sku -->
<view class="detail-sku bg-white margin-bottom-sm">
    <view class="detail-loading cu-load loading" v-if="skuLoading"></view>
    <sku v-on:affirmSku="affirmSku" :specData="specData" :skuGroup="skuGroup" v-else></sku>
</view>

<script>
import Sku from '@/components/shop/sku/sku.vue'

export default {
    components: {
        Sku
    },
    data() {
        return {
            detailData: null, // demo数据在下面
           skuLoading: true, // sku的loading加载
            specData: [],
            skuGroup: [],
        }
    },    methods: {
                // 获得最终选中规格的数据
		affirmSku(e) {
			if (this.selectSkuData && e.id !== this.selectSkuData.id) this.num = 1
			this.selectSkuData = e
			console.log('确定选择规格', e)
		},
                // 获得sku规格数据进行处理
		getSpec() {
			let specData = this.detailData.spec
			if (specData && specData.length > 0) {
				specData.forEach((spec_item, index) => {
					spec_item.leaf.forEach(item => {
						item.is_click = false
					})
				})
			}
			this.specData = specData
			this.skuGroup = this.detailData.sku // 规格组数据
			this.skuLoading = false
		},
                // 获取商品详情
		getDetail() {
                    // 这里写自己的详情请求,然后进行sku数据处理
                    this.getSpec()
                }
    }
}
</script>

最后贴上这个组件所需要的数据格式

detailData: {
     "spec":[            {                "id":1,                "name":"大小",                "sort":0,                "leaf":[                    {                        "id":1,                        "goods_id":1,                        "goods_spec_id":1,                        "goods_spec_name":"大小",                        "name":"大",                        "is_click":0                    },                    {                        "id":5,                        "goods_id":1,                        "goods_spec_id":1,                        "goods_spec_name":"大小",                        "name":"中",                        "is_click":0                    },                    {                        "id":6,                        "goods_id":1,                        "goods_spec_id":1,                        "goods_spec_name":"大小",                        "name":"小",                        "is_click":0                    }                ]            },            {                "id":3,                "name":"甜度",                "sort":0,                "leaf":[                    {                        "id":2,                        "goods_id":1,                        "goods_spec_id":3,                        "goods_spec_name":"甜度",                        "name":"10",                        "is_click":0                    },                    {                        "id":3,                        "goods_id":1,                        "goods_spec_id":3,                        "goods_spec_name":"甜度",                        "name":"7",                        "is_click":0                    },                    {                        "id":4,                        "goods_id":1,                        "goods_spec_id":3,                        "goods_spec_name":"甜度",                        "name":"5",                        "is_click":0                    }                ]            }        ],        "sku":[            {                "id":18,                "store_id":2,                "goods_id":4,                "key":"1_2",                "name":"大小:大 甜度:10 ",                "price":30,                "supply_price":20,                "admin_goods_id":1,                "admin_sku_id":1            },            {                "id":19,                "store_id":2,                "goods_id":4,                "key":"1_3",                "name":"大小:大 甜度:7 ",                "price":30,                "supply_price":20,                "admin_goods_id":1,                "admin_sku_id":2            },            {                "id":20,                "store_id":2,                "goods_id":4,                "key":"1_4",                "name":"大小:大 甜度:5 ",                "price":30,                "supply_price":20,                "admin_goods_id":1,                "admin_sku_id":3            },            {                "id":21,                "store_id":2,                "goods_id":4,                "key":"5_2",                "name":"大小:中 甜度:10 ",                "price":30,                "supply_price":20,                "admin_goods_id":1,                "admin_sku_id":4            },            {                "id":22,                "store_id":2,                "goods_id":4,                "key":"5_3",                "name":"大小:中 甜度:7 ",                "price":30,                "supply_price":20,                "admin_goods_id":1,                "admin_sku_id":5            },            {                "id":23,                "store_id":2,                "goods_id":4,                "key":"5_4",                "name":"大小:中 甜度:5 ",                "price":30,                "supply_price":20,                "admin_goods_id":1,                "admin_sku_id":6            },            {                "id":24,                "store_id":2,                "goods_id":4,                "key":"6_2",                "name":"大小:小 甜度:10 ",                "price":30,                "supply_price":20,                "admin_goods_id":1,                "admin_sku_id":7            },            {                "id":25,                "store_id":2,                "goods_id":4,                "key":"6_3",                "name":"大小:小 甜度:7 ",                "price":30,                "supply_price":20,                "admin_goods_id":1,                "admin_sku_id":8            },            {                "id":26,                "store_id":2,                "goods_id":4,                "key":"6_4",                "name":"大小:小 甜度:5 ",                "price":30,                "supply_price":20,                "admin_goods_id":1,                "admin_sku_id":9            }        ],
}