积分商城功能完善

This commit is contained in:
2025-12-10 19:33:29 +08:00
parent 6b53d30f69
commit 87f2ee8c3d
12 changed files with 1268 additions and 589 deletions

View File

@@ -1,267 +1,406 @@
<template>
<view class="color-333 u-font-28">
<view class="top" :style="{ backgroundImage: 'url(' + imgs.bg + ')' }">
<view class="name"> 这里是优惠券名称啊啊啊啊 </view>
<view class="info"> 15元代金券满200可用 </view>
</view>
<view class="sku">
<view>
<view>
<text class="price">800</text>
<text class="text">积分</text>
<text class="price">+</text>
<text class="price">9.99</text>
<text class="text"></text>
</view>
<view class="u-m-t-16 text"> 限购2份 </view>
</view>
<text class="text">剩余99999</text>
</view>
<view class="goods-name">这里是商品名称啊啊啊啊啊</view>
<view class="bg-f7" style="height: 24rpx"></view>
<view class="desc">
<view class="u-flex">
<view class="color-666 no-wrap">领取方式</view>
<view class="u-m-l-16">系统发放</view>
</view>
<view class="u-flex u-m-t-16 u-col-baseline">
<view class="color-666 no-wrap">特别说明</view>
<view class="u-m-l-16"
>优惠券为虚拟发放一旦兑换不支持退款请悉知</view
>
</view>
</view>
<view class="bg-f7" style="height: 24rpx"></view>
<view class="color-333 u-font-28" v-if="item.id">
<template v-if="item.goodsCategory=='优惠券'">
<view class="top" :style="{ backgroundImage: 'url(' + imgs.bg + ')' }">
<view class="name u-line-1"> {{item.couponInfo.title}} </view>
<view class="info"> {{returnCouponTypeName}} </view>
</view>
</template>
<template v-else>
<image class="top-img" :src="item.goodsImageUrl" mode="aspectFit"></image>
<view class="goods-detail">
<view class="u-flex u-row-center">
<view class="title">商品详情</view>
</view>
</view>
</template>
<view class="sku">
<view>
<view>
<text class="price">{{item.requiredPoints}}</text>
<text class="text">积分</text>
<template v-if="item.extraPrice">
<text class="price">+</text>
<text class="price">{{item.extraPrice}}</text>
<text class="text"></text>
</template>
<view style="height: 72px"></view>
<view class="fixed-bottom u-flex u-row-center">
<view class="btn" @click="exchangeClick">立即兑换</view>
</view>
</view>
<view class="u-m-t-16 text" v-if="item.limitQuota"> 限购{{item.limitQuota}} </view>
</view>
<text class="text">剩余{{item.quantity}}</text>
</view>
<view class="goods-name">{{item.goodsName}}</view>
<view class="bg-f7" style="height: 24rpx"></view>
<view class="desc">
<view class="u-flex">
<view class="color-666 no-wrap">领取方式</view>
<view class="u-m-l-16" v-if="item.goodsCategory=='优惠券'">系统发放</view>
<view class="u-m-l-16" v-else>店内自取</view>
</view>
<view class="u-flex u-m-t-16 u-col-baseline" v-if="item.goodsCategory=='优惠券'">
<view class="color-666 no-wrap">特别说明</view>
<view class="u-m-l-16">优惠券为虚拟发放一旦兑换不支持退款请悉知</view>
</view>
</view>
<view class="bg-f7" style="height: 24rpx"></view>
<!-- 兑换确认弹窗start -->
<modal
v-model="modalData.show"
title="立即兑换确认"
@confirm="confirmExchange"
>
<view style="padding: 32rpx 50rpx">
<view class="u-font-32"
>您将消耗{800}积分兑换商品{这里是商品名称}是否确认兑换</view
>
<view class="u-m-t-20 waring u-flex u-font-32 u-col-center">
<up-icon name="info-circle" size="16" color="#FF9900"></up-icon>
<view class="u-m-l-24"
>优惠券为虚拟发放一旦兑换不支持退款请悉知</view
>
</view>
</view>
</modal>
<view class="goods-detail" v-if="item.goodsCategory!='优惠券'">
<view class="u-flex u-row-center">
<view class="title">商品详情</view>
</view>
<view class="u-m-t-32">
<image class="w-full" v-for="(item,index) in item.goodsDescription" :key="index" mode="widthFix" :src="item">
</image>
</view>
</view>
<!-- 需要支付的弹窗 -->
<up-popup
:show="popupData.show"
mode="bottom"
closeOnClickOverlay
@close="popupData.show = false"
>
<view class="popup-content">
<view class="popup-content-top">
<text class="color-666">领取方式</text>
<text class="u-m-l-16">需前往店铺自行兑换领取</text>
</view>
<view class="goods-info">
<view class="u-flex">
<image class="cover"></image>
<view class="u-flex u-flex-1 u-row-between u-p-l-16 u-col-center">
<view>
<view class="u-font-32 font-bold">这里是商品名称</view>
<view class="u-m-t-54 color-666">8000积分+9.99</view>
</view>
<text class="color-666">X1</text>
</view>
</view>
<view class="u-m-t-20 waring u-flex u-col-center">
<up-icon name="info-circle" size="16" color="#FF9900"></up-icon>
<view class="u-m-l-16"
>优惠券为虚拟发放一旦兑换不支持退款请悉知</view
>
</view>
</view>
<view style="height: 100px"></view>
<view class="fixed-bottom u-flex u-row-center">
<view class="btn" @click="exchangeClick" :class="isCanExchange">立即兑换</view>
</view>
<view class="bottom font-bold text-right">
<text>合计 8000积分 + ¥9.99</text>
</view>
<!-- 兑换确认弹窗start -->
<modal v-model="modalData.show" title="立即兑换确认" @confirm="confirmExchange">
<view style="padding: 32rpx 50rpx">
<view class="u-font-32">您将消耗{{item.requiredPoints}}积分兑换商品{{item.goodsName}}是否确认兑换</view>
<view class="u-m-t-20 waring u-flex u-font-32 u-col-center">
<up-icon name="info-circle" size="16" color="#FF9900"></up-icon>
<view class="u-m-l-24">优惠券为虚拟发放一旦兑换不支持退款请悉知</view>
</view>
</view>
</modal>
<view class="u-m-t-42 u-flex u-row-center">
<view class="btn">确认兑换</view>
</view>
</view>
</up-popup>
<!-- 兑换确认弹窗end -->
</view>
<!-- 需要支付的弹窗 -->
<up-popup :show="popupData.show" mode="bottom" closeOnClickOverlay @close="popupData.show = false">
<view class="popup-content">
<view class="popup-content-top">
<text class="color-666">领取方式</text>
<text class="u-m-l-16">需前往店铺自行兑换领取</text>
</view>
<view class="goods-info">
<view class="u-flex">
<image class="cover"></image>
<view class="u-flex u-flex-1 u-row-between u-p-l-16 u-col-center">
<view>
<view class="u-font-32 font-bold">这里是商品名称</view>
<view class="u-m-t-54 color-666">8000积分+9.99</view>
</view>
<text class="color-666">X1</text>
</view>
</view>
<view class="u-m-t-20 waring u-flex u-col-center">
<up-icon name="info-circle" size="16" color="#FF9900"></up-icon>
<view class="u-m-l-16">优惠券为虚拟发放一旦兑换不支持退款请悉知</view>
</view>
</view>
<view class="bottom font-bold text-right">
<text>合计 8000积分 + ¥9.99</text>
</view>
<view class="u-m-t-42 u-flex u-row-center">
<view class="btn">确认兑换</view>
</view>
</view>
</up-popup>
<!-- 兑换确认弹窗end -->
</view>
</template>
<script setup>
import modal from "@/scoreShop/components/modal.vue";
const imgs = {
bg: "https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/9fd6a3ad2b384f6cb4e88ed6b77bd334.png",
};
const modalData = reactive({
show: false,
});
import {
computed,
reactive
} from "vue";
import {
getOpenId
} from '@/utils/uniapp.js'
import modal from "@/scoreShop/components/modal.vue";
import * as pointGoodsApi from "@/common/api/order/pointGoods.js";
const imgs = {
bg: "https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/9fd6a3ad2b384f6cb4e88ed6b77bd334.png",
};
const modalData = reactive({
show: false,
});
const popupData = reactive({
show: false,
});
function exchangeClick() {
modalData.show = true;
}
function confirmExchange() {
modalData.show = false;
}
const popupData = reactive({
show: false,
});
function exchangeClick() {
modalData.show = true;
}
function confirmExchange() {
// modalData.show = false;
exchange()
}
async function exchange() {
uni.showLoading({
title: '兑换中……'
})
const openId = await getOpenId()
uni.hideLoading()
if (openId) {
pointGoodsApi.exchange({
pointsGoodsId: item.id,
shopId: item.shopId,
number: 1,
price: item.extraPrice,
openId
}).then(res => {
modalData.show = false;
if(res){
uni.setStorageSync('exchange_goods_success',res)
uni.redirectTo({
url:'/scoreShop/success/index'
})
}else{
uni.showToast({
title: '兑换失败',
icon: 'none'
})
}
})
} else {
uni.showToast({
title: '鉴权失败,兑换失败',
icon: 'none'
})
}
}
const item = reactive({
goodsDescription: []
})
const couponTypes = {
1: "满减券",
2: "商品券",
3: "折扣券",
4: "第二件半价券",
5: "消费送券",
6: "买一送一券",
7: "固定价格券",
8: "免配送费券",
};
const returnCouponTypeName = computed(() => {
if (!item.id) {
return ' '
}
const type = item.couponInfo.couponType
if (type == 1) {
return item.couponInfo.discountAmount + '元代金券' + ' ' + `(满${item.fullAmount}可用)`
}
if (type == 2) {
return item.couponInfo.discountNum + '件商品兑换' + ' ' + `(满${item.fullAmount}可用)`
}
if (type == 3) {
return item.couponInfo.discountRate / 10 + '折' + ' ' + `(满${item.fullAmount}可用)`
}
if (type == 4) {
return '第二件半价券'
}
if (type == 6) {
return '买一送一券'
}
})
const pointsUser = reactive({
pointBalance: 0,
})
const isCanExchange = computed(() => {
if (pointsUser.pointBalance <= 0) {
return false
}
if (pointsUser.pointBalance < item.requiredPoints) {
return false
}
if (item.quantity <= 0) {
return false
}
if(item.limitQuota&&item.boughtCount>=item.limitQuota){
return false
}
return true
})
onLoad((opt) => {
const exchange_goods = uni.getStorageSync('exchange_goods')
if (exchange_goods.goodsDescription) {
exchange_goods.goodsDescription = JSON.parse(exchange_goods.goodsDescription)
} else {
exchange_goods.goodsDescription = []
}
const pointsUserData = uni.getStorageSync('pointsUser')
Object.assign(item, exchange_goods)
Object.assign(pointsUser, pointsUserData)
})
</script>
<style lang="scss" scoped>
.top {
margin: 14rpx 18rpx;
background-size: cover;
height: 350rpx;
box-sizing: border-box;
padding-left: 70rpx;
padding-top: 95rpx;
$topHeight: 350rpx;
.name {
color: #000000;
font-size: 36rpx;
font-weight: 700;
}
.info {
color: #333333;
font-size: 48rpx;
font-weight: 700;
margin-top: 62rpx;
}
}
.sku {
display: flex;
justify-content: space-between;
padding: 20rpx 36rpx;
align-items: center;
background: linear-gradient(90deg, #ff4a63 0%, #fd1f48 100%);
.price {
color: #fff;
font-weight: 700;
font-size: 36rpx;
}
.text {
color: #fff;
font-size: 28rpx;
}
}
.goods-name {
padding: 20rpx 28rpx;
background: #fff;
font-size: 32rpx;
font-weight: 700;
color: #333;
}
.desc {
padding: 32rpx 28rpx;
background-color: #fff;
}
.goods-detail {
padding: 32rpx;
.title {
position: relative;
padding: 0 22rpx;
&::before {
content: "";
display: block;
position: absolute;
right: 100%;
width: 70rpx;
height: 2rpx;
top: 50%;
transform: translateY(-50%);
background: linear-gradient(90deg, #f7f8f9 0%, #c9cbcc 100%);
}
&::after {
left: 100%;
content: "";
position: absolute;
top: 50%;
transform: translateY(-50%);
display: block;
width: 70rpx;
height: 2rpx;
background: linear-gradient(90deg, #c9cbcc 0%, #f7f8f9 100%);
}
}
}
.top-img {
width: 750rpx;
height: $topHeight;
}
.fixed-bottom {
position: fixed;
background-color: #fff;
left: 0;
right: 0;
padding-bottom: calc(env(safe-area-inset-bottom) + 2rpx);
background-color: #fff;
bottom: 0;
z-index: 10;
.btn {
padding: 16rpx 62rpx;
border-radius: 16rpx;
font-size: 32rpx;
font-weight: 700;
color: #9c571f;
background: linear-gradient(180deg, #f7cc84 0%, #f9dda9 100%);
&.gray {
background: #9999992b;
color: #999999;
}
}
}
.waring {
background-color: rgba(255, 204, 0, 0.09);
padding: 32rpx 24rpx;
color: #ff8d28;
}
.popup-content {
font-size: 28rpx;
min-height: 300px;
.popup-content-top {
padding: 32rpx 28rpx;
border-bottom: 1px solid #ededed;
}
.goods-info {
padding: 44rpx 28rpx;
border-bottom: 1px solid #ededed;
.cover {
width: 184rpx;
height: 184rpx;
border-radius: 16rpx;
background: #d9d9d9;
}
}
.bottom {
padding: 20rpx 28rpx 20rpx 20rpx;
border-bottom: 1px solid #ededed;
}
.btn {
display: flex;
padding: 22rpx 214rpx;
align-items: flex-start;
gap: 20rpx;
border-radius: 66rpx;
background: #e8ad7b;
font-size: 32rpx;
color: #fff;
font-size: 700;
}
}
</style>
.top {
margin: 14rpx 18rpx;
background-size: cover;
height: $topHeight;
box-sizing: border-box;
padding-left: 70rpx;
padding-top: 95rpx;
.name {
color: #000000;
font-size: 36rpx;
font-weight: 700;
}
.info {
color: #333333;
font-size: 48rpx;
font-weight: 700;
margin-top: 62rpx;
}
}
.sku {
display: flex;
justify-content: space-between;
padding: 20rpx 36rpx;
align-items: center;
background: linear-gradient(90deg, #ff4a63 0%, #fd1f48 100%);
.price {
color: #fff;
font-weight: 700;
font-size: 36rpx;
}
.text {
color: #fff;
font-size: 28rpx;
}
}
.goods-name {
padding: 20rpx 28rpx;
background: #fff;
font-size: 32rpx;
font-weight: 700;
color: #333;
}
.desc {
padding: 32rpx 28rpx;
background-color: #fff;
}
.goods-detail {
padding: 32rpx;
.title {
position: relative;
padding: 0 22rpx;
&::before {
content: "";
display: block;
position: absolute;
right: 100%;
width: 70rpx;
height: 2rpx;
top: 50%;
transform: translateY(-50%);
background: linear-gradient(90deg, #f7f8f9 0%, #c9cbcc 100%);
}
&::after {
left: 100%;
content: "";
position: absolute;
top: 50%;
transform: translateY(-50%);
display: block;
width: 70rpx;
height: 2rpx;
background: linear-gradient(90deg, #c9cbcc 0%, #f7f8f9 100%);
}
}
}
.fixed-bottom {
position: fixed;
background-color: #fff;
left: 0;
right: 0;
padding-bottom: calc(env(safe-area-inset-bottom) + 2rpx);
padding-top: 32rpx;
background-color: #fff;
bottom: 0;
z-index: 10;
.btn {
padding: 16rpx 62rpx;
border-radius: 16rpx;
font-size: 32rpx;
font-weight: 700;
color: #9c571f;
background: linear-gradient(180deg, #f7cc84 0%, #f9dda9 100%);
&.gray {
background: #9999992b;
color: #999999;
}
}
}
.waring {
background-color: rgba(255, 204, 0, 0.09);
padding: 32rpx 24rpx;
color: #ff8d28;
}
.popup-content {
font-size: 28rpx;
min-height: 300px;
.popup-content-top {
padding: 32rpx 28rpx;
border-bottom: 1px solid #ededed;
}
.goods-info {
padding: 44rpx 28rpx;
border-bottom: 1px solid #ededed;
.cover {
width: 184rpx;
height: 184rpx;
border-radius: 16rpx;
background: #d9d9d9;
}
}
.bottom {
padding: 20rpx 28rpx 20rpx 20rpx;
border-bottom: 1px solid #ededed;
}
.btn {
display: flex;
padding: 22rpx 214rpx;
align-items: flex-start;
gap: 20rpx;
border-radius: 66rpx;
background: #e8ad7b;
font-size: 32rpx;
color: #fff;
font-size: 700;
}
}
</style>