uniapp 购物弹窗组件 (微信小程序)
效果图,暂时只适应单规格,居中弹出和下方弹出,如需求不满足,请自行修改代码
(更新于24/11/15)
居中显示效果
下方弹出效果
html
<template>
<view class="" v-if="show"
:class="mode=='center'?(style_show?'specifications_show_center openAnimation':'specifications_show_center closeAnimation'):mode=='bottom'?(style_show?'specifications_show_bottom openAnimation':'specifications_show_bottom closeAnimation'):''"
@tap.stop="()=>{}">
<view class="specifications"
:class="mode=='bottom'?(style_show?'specifications_bottom_open ':'specifications_bottom_close '):''"
@tap.stop="()=>{}">
<view class="specification">
<view class="specification_title">请选择规格</view>
<image class="colseIcon"
:src="$publicfun.locaAndHttp()?'../../static/all/del.png':$publicfun.httpUrlImg('colse.png')"
mode="aspectFit" @tap.stop="colosePopue"></image>
<!-- 商品信息 -->
<view class="modal-header flex ">
<view class="header-left ss-m-r-30">
<image class="sku-image" :src="component_goodsDetail.image" mode="aspectFill"
@tap.stop="previewImages1(component_goodsDetail.image,1)">
</image>
</view>
<view class="header-right ">
<view class="goods-title ss-line-2">{{component_goodsDetail.title||'商品名称' }}</view>
<view class="header-right-bottom ss-flex ss-col-center ss-row-between">
<view class="price-text">
{{is_kongobj=='{}'? Number(component_goodsDetail.price*1).toFixed(2) :Number(chooseSpecificationObject.price*1).toFixed(2)}}
</view>
<view class="stock-text ss-m-l-20" v-if="is_kongobj!='{}'">
库存{{ is_kongobj=='{}'?component_goodsDetail.goods_stock:chooseSpecificationObject.inventory}}件
</view>
</view>
</view>
</view>
<view class="specification_title2">
规格
</view>
<view class="specification_title_1">
<scroll-view scroll-y="true" style="height: 100%;">
<view>
<height :hg='30'></height>
<view class="flex flexwrap">
<view v-for="(itemall,indexall) in component_goodsDetail.multiplejson" :key="indexall"
@click.stop="setguigeindex(indexall,itemall)">
<view
:class="guigeindex==indexall?'specification_title_1_title specification_title_1_titlec':'specification_title_1_title'"
v-if='itemall.title'>
{{itemall.title}}
</view>
</view>
</view>
<!-- 单规格 -->
<view class="mt30" v-if="false">
<view class="specification_title_1_content">
<view class="specification_title_1_content_flex flex jc ac"
:class="guigeindex2==index?'specification_title_1_content_flex_activate':''"
v-for="(item,index) in specification_list[guigeindex].item" :key="index"
@tap.stop="selective_specification(item.guigechildren,item)">
<!-- 可用于显示商品图片 -->
<!-- <image class="image" :src="sheep.$url.cdn(type_return_img(item.id).url)"
mode="aspectFill" v-if="type_return_img(item.id).url!=''">
</image> -->
<view>{{item.name}}</view>
</view>
<view class="nogoods" v-if="item.guigechildren.length==0" style="margin: auto;">
<u-empty text='没有规格~' icon="">
</u-empty>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<view class="" v-if="mode=='center'">
<view class="selected">
<text>已选规格:</text>
{{is_kongobj=='{}'?'未选择':chooseSpecificationObject.title}}
</view>
</view>
<view class="buy-num-box flex jsb ac">
<view class="label-text">购买数量</view>
<view class="ShopCar flex jc ac">
<image class="ShopCaricon"
:src="$publicfun.locaAndHttp()?'../../static/all/del.png':$publicfun.httpUrlImg('del.png')"
mode="aspectFit" @tap.stop="delnum(item)"></image>
<input type="number" v-model="goodsNum" class="ShopCarInp" @blur="inpnum(item)" />
<image class="ShopCaricon"
:src="$publicfun.locaAndHttp()?'../../static/all/add.png':$publicfun.httpUrlImg('add.png')"
mode="aspectFit" @tap.stop="addnum(item)"></image>
</view>
</view>
<view class="sublist" v-if="mode=='center'">
<view class="sublist_left">
<view class="">
<text>总计 </text>
<text class="TotalPrices">
<text>¥</text>
{{is_kongobj=='{}'?'0.00':Number(chooseSpecificationObject.price*goodsNum).toFixed(2) }}
</text>
</view>
</view>
<view class="sublist_right" @tap.stop="goodsAddCar()">
<text>+</text> <text>加入购物车</text>
</view>
</view>
<view class="specification_bottombuts" v-if="mode=='bottom'">
<view class="specification_bottombuts_but specification_bottombuts_ShoppingCart"
@tap.stop="goodsAddCar()">
加入购物车
</view>
<view class="specification_bottombuts_but specification_bottombuts_PayNow" @tap.stop="newPay()">
立即购买
</view>
</view>
<view class="" style="width: 100%;height: 30px;" v-if="mode=='bottom'">
</view>
</view>
</view>
</view>
</template>
js
export default {
name: "specification",
//style_show 主要控制动画效果
//mode 设置方向弹出
//show 控制显示不显示
//goodsDetail 传入的商品信息
props: ['show', 'style_show', 'goodsDetail', 'mode'],
data() {
return {
orderprive: '',
style: [], //规格动态样式
// goodsDetail: { //模拟商品信息
// goods_name: '番茄鸡蛋面',
// goods_price: '9.90',
// goods_stock: 999,
// specification: [{
// goods_name: '规格1',
// goods_price: '9.90',
// goods_stock: 999,
// children: [{
// goods_name: '11',
// goods_price: '6.60',
// goods_stock: 999,
// }]
// }]
// },
chooseSpecificationObject: {}, //选中的规格
guigeindex: 0, //一级规格
guigeindex2: 0, //二级规格
component_goodsDetail: {}, //请求接口的商品数据
goodsNum: 1,
};
},
onLoad() {
},
watch: {
//监听是否有商品id传入进来
goodsDetail: {
handler(newName, oldName) {
//获取商品信息的接口
GoodsDetail({
id: newName.id
}).then(res => {
this.component_goodsDetail = res.data;
this.chooseSpecificationObject = res.data ? res.data.multiplejson[0] : {};
this.guigeindex = 0;
console.log(res, '商品详情');
})
this.goodsNum = 1;
},
immediate: true,
deep: true
}
},
onShow() {
},
computed: {
//判断是否选中规格
is_kongobj() {
console.log(this.chooseSpecificationObject);
return JSON.stringify(this.chooseSpecificationObject);
}
},
methods: {
newPay() {
if (!this.style_show) {
return
}
let that = this;
//可用于显示弹窗不默认选中规格的判断
// if (JSON.stringify(this.choose_specification) == '{}') {
// uni.showToast({
// title: "请选择规格",
// icon: "none"
// })
// return
// }
// console.log(that.goodsNum);
// return
// 直接购买将需要的信息提交到vuex缓存中,用于渲染页面
this.$store.commit('set_is_immediately', {
type: 'goodsDetail',
id: that.component_goodsDetail.id, //id
item: that.chooseSpecificationObject, //规格名称
count: that.goodsNum,
goodsDetail: [that.component_goodsDetail] //商品列表数组
})
//跳转到提交订单页面
this.$publicfun.navigateTo('/pagesA/SubmitOrder/SubmitOrder')
this.colosePopue()
},
//选中规格
setguigeindex(index, item) {
if (!this.style_show) {
return
}
this.chooseSpecificationObject = item;
this.guigeindex = index;
if (this.goodsNum > this.chooseSpecificationObject.inventory) {
this.goodsNum = this.chooseSpecificationObject.inventory;
}
},
//删除购物车数量
delnum() {
if (!this.style_show) {
return
}
if (this.goodsNum > 1) {
this.goodsNum--;
} else {
//数量少于1 关闭弹窗
this.colosePopue()
}
},
//添加购物车数量
addnum() {
if (!this.style_show) {
return
}
if (this.goodsNum < this.chooseSpecificationObject.inventory) {
this.goodsNum++;
} else {
uni.showToast({
title: '超出购买数量~',
icon: "none"
})
}
},
//手动输入购买数量
inpnum() {
if (!this.style_show) {
return
}
if (this.goodsNum > this.chooseSpecificationObject.inventory) {
this.goodsNum = this.chooseSpecificationObject.inventory;
}
if (this.goodsNum == '') {
// this.colosePopue()
this.goodsNum = 1;
this.$publicfun.showToast('购买数量不得少于1~')
}
if (this.goodsNum <= 0) {
// this.colosePopue()
this.goodsNum = 1;
this.$publicfun.showToast('购买数量不得少于1~')
}
},
//预览商品图片
previewImages1(url, index) {
//因为动画效果是 用透明度为0,再消失,透明度改变的时候还可以点击,加这个判断是很必要的
if (!this.style_show) {
return
}
//预览图片
this.$publicfun.previewImage({
url: url,
index: index
})
},
//关闭弹窗
colosePopue() {
if (!this.style_show) {
return
}
this.$emit('closeCar')
// this.component_goodsDetail={};
},
//添加购物车
goodsAddCar() {
if (!this.style_show) {
return
}
let that = this;
//这是添加到购物车
carAddAndDel({
id: this.goodsDetail.id,
count: this.goodsNum,
item_name: this.chooseSpecificationObject.title,
type: 'add'
}).then(res => {
if (res.code == 1) {
//通过vuex更新数据
this.$store.dispatch('fun_set_shopCar');
// console.log(res);
that.colosePopue();
uni.showToast({
title: '已添加到购物车~',
icon: "none"
})
} else {
uni.showToast({
title: res.msg,
icon: "none"
})
}
})
},
}
}
css部分
//居中显示
.specifications_show_center {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 999999999;
background: rgba(0, 0, 0, 0.6);
display: flex;
justify-content: center;
align-items: center;
opacity: 1;
border-radius: 18rpx;
}
.openAnimation {
animation: slow_show 0.3s linear;
}
.closeAnimation {
opacity: 0 !important;
animation: close_slow_show 0.3s linear;
}
@keyframes slow_show {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes close_slow_show {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
//从下方弹出显示
.specifications_show_bottom {
width: 100vw;
height: 100vh;
position: fixed;
bottom: 0;
left: 0;
z-index: 999999999;
background: rgba(0, 0, 0, 0.6);
display: flex;
justify-content: center;
align-items: flex-end;
opacity: 1;
border-radius: 18rpx;
.specifications {
width: 100% !important;
position: absolute;
bottom: 0;
left: 0;
padding: 30rpx 50rpx !important;
}
.specifications_bottom_open {
animation: From_bottom_to_top 0.22s linear;
}
.specifications_bottom_close {
animation: From_top_to_bottom 0.3s linear;
}
}
@keyframes From_bottom_to_top {
0% {
bottom: -100%;
}
100% {
bottom: 0;
}
}
@keyframes From_top_to_bottom {
0% {
bottom: 0;
}
100% {
bottom: -100%;
}
}
//以上是动画效果
.specifications {
width: 690rpx;
padding: 30rpx;
box-sizing: border-box;
background: #fff;
border-radius: 18rpx;
.buy-num-box {
height: 100rpx;
// padding-left: 20rpx;
// box-sizing: border-box;
.ShopCar {
.ShopCaricon {
width: 46rpx;
height: 46rpx;
}
.ShopCarInp {
width: 100rpx;
height: 46rpx;
text-align: center;
}
}
}
.modal-header {
// position: relative;
padding: 20rpx 0;
margin-top: 20rpx;
.sku-image {
width: 160rpx;
height: 160rpx;
border-radius: 10rpx;
background: darkslategray;
}
.header-right {
width: calc(100% - 160rpx - 20rpx);
height: 160rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
margin-left: 20rpx;
}
.close-icon {
position: absolute;
top: 10rpx;
right: 20rpx;
font-size: 46rpx;
opacity: 0.2;
}
.goods-title {
font-size: 28rpx;
font-weight: bold;
line-height: 42rpx;
position: relative;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
.tig {
border: 2rpx solid #ff6000;
border-radius: 4rpx;
width: 126rpx;
height: 38rpx;
position: absolute;
left: 0;
top: 0;
.tig-icon {
width: 40rpx;
height: 40rpx;
background: #ff6000;
margin-left: -2rpx;
border-radius: 4rpx 0 0 4rpx;
.groupon-tag {
width: 32rpx;
height: 32rpx;
}
}
.tig-title {
font-size: 24rpx;
font-weight: 500;
line-height: normal;
color: #ff6000;
width: 86rpx;
display: flex;
justify-content: center;
align-items: center;
}
}
.info-title {
text-indent: 132rpx;
}
}
.header-right-bottom {
display: flex;
justify-content: space-between;
align-items: center;
}
.price-text {
font-size: 30rpx;
font-weight: 500;
color: red;
font-family: OPPOSANS;
&::before {
content: '¥';
font-size: 24rpx;
}
}
.stock-text {
font-size: 26rpx;
color: #999999;
}
}
.d_box {
.d_box_item {
font-size: 34rpx;
color: #3d3d3d;
width: 45%;
margin-bottom: 20rpx;
}
}
.specification {
background: #FFFFFF;
border-radius: 12rpx;
position: relative;
z-index: 10074;
.specification_title {
text-align: center;
font-size: 38rpx;
font-weight: 600;
color: #353535;
margin: 0 auto;
}
.colseIcon {
width: 40rpx;
height: 40rpx;
position: absolute;
right: 0rpx;
top: 0rpx;
}
.specification_title2 {
font-size: 38rpx;
font-weight: 600;
color: #353535;
margin: 0 auto;
margin-bottom: 30rpx;
}
.specification_goods_title {
font-size: 38rpx;
font-weight: 600;
}
.specification_title_1 {
width: 100%;
max-height: 400rpx !important;
margin: 0 auto;
.specification_title_1_title {
font-size: 32rpx;
font-weight: 600;
color: #676767;
// color: $AccentColor;
display: inline-block;
padding: 10rpx 20rpx;
text-align: center;
background: #fff;
border-radius: 18rpx;
white-space: nowrap;
// border: 1rpx solid $AccentColor !important;
border: 1rpx solid #676767 !important;
margin-right: 10rpx;
margin-bottom: 10rpx;
}
.specification_title_1_titlec {
color: $AccentColor !important;
// background: $AccentColor !important;
border: 1rpx solid $AccentColor !important;
}
.specification_title_1_content {
display: flex;
flex-wrap: wrap;
// overflow-x: auto;
display: -webkit-box;
-webkit-overflow-scrolling: touch;
.specification_title_1_content_flex_activate {
background: $AccentColor !important;
border: 1rpx solid $AccentColor !important;
color: #fff !important;
}
.font_sizi_1 {
color: $AccentColor;
}
.font_sizi_2 {
border-left: 1rpx solid $AccentColor;
}
.specification_title_1_content_flex {
// height: 63rpx;
background: #FFFFFF;
border-radius: 14rpx;
border: 1rpx solid #999;
margin-right: 20rpx;
text-align: center;
// line-height: 63rpx;
font-size: 28rpx;
font-weight: 400;
color: #343434;
padding: 10rpx 20rpx;
margin-bottom: 20rpx;
.image {
width: 60rpx;
height: 60rpx;
margin-right: 20rpx;
border-radius: 12rpx;
}
}
}
}
.close {
position: absolute;
bottom: -150rpx;
left: 50%;
transform: translateX(-50%);
}
}
.selected {
width: 100%;
padding: 20rpx 0;
background: #F5F5F5;
margin-top: 60rpx;
text {
font-size: 24rpx;
font-weight: 400;
color: #363636;
}
text:nth-child(1) {
font-size: 24rpx;
font-weight: 400;
color: #676767;
padding: 0 20rpx;
margin-left: 10rpx;
}
}
.sublist {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 30rpx;
// padding: 30rpx 40rpx;
.sublist_left {
.TotalPrices {
margin-left: 10rpx;
}
text:nth-child(1) {
font-size: 28rpx;
font-weight: 600;
color: #363636;
}
text:nth-child(2) {
font-size: 28rpx;
font-weight: 600;
color: #FF0000;
text:nth-child(1) {
font-size: 28rpx;
font-weight: 600;
color: #FF0000;
font-size: 24rpx;
}
}
}
.sublist_right {
width: 234rpx;
height: 62rpx;
background: $AccentColor;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 500;
color: #F3FCFF;
line-height: 62rpx;
text-align: center;
}
}
//从下方弹出的弹窗按钮
.specification_bottombuts {
width: 100%;
margin: auto;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20rpx;
.specification_bottombuts_but {
width: 50%;
padding: 25rpx 20rpx;
font-size: 28rpx;
font-weight: bold;
color: #fff;
text-align: center;
}
.specification_bottombuts_ShoppingCart {
background: #9dceff;
border-radius: 99px 0 0 99px;
}
.specification_bottombuts_PayNow {
background: #0a84ff;
border-radius: 0px 99px 99px 0px;
}
}
.minus {
width: 25px;
height: 25px;
border-width: 1px;
border-color: #E6E6E6;
border-style: solid;
border-top-left-radius: 100px;
border-top-right-radius: 100px;
border-bottom-left-radius: 100px;
border-bottom-right-radius: 100px;
@include flex;
justify-content: center;
align-items: center;
}
.input {
padding: 0 10px;
}
.plus {
width: 25px;
height: 25px;
border-width: 1px;
background-color: #00B8FB;
border-color: #00B8FB;
border-style: solid;
border-top-left-radius: 100px;
border-top-right-radius: 100px;
border-bottom-left-radius: 100px;
border-bottom-right-radius: 100px;
@include flex;
justify-content: center;
align-items: center;
}
}