feat: 新增优惠券功能与优化用户体验

- 新增商品券类型支持,包括商品券的领取、使用和展示
- 优化优惠券领取流程,支持兑换码兑换和自动跳转登录
- 新增优惠活动功能,支持活动商品折扣
- 重构优惠券页面布局,区分领取和使用页面
- 新增分销商信息编辑功能
- 优化小程序更新机制,改进更新提示流程
- 修复优惠券使用条件判断逻辑
- 调整UI样式,统一主题色为绿色
- 新增电话客服功能,支持动态电话号码
- 优化订单详情页,展示商品券信息
This commit is contained in:
yindongqi 2025-07-14 10:00:22 +08:00
parent b09939848c
commit 891ee783a5
22 changed files with 3027 additions and 440 deletions

100
App.vue
View File

@ -12,56 +12,78 @@ import {
import { APP_ID } from '@/config'
//
const checkUpdate = () => {
//
if (!uni.canIUse('getUpdateManager')) {
uni.showModal({
title: '提示',
content: '当前微信版本过低,请升级后使用',
showCancel: false
});
return;
}
const updateManager = uni.getUpdateManager();
//
updateManager.onCheckForUpdate((res) => {
if (res.hasUpdate) {
uni.showLoading({ title: '检测到新版本,下载中...' });
}
});
//
updateManager.onUpdateReady(() => {
uni.hideLoading();
uni.showModal({
title: '更新提示',
content: '新版本已准备好,立即重启应用?',
showCancel: false,
success: (res) => {
if (res.confirm) {
//
updateManager.applyUpdate();
}
const autoUpdate = () => {
//
if (uni.canIUse("getUpdateManager")) {
//
const updateManager = uni.getUpdateManager();
//1.
updateManager.onCheckForUpdate((res) => {
//
if (res.hasUpdate) {
//
uni.showModal({
title: "更新提示",
content: "检测到新版本,是否下载新版本并重启小程序?",
success: (res) => {
if (res.confirm) {
//2.
downLoadAndUpdate(updateManager);
} else if (res.cancel) {
//
uni.showModal({
title: "温馨提示",
content: "本次版本更新涉及到新的功能添加,旧版本无法正常访问的哦",
showCancel: false, //
confirmText: "确定更新", //
success: (res) => {
if (res.confirm) {
//
downLoadAndUpdate(updateManager);
}
},
});
}
},
});
}
});
} else {
//
uni.showModal({
title: "提示",
content: "当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。",
});
}
}
/**
* 下载小程序新版本并重启应用
*/
const downLoadAndUpdate = (updateManager) => {
uni.showLoading();
//
updateManager.onUpdateReady(() => {
uni.hideLoading();
// applyUpdate
updateManager.applyUpdate();
});
//
//
updateManager.onUpdateFailed(() => {
uni.hideLoading();
//
uni.showModal({
title: '提示',
content: '更新失败,请删除小程序后重新搜索打开',
showCancel: false
title: "已经有新版本了哟~",
content: "新版本已经上线啦~,请您删除当前小程序,重新搜索打开哟~",
});
});
};
}
onLaunch(() => {
checkUpdate();
autoUpdate();
console.log('App Launch')
})

16
api/activity.js Normal file
View File

@ -0,0 +1,16 @@
import api from './api'
/**
* 获取某个优惠活动
*/
export function getpromotionactivity(id) {
return api.get(`/promotionactivity/get?id=${id}`, {}, { login: false })
}
/**
* 获取所有优惠活动
*/
export function getPromotionactivityInfo(data) {
return api.get(`/promotionactivity/page`, data, { login: true })
}

View File

@ -14,6 +14,13 @@ export function couponMine(data) {
return api.get(`/coupon/my`, data, { login: false })
}
/**
* couponMine
*/
export function couponCanUserMine(data) {
return api.get(`/coupon/use`, data, { login: false })
}
/**
* couponIndex let couponCount = (params = {}) => vm.$u.get('/coupon/count', params);
*/

View File

@ -21,3 +21,11 @@ export function getDistributorInfo(id) {
export function getCommissionList(data) {
return api.get(`/order/listByDistributor`, data, { login: true })
}
/**
* 编辑分销商信息
* @param {Object} data - 包含 id, name, phone, workUnit, recipientName, bankCardNumber, bankName
*/
export function editDistributor(data) {
return api.put(`/distributor/update`, data, { login: true })
}

View File

@ -13,3 +13,10 @@ export function menuGoods(data) {
return api.get('/product/products', data, { login: false })
}
/**
* 获取某个商品信息
*/
export function getGoods(id) {
return api.get('/product/detail/' + id, { login: false })
}

View File

@ -124,11 +124,19 @@
"path": "pages/coupons/coupons",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "我的卡券",
"navigationBarTitleText": "领取卡券",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
}, {
"path": "pages/coupons/mycoupons",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "我的卡券",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
},{
"path": "pages/mine/userinfo",
"style": {
"navigationBarTitleText": "用户信息",

View File

@ -0,0 +1,563 @@
<template>
<uv-navbar :fixed="false" :title="title" left-arrow @leftClick="$onClickLeft" />
<view class="container position-relative w-100 h-100 overflow-hidden">
<view class="exchange-box">
<view class="input-box">
<input type="text" v-model="exchange_code" placeholder="请输入兑换码"
placeholder-class="text-color-assist font-size-base" />
<button type="primary" style="background-color: #52ac41;" @click="exchange">兑换</button>
</view>
</view>
<view class="tabbar">
<view class="tab" :class="{ active: activeTabIndex == index }" v-for="(item, index) in tabs" :key="index"
@tap="handleTab(index)">
<view class="title">{{ item.title }}</view>
</view>
</view>
<view class="flex-fill">
<scroll-view scroll-y class="coupon-list" @scrolltolower="getCoupons(activeTabIndex)">
<view class="wrapper" v-if="0 === activeTabIndex">
<uv-empty v-if="myCoupons.length == 0" mode="list"></uv-empty>
<view class="coupon" v-for="(item, index) in myCoupons" :key="index"
@tap="openDetailModal(item, index)">
<view class="taobao">
<view class="ticket">
<view class="left">
<image class="picture" :src="item.image" mode="aspectFill"></image>
<view class="introduce">
<view class="top">
<text class="big">{{ item.value }}</text>
<view>
{{ item.least }}{{ item.value }}
</view>
</view>
<view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd')}}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 1">
<view class="use immediate-use" :round="true" @tap="receive(item, index)">立即领取
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 0">
<view v-if="item.status == 0" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view>
<view v-else class="used">已使用</view>
</view>
</view>
</view>
</view>
</view>
<view class="wrapper" v-if="1 === activeTabIndex">
<uv-empty v-if="notCoupons.length == 0" mode="list"></uv-empty>
<view class="coupon" v-for="(item, index) in notCoupons" :key="index"
@tap="openDetailModal(item, index)">
<view class="taobao">
<view class="ticket">
<view class="left">
<image class="picture" :src="item.image" mode="aspectFill"></image>
<view class="introduce">
<view class="top">
<text class="big">{{ item.value }}</text>
<view>
{{ item.least }}{{ item.value }}
</view>
</view>
<view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd')}}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 1">
<view class="use immediate-use" :round="true" @tap="receive(item, index)">立即领取
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 0">
<view v-if="item.status == 0" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view>
<view v-else class="used">已使用</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<modal custom :show="detailModalVisible" @cancel="closeDetailModal" width="90%">
<view class="modal-content">
<view class="d-flex font-size-extra-lg text-color-base just-content-center mb-20">{{ coupon.title }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
有效期{{ formatDateTime(coupon.startTime, 'yyyy-MM-dd') }}-{{ formatDateTime(coupon.endTime,
'yyyy-MM-dd')}}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
领取时间{{ formatDateTime(coupon.createTime) }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
券价值{{ coupon.least }}{{ coupon.value }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20" v-if="activeTabIndex == 1">
每人限领{{ coupon.limit }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
适用范围{{ typeInfo(coupon.type) }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
适用店铺{{ coupon.shopName }}
</view>
<view class="d-flex align-items-center just-content-center" v-if="activeTabIndex == 0">
<button type="primary" @tap="useCoupon" class="use-coupon-btn">立即使用</button>
</view>
<view class="d-flex align-items-center just-content-center" v-if="activeTabIndex == 1">
<button type="primary" @tap="receive(coupon, couponIndex)" class="use-coupon-btn">立即领取</button>
</view>
</view>
</modal>
<!--轻提示-->
<uv-toast ref="uToast"></uv-toast>
</view>
</template>
<script setup>
import {
ref,
watch
} from 'vue'
import { useMainStore } from '@/store/store'
import { storeToRefs } from 'pinia'
import { onLoad, onShow, onPullDownRefresh, onHide } from '@dcloudio/uni-app'
import { formatDateTime, kmUnit } from '@/utils/util'
import {
couponReceive,
couponMine,
couponIndexApi
} from '@/api/coupon'
const main = useMainStore()
const { isLogin } = storeToRefs(main)
const title = ref('优惠券')
const tabs = ref([
{
title: '我的优惠券', page: 1, pagesize: 10,
coupons: []
},
{
title: '未领优惠券', page: 1, pagesize: 10,
coupons: []
}
])
const activeTabIndex = ref(0)
const detailModalVisible = ref(false)
const coupon = ref({})
const couponIndex = ref(0)
const exchange_code = ref('')
const uToast = ref()
const myCoupons = ref([])
const notCoupons = ref([])
onShow(() => {
getCoupons(0)
})
onPullDownRefresh(() => {
if (activeTabIndex.value == 0) {
myCoupons.value = []
}
if (activeTabIndex.value == 1) {
notCoupons.value = []
}
tabs.value[activeTabIndex.value].page = 1;
getCoupons(activeTabIndex.value)
})
watch(activeTabIndex, () => {
getCoupons(activeTabIndex.value)
})
//
const exchange = async () => {
let data = await couponReceive({ code: exchange_code.value });
if (data) {
uToast.value.show({
message: '兑换成功',
type: 'success'
});
tabs.value[0].coupons = [];
tabs.value[0].page = 1;
getCoupons(0)
tabs.value[1].coupons = [];
tabs.value[1].page = 1;
getCoupons(1)
}
}
// 使
const typeInfo = (type) => {
if (type == 0) {
return '通用'
}
if (type == 1) {
return '自取'
}
if (type == 2) {
return '外卖'
}
}
const handleTab = (index) => {
console.log('activeTabIndex2:', index)
activeTabIndex.value = index
}
const getCoupons = async (type) => {
let page = tabs.value[type].page;
let pagesize = tabs.value[type].pagesize;
//
let data = [];
if (type == 0) {
myCoupons.value = await couponMine({ page: page, pagesize: pagesize });
}
//
// if (type == 1) {
// notCoupons.value = await couponIndexApi({page:page,pagesize:pagesize});
// }
if (type == 1) {
notCoupons.value = await couponIndexApi({ page: page, pagesize: pagesize });
//
notCoupons.value = notCoupons.value.filter(notCoupon =>
!myCoupons.value.some(myCoupon => myCoupon.couponId === notCoupon.id)
);
}
//console.log('data:',data)
uni.stopPullDownRefresh();
console.log('tabs.value:', tabs.value[type].title)
//tabs.value[type].page++;
}
const openDetailModal = (couponItem, index) => {
couponIndex.value = index;
coupon.value = couponItem
detailModalVisible.value = true
}
const useCouponWith = (coupon) => {
//coupon.value = coupon
useCoupon();
}
const closeDetailModal = () => {
detailModalVisible.value = false
coupon.value = {}
}
const useCoupon = () => {
uni.switchTab({
url: '/pages/menu/menu'
})
}
const showTip1 = () => {
uni.showToast({
title: '您暂时还没有赠送中卡券哦~',
icon: 'none'
})
}
const showTip2 = () => {
uni.showToast({
title: '您暂时还没有券码哦~',
icon: 'none'
})
}
//
const receive = async (coupon, index) => {
let data = await couponReceive({ id: coupon.id });
if (data) {
uToast.value.show({
message: '领取成功',
type: 'success'
});
detailModalVisible.value = false
}
}
</script>
<style lang="scss" scoped>
/* #ifdef H5 */
page {
height: 100%;
}
/* #endif */
.container {
display: flex;
flex-direction: column;
}
.exchange-box {
flex-shrink: 0;
height: 200rpx;
background-color: #ffffff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.input-box {
display: flex;
align-items: stretch;
width: 70%;
flex-shrink: 0;
input {
flex: 1;
height: 80rpx;
border: 1rpx solid #eee;
border-right: 0;
border-radius: 8rpx 0 0 8rpx;
// padding: 20rpx;
font-size: $font-size-base;
color: $text-color-base;
}
button {
border-radius: 0 8rpx 8rpx 0;
display: flex;
align-items: center;
}
}
}
.tabbar {
flex-shrink: 0;
width: 100%;
height: 120rpx;
display: flex;
align-items: center;
justify-content: center;
.tab {
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: $font-size-base;
// color: $text-color-base;
color: #52ac41;
position: relative;
.title {
padding: 15rpx 0;
}
&.active {
color: #52ac41;
.title {
border-bottom: 5rpx solid #52ac41;
}
}
}
}
.coupon-list {
height: calc(100vh - 120rpx - 200rpx);
/* #ifdef H5 */
height: calc(100vh - 120rpx - 200rpx - 44px);
/* #endif */
}
.wrapper {
padding: 0 20rpx;
display: flex;
flex-direction: column;
.coupon {
display: flex;
flex-direction: column;
background-color: #FFFFFF;
margin-bottom: 30rpx;
//padding: 0 30rpx;
border-radius: 6rpx;
box-shadow: 0 10rpx 10rpx -10rpx rgba(15, 15, 15, 0.1);
position: relative;
&::before {
content: "";
position: absolute;
background-color: $bg-color;
width: 30rpx;
height: 30rpx;
bottom: 65rpx;
left: -15rpx;
border-radius: 100%;
}
&::after {
content: "";
position: absolute;
background-color: $bg-color;
width: 30rpx;
height: 30rpx;
bottom: 65rpx;
right: -15rpx;
border-radius: 100%;
}
.detail {
padding: 20rpx 0;
position: relative;
&::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: 0;
border-bottom: 1rpx dashed #c6c6c6;
transform: scaleY(0.5);
}
.coupon-img {
width: 150rpx;
height: 150rpx;
margin-right: 40rpx;
}
}
}
}
.use-coupon-btn {
width: 95%;
border-radius: 50rem !important;
}
.taobao {
background-color: white;
.title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
font-size: 30rpx;
.left {
display: flex;
align-items: center;
}
.store {
font-weight: 500;
}
.buddha {
width: 70rpx;
height: 70rpx;
border-radius: 10rpx;
margin-right: 10rpx;
}
.entrance {
color: $uv-info;
border: solid 2rpx $uv-info;
line-height: 48rpx;
padding: 0 30rpx;
background: none;
border-radius: 15px;
}
}
.ticket {
display: flex;
.left {
width: 70%;
padding: 20rpx;
background-color: white; //rgb(255, 245, 244);
border-radius: 20rpx;
border-right: dashed 2rpx rgb(224, 215, 211);
display: flex;
.picture {
//width: 172rpx;
border-radius: 20rpx;
width: 190rpx;
height: 190rpx;
}
.introduce {
margin-left: 10rpx;
.top {
color: $uv-warning;
font-size: 28rpx;
.big {
font-size: 60rpx;
font-weight: bold;
margin-right: 10rpx;
}
}
.type {
font-size: 28rpx;
color: $uv-info-dark;
}
.date {
margin-top: 10rpx;
font-size: 20rpx;
color: $uv-info-dark;
}
}
}
.right {
width: 30%;
padding: 40rpx 20rpx;
background-color: white; //rgb(255, 245, 244);
border-radius: 20rpx;
display: flex;
align-items: center;
.use {
height: auto;
padding: 0 20rpx;
font-size: 24rpx;
border-radius: 40rpx;
color: #ffffff !important;
background-color: $uv-warning !important;
line-height: 40rpx;
color: rgb(117, 142, 165);
margin-left: 20rpx;
}
.used {
height: auto;
padding: 0 20rpx;
font-size: 24rpx;
border-radius: 40rpx;
//color: #ffffff!important;
//background-color: $u-type-warning!important;
line-height: 40rpx;
//color: rgb(117, 142, 165);
margin-left: 20rpx;
}
}
.right_log {
text-align: center;
width: 30%;
padding: 80rpx 0rpx;
background-color: white; //rgb(255, 245, 244);
border-radius: 20rpx;
align-items: center;
}
}
}
</style>

View File

@ -1,6 +1,7 @@
<template>
<uv-navbar :fixed="false" :title="title" left-arrow @leftClick="$onClickLeft" />
<view class="container position-relative w-100 h-100 overflow-hidden">
<view class="exchange-box">
<view class="input-box">
<input type="text" v-model="exchange_code" placeholder="请输入兑换码"
@ -8,15 +9,15 @@
<button type="primary" style="background-color: #52ac41;" @click="exchange">兑换</button>
</view>
</view>
<view class="tabbar">
<!-- <view class="tabbar">
<view class="tab" :class="{ active: activeTabIndex == index }" v-for="(item, index) in tabs" :key="index"
@tap="handleTab(index)">
<view class="title">{{ item.title }}</view>
</view>
</view>
</view> -->
<view class="flex-fill">
<scroll-view scroll-y class="coupon-list" @scrolltolower="getCoupons(activeTabIndex)">
<view class="wrapper" v-if="0 === activeTabIndex">
<view class="wrapper" v-if="1 === activeTabIndex">
<uv-empty v-if="myCoupons.length == 0" mode="list"></uv-empty>
<view class="coupon" v-for="(item, index) in myCoupons" :key="index"
@tap="openDetailModal(item, index)">
@ -34,14 +35,18 @@
</view>
<view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd')}}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 1">
<view class="use immediate-use" :round="true" @tap="receive(item, index)">立即领取
'yyyy-MM-dd') }}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 0">
<view v-if="item.isReceive === 1" class="use immediate-use"
style="background-color: #999999;">已领取</view>
<view v-else class="use immediate-use" :round="true" @tap="receive(item, index)">
立即领取{{ item.isReceive }}</view>
<!-- <view class="use immediate-use" :round="true" @tap="receive(item, index)">立即领取
</view> -->
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 1">
<view v-if="item.status == 0" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view>
<view v-else class="used">已使用</view>
@ -50,14 +55,17 @@
</view>
</view>
</view>
<view class="wrapper" v-if="1 === activeTabIndex">
<view class="wrapper" v-if="0 === activeTabIndex">
<uv-empty v-if="notCoupons.length == 0" mode="list"></uv-empty>
<view class="coupon" v-for="(item, index) in notCoupons" :key="index"
@tap="openDetailModal(item, index)">
<view class="taobao">
<view class="taobao" v-if="item.couponType == 1">
<view class="ticket">
<view class="left">
<image class="picture" :src="item.image" mode="aspectFill"></image>
<image class="picture" :src="item.image" mode="aspectFill">
<view class="coupon-type">满减券</view>
</image>
<view class="introduce">
<view class="top">
@ -68,14 +76,51 @@
</view>
<view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd')}}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 1">
<view class="use immediate-use" :round="true" @tap="receive(item, index)">立即领取
'yyyy-MM-dd') }}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 0">
<view v-if="item.isReceive === 1" class="use immediate-use"
style="background-color: #999999 !important; padding-left: 25rpx; padding-right: 25rpx;">
已领取</view>
<view v-else class="use immediate-use" :round="true" @tap="receive(item, index)">
立即领取</view>
<!-- <view class="use immediate-use" :round="true" @tap="receive(item, index)">立即领取
</view> -->
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 1">
<view v-if="item.status == 0" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view>
<view v-else class="used">已使用</view>
</view>
</view>
</view>
<view class="taobao" v-if="item.couponType == 2">
<view class="ticket">
<view class="left">
<image class="picture" :src="item.image" mode="aspectFill">
<view class="coupon-type">商品券</view>
</image>
<view class="introduce">
<view class="top">
商品名称<text class="big" style="font-size: 32rpx;">{{ item.productName
}}</text>
</view>
<view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd') }}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 0">
<view v-if="item.isReceive === 1" class="use immediate-use"
style="background-color: #999999 !important; padding-left: 25rpx; padding-right: 25rpx;">
已领取</view>
<view v-else class="use immediate-use" :round="true" @tap="receive(item, index)">
立即领取</view>
<!-- <view class="use immediate-use" :round="true" @tap="receive(item, index)">立即领取
</view> -->
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 1">
<view v-if="item.status == 0" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view>
<view v-else class="used">已使用</view>
@ -86,21 +131,26 @@
</view>
</scroll-view>
</view>
<modal custom :show="detailModalVisible" @cancel="closeDetailModal" width="90%">
<view class="modal-content">
<view class="modal-content" v-if="coupon.couponType == 1">
<view class="d-flex font-size-extra-lg text-color-base just-content-center mb-20">{{ coupon.title }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
有效期{{ formatDateTime(coupon.startTime, 'yyyy-MM-dd') }}-{{ formatDateTime(coupon.endTime,
'yyyy-MM-dd')}}
领取期限{{ formatDateTime(coupon.startTime, 'yyyy-MM-dd') }}-{{ formatDateTime(coupon.endTime,
'yyyy-MM-dd') }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
领取时间{{ formatDateTime(coupon.createTime) }}
<view class="d-flex font-size-sm text-color-base mb-20" v-if="coupon.expiryDays != null">
有效期自领取后{{ coupon.expiryDays }}天内有效
</view>
<view class="d-flex font-size-sm text-color-base mb-20" v-else>
有效期{{ formatDateTime(coupon.startTime, 'yyyy-MM-dd') }}-{{ formatDateTime(coupon.endTime,
'yyyy-MM-dd') }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
券价值{{ coupon.least }}{{ coupon.value }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20" v-if="activeTabIndex == 1">
<view class="d-flex font-size-sm text-color-base mb-20" v-if="activeTabIndex == 0">
每人限领{{ coupon.limit }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
@ -109,11 +159,50 @@
<view class="d-flex font-size-sm text-color-base mb-20">
适用店铺{{ coupon.shopName }}
</view>
<view class="d-flex align-items-center just-content-center" v-if="activeTabIndex == 0">
<view class="d-flex align-items-center just-content-center" v-if="activeTabIndex == 1">
<button type="primary" @tap="useCoupon" class="use-coupon-btn">立即使用</button>
</view>
<view class="d-flex align-items-center just-content-center" v-if="activeTabIndex == 0">
<button v-if="coupon.isReceive === 0" type="primary" @tap="receive(coupon, couponIndex)"
class="use-coupon-btn">立即领取</button>
<button v-if="coupon.isReceive === 1" style="background-color: #999999; color: white;"
class="use-coupon-btn">已领取</button>
</view>
</view>
<view class="modal-content" v-if="coupon.couponType == 2">
<view class="d-flex font-size-extra-lg text-color-base just-content-center mb-20">{{ coupon.title }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
领取期限{{ formatDateTime(coupon.startTime, 'yyyy-MM-dd') }}-{{ formatDateTime(coupon.endTime,
'yyyy-MM-dd') }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20" v-if="coupon.expiryDays != null">
有效期自领取后{{ coupon.expiryDays }}天内有效
</view>
<view class="d-flex font-size-sm text-color-base mb-20" v-else>
有效期{{ formatDateTime(coupon.startTime, 'yyyy-MM-dd') }}-{{ formatDateTime(coupon.endTime,
'yyyy-MM-dd') }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
券价值使用此券免费领取 {{ coupon.productName }} 一份
</view>
<view class="d-flex font-size-sm text-color-base mb-20" v-if="activeTabIndex == 0">
每人限领{{ coupon.limit }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
适用范围{{ typeInfo(coupon.type) }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
适用店铺{{ coupon.shopName }}
</view>
<view class="d-flex align-items-center just-content-center" v-if="activeTabIndex == 1">
<button type="primary" @tap="receive(coupon, couponIndex)" class="use-coupon-btn">立即领取</button>
<button type="primary" @tap="useCoupon" class="use-coupon-btn">立即使用</button>
</view>
<view class="d-flex align-items-center just-content-center" v-if="activeTabIndex == 0">
<button v-if="coupon.isReceive === 0" type="primary" @tap="receive(coupon, couponIndex)"
class="use-coupon-btn">立即领取</button>
<button v-if="coupon.isReceive === 1" style="background-color: #999999; color: white;"
class="use-coupon-btn">已领取</button>
</view>
</view>
</modal>
@ -138,16 +227,16 @@ import {
couponIndexApi
} from '@/api/coupon'
const main = useMainStore()
const { isLogin } = storeToRefs(main)
const title = ref('优惠券')
const { member, isLogin } = storeToRefs(main);
const title = ref('领取优惠券')
const tabs = ref([
// {
// title: '', page: 1, pagesize: 10,
// coupons: []
// },
{
title: '我的优惠券', page: 1, pagesize: 10,
coupons: []
},
{
title: '未领优惠券', page: 1, pagesize: 10,
title: '领优惠券', page: 1, pagesize: 10,
coupons: []
}
])
@ -160,7 +249,50 @@ const uToast = ref()
const myCoupons = ref([])
const notCoupons = ref([])
onLoad(async (options) => {
// id,
if (options.couponId) {
console.log('111111111111111options.couponId', options.couponId);
if (!main.isLogin) {
uni.navigateTo({ url: '/pages/components/pages/login/login?couponId=' + options.couponId })
return
}
//
getCoupons(0)
let data = await couponReceive({ id: options.couponId });
if (data) {
uToast.value.show({
message: '领取成功',
type: 'success'
});
setTimeout(() => {
uni.redirectTo({
url: '/pages/components/pages/coupons/mycoupons'
})
}, 1000)
}
} else {
if (!main.isLogin) {
uni.navigateTo({ url: '/pages/components/pages/login/login?' })
return
}
//
getCoupons(0)
}
})
onShow(() => {
// console.log("444444444444", member);
// if (!main.isLogin) {
// uni.navigateTo({ url: '/pages/components/pages/login/login' })
// return
// }
getCoupons(0)
})
onPullDownRefresh(() => {
@ -188,9 +320,14 @@ const exchange = async () => {
tabs.value[0].coupons = [];
tabs.value[0].page = 1;
getCoupons(0)
tabs.value[1].coupons = [];
tabs.value[1].page = 1;
getCoupons(1)
setTimeout(() => {
uni.redirectTo({
url: '/pages/components/pages/coupons/mycoupons'
})
}, 1000)
// tabs.value[1].coupons = [];
// tabs.value[1].page = 1;
// getCoupons(1)
}
}
// 使
@ -214,14 +351,14 @@ const getCoupons = async (type) => {
let pagesize = tabs.value[type].pagesize;
//
let data = [];
if (type == 0) {
myCoupons.value = await couponMine({ page: page, pagesize: pagesize });
}
// if (type == 1) {
// myCoupons.value = await couponMine({ page: page, pagesize: pagesize });
// }
//
// if (type == 1) {
// notCoupons.value = await couponIndexApi({page:page,pagesize:pagesize});
// }
if (type == 1) {
if (type == 0) {
notCoupons.value = await couponIndexApi({ page: page, pagesize: pagesize });
//
notCoupons.value = notCoupons.value.filter(notCoupon =>
@ -273,6 +410,12 @@ const receive = async (coupon, index) => {
type: 'success'
});
detailModalVisible.value = false
setTimeout(() => {
uni.redirectTo({
url: '/pages/components/pages/coupons/mycoupons'
})
}, 1000)
}
}
@ -449,6 +592,7 @@ page {
align-items: center;
}
.store {
font-weight: 500;
}
@ -486,6 +630,21 @@ page {
border-radius: 20rpx;
width: 190rpx;
height: 190rpx;
position: relative;
flex-shrink: 0;
/* 禁止图片收缩 */
.coupon-type {
font-size: 24rpx;
color: #ffffff;
background-color: #f9ae3d;
padding: 5rpx 10rpx;
border-radius: 10rpx;
margin-right: 10rpx;
position: absolute;
top: 10rpx;
right: 0rpx;
}
}
.introduce {
@ -499,6 +658,7 @@ page {
font-size: 60rpx;
font-weight: bold;
margin-right: 10rpx;
max-width: 300rpx;
}
}

View File

@ -0,0 +1,738 @@
<template>
<uv-navbar :fixed="false" :title="title" left-arrow @leftClick="$onClickLeft" />
<view class="container position-relative w-100 h-100 overflow-hidden">
<!-- 二级菜单 -->
<view class="menu-container">
<!-- 第一级菜单通用/自取/外卖 -->
<view class="first-level-menu">
<view v-for="(item, index) in firstLevelMenus" :key="index"
:class="['menu-item', { 'active': activeFirstMenu === index }]" @tap="changeFirstMenu(index)">
{{ item.name }}
</view>
</view>
<!-- 第二级菜单未使用/已使用/已过期 -->
<view class="second-level-menu">
<view v-for="(item, index) in secondLevelMenus" :key="index"
:class="['menu-item', { 'active': activeSecondMenu === index }]" @tap="changeSecondMenu(index)">
{{ item.name }}
</view>
</view>
</view>
<!-- 兑换码区域 -->
<!-- <view class="exchange-box">
<view class="input-box">
<input type="text" v-model="exchange_code" placeholder="请输入兑换码"
placeholder-class="text-color-assist font-size-base" />
<button type="primary" style="background-color: #52ac41;" @click="exchange">兑换</button>
</view>
</view> -->
<!-- 优惠券列表 -->
<view class="flex-fill">
<scroll-view scroll-y class="coupon-list" @scrolltolower="loadMoreCoupons">
<uv-empty v-if="filteredCoupons.length == 0" mode="list"></uv-empty>
<view class="coupon" v-for="(item, index) in filteredCoupons" :key="index"
@tap="openDetailModal(item, index)">
<view class="coupon-card" v-if="item.couponType == 1">
<view class="ticket">
<view class="left">
<image class="picture" :src="item.image" mode="aspectFill">
<view class="coupon-type">满减券</view>
</image>
<view class="introduce">
<view class="top">
<text class="big">{{ item.value }}</text>
<view>
{{ item.least }}{{ item.value }}
</view>
</view>
<view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd') }}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right">
<view v-if="item.status == 0 && item.valid == 1" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view>
<view v-else-if="item.status == 0 && item.valid == 0" class="used"
:round="true" style="font-size: 20rpx;">未到使用期限</view>
<view v-else-if="item.status == 1" class="used">已使用</view>
<view v-else class="status-text expired">已过期</view>
</view>
</view>
</view>
<view class="coupon-card" v-if="item.couponType == 2">
<view class="ticket">
<view class="left">
<image class="picture" :src="item.image" mode="aspectFill">
<view class="coupon-type">商品券</view>
</image>
<view class="introduce">
<view class="top">
商品名称<text class="big" style="font-size: 32rpx;">{{ item.productName }}</text>
</view>
<view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd') }}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right">
<view v-if="item.status == 0 && item.valid == 1" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view>
<view v-else-if="item.status == 0 && item.valid == 0" class="used"
:round="true" style="font-size: 20rpx;">未到使用期限</view>
<view v-else-if="item.status == 1" class="used">已使用</view>
<view v-else class="status-text expired">已过期</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 优惠券详情模态框 -->
<modal custom :show="detailModalVisible" @cancel="closeDetailModal" width="90%">
<view class="modal-content">
<view class="d-flex font-size-extra-lg text-color-base just-content-center mb-20">{{ coupon.title }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
有效期{{ formatDateTime(coupon.startTime, 'yyyy-MM-dd') }}-{{ formatDateTime(coupon.endTime,
'yyyy-MM-dd') }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
领取时间{{ formatDateTime(coupon.createTime) }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20" v-if="coupon.couponType == 1">
券价值{{ coupon.least }}{{ coupon.value }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20" v-else>
券价值使用此券免费领取 {{ coupon.productName }} 一份
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
适用范围{{ typeInfo(coupon.type) }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">
适用店铺{{ coupon.shopName }}
</view>
<view class="d-flex align-items-center just-content-center"
v-if="coupon.status == 0 && coupon.valid == 1">
<button type="primary" @tap="useCoupon" class="use-coupon-btn">立即使用</button>
</view>
</view>
</modal>
<!--轻提示-->
<uv-toast ref="uToast"></uv-toast>
</view>
</template>
<script setup>
import {
ref,
watch,
computed
} from 'vue'
import { useMainStore } from '@/store/store'
import { storeToRefs } from 'pinia'
import { onLoad, onShow, onPullDownRefresh, onHide } from '@dcloudio/uni-app'
import { formatDateTime, kmUnit } from '@/utils/util'
import {
couponReceive,
couponMine,
couponIndexApi
} from '@/api/coupon'
const main = useMainStore()
const { isLogin } = storeToRefs(main)
const title = ref('我的优惠券')
//
const firstLevelMenus = ref([
{ name: '通用', type: 0, count: 0 },
{ name: '自取', type: 1, count: 0 },
{ name: '外卖', type: 2, count: 0 }
])
const activeFirstMenu = ref(0)
//
const secondLevelMenus = ref([
{ name: '未使用', status: 0 },
{ name: '已使用', status: 1 },
{ name: '已过期', status: 2 }
])
const activeSecondMenu = ref(0)
const detailModalVisible = ref(false)
const coupon = ref({})
const couponIndex = ref(0)
const exchange_code = ref('')
const uToast = ref()
const myCoupons = ref([])
const page = ref(1)
const pagesize = ref(10)
const loading = ref(false)
const finished = ref(false)
//
const filteredCoupons = computed(() => {
if (activeSecondMenu.value === 2) {
return myCoupons.value.map(coupon => ({
...coupon,
status: 2
}));
}
return myCoupons.value;
// return myCoupons.value.filter(item => {
// // //
// const typeMatch = item.type === firstLevelMenus.value[activeFirstMenu.value].type;
// // 使/使/
// const statusMatch = item.status === secondLevelMenus.value[activeSecondMenu.value].status;
// return typeMatch && statusMatch;
// });
});
//
watch(activeFirstMenu, () => {
//
resetAndReload();
});
//
watch(activeSecondMenu, () => {
//
resetAndReload();
});
onShow(() => {
resetAndReload();
})
onPullDownRefresh(() => {
resetAndReload();
})
//
const resetAndReload = () => {
page.value = 1;
finished.value = false;
myCoupons.value = [];
loadCoupons();
}
//
const changeFirstMenu = (index) => {
activeFirstMenu.value = index;
}
//
const changeSecondMenu = (index) => {
activeSecondMenu.value = index;
}
//
const loadMoreCoupons = () => {
if (!loading.value && !finished.value) {
page.value++;
loadCoupons();
}
}
//
const loadCoupons = async () => {
if (loading.value) return;
loading.value = true;
try {
const data = await couponMine({ page: page.value, pagesize: pagesize.value, type: firstLevelMenus.value[activeFirstMenu.value].type, status: secondLevelMenus.value[activeSecondMenu.value].status });
if (data && data.length > 0) {
//
myCoupons.value = page.value === 1 ? data : [...myCoupons.value, ...data];
//
updateCouponCounts();
} else {
finished.value = true;
}
} catch (error) {
console.error('加载优惠券失败', error);
} finally {
loading.value = false;
uni.stopPullDownRefresh();
}
}
//
const updateCouponCounts = () => {
//
firstLevelMenus.value.forEach(menu => menu.count = 0);
// 使
myCoupons.value.forEach(coupon => {
if (coupon.status === 0) { // 使
const menuIndex = firstLevelMenus.value.findIndex(menu => menu.type === coupon.type);
if (menuIndex !== -1) {
firstLevelMenus.value[menuIndex].count++;
}
}
});
}
//
const exchange = async () => {
if (!exchange_code.value) {
uToast.value.show({
message: '请输入兑换码',
type: 'warning'
});
return;
}
let data = await couponReceive({ code: exchange_code.value });
if (data) {
uToast.value.show({
message: '兑换成功',
type: 'success'
});
exchange_code.value = '';
resetAndReload();
}
}
// 使
const typeInfo = (type) => {
if (type == 0) {
return '通用'
}
if (type == 1) {
return '自取'
}
if (type == 2) {
return '外卖'
}
}
//
const openDetailModal = (couponItem, index) => {
couponIndex.value = index;
coupon.value = couponItem;
detailModalVisible.value = true;
}
// 使
const useCouponWith = (coupon) => {
useCoupon();
}
//
const closeDetailModal = () => {
detailModalVisible.value = false;
coupon.value = {};
}
// 使
const useCoupon = () => {
uni.switchTab({
url: '/pages/menu/menu'
})
}
// 使
const showRules = (item) => {
uni.showModal({
title: '使用规则',
content: `${item.least}元可用,最高减${item.value}`,
showCancel: false
});
}
</script>
<style lang="scss" scoped>
/* #ifdef H5 */
page {
height: 100%;
}
/* #endif */
.container {
display: flex;
flex-direction: column;
background-color: #f5f5f5;
}
/* 菜单容器样式 */
.menu-container {
background-color: #ffffff;
padding: 10rpx 0;
}
/* 一级菜单样式 */
.first-level-menu {
display: flex;
justify-content: space-around;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
.menu-item {
padding: 10rpx 30rpx;
font-size: 28rpx;
color: #333;
position: relative;
&.active {
color: #52ac41;
font-weight: bold;
&::after {
content: '';
position: absolute;
bottom: -10rpx;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background-color: #52ac41;
border-radius: 2rpx;
}
}
}
}
/* 二级菜单样式 */
.second-level-menu {
display: flex;
justify-content: space-around;
padding: 20rpx 0;
.menu-item {
padding: 10rpx 30rpx;
font-size: 26rpx;
color: #666;
border-radius: 30rpx;
&.active {
color: #ffffff;
background-color: #52ac41;
}
}
}
/* 兑换码区域样式 */
.exchange-box {
flex-shrink: 0;
height: 120rpx;
background-color: #ffffff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 20rpx;
.input-box {
display: flex;
align-items: stretch;
width: 70%;
flex-shrink: 0;
input {
flex: 1;
height: 80rpx;
border: 1rpx solid #eee;
border-right: 0;
border-radius: 8rpx 0 0 8rpx;
padding: 0 20rpx;
font-size: $font-size-base;
color: $text-color-base;
}
button {
border-radius: 0 8rpx 8rpx 0;
display: flex;
align-items: center;
justify-content: center;
}
}
}
/* 优惠券列表样式 */
.coupon-list {
height: calc(100vh - 240rpx);
/* #ifdef H5 */
height: calc(100vh - 240rpx - 44px);
/* #endif */
padding: 20rpx;
}
/* 优惠券卡片样式 */
.coupon {
margin-bottom: 20rpx;
}
.coupon-card {
background-color: #ffffff;
border-radius: 12rpx;
overflow: hidden;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
// .ticket {
// display: flex;
// position: relative;
// &::before {
// content: "";
// position: absolute;
// left: -10rpx;
// top: 50%;
// width: 20rpx;
// height: 20rpx;
// border-radius: 50%;
// background-color: #f5f5f5;
// transform: translateY(-50%);
// }
// &::after {
// content: "";
// position: absolute;
// right: -10rpx;
// top: 50%;
// width: 20rpx;
// height: 20rpx;
// border-radius: 50%;
// background-color: #f5f5f5;
// transform: translateY(-50%);
// }
// .left {
// flex: 1;
// padding: 30rpx;
// display: flex;
// align-items: center;
// border-right: 1rpx dashed #e0e0e0;
// .discount {
// display: flex;
// align-items: baseline;
// color: #ff5722;
// margin-right: 20rpx;
// .big {
// font-size: 80rpx;
// font-weight: bold;
// line-height: 1;
// }
// .unit {
// font-size: 28rpx;
// margin-left: 4rpx;
// }
// }
// .introduce {
// flex: 1;
// .title {
// font-size: 28rpx;
// color: #333;
// font-weight: bold;
// margin-bottom: 10rpx;
// }
// .date {
// font-size: 22rpx;
// color: #999;
// margin-bottom: 10rpx;
// }
// .rule {
// font-size: 22rpx;
// color: #52ac41;
// display: flex;
// align-items: center;
// }
// }
// }
// .right {
// width: 180rpx;
// padding: 30rpx 20rpx;
// display: flex;
// flex-direction: column;
// align-items: center;
// justify-content: center;
// .use-btn {
// width: 140rpx;
// height: 60rpx;
// line-height: 60rpx;
// text-align: center;
// background-color: #ff5722;
// color: #ffffff;
// font-size: 24rpx;
// border-radius: 30rpx;
// margin-bottom: 10rpx;
// }
// .status-text {
// font-size: 24rpx;
// margin-bottom: 10rpx;
// &.used {
// color: #999;
// }
// &.expired {
// color: #999;
// }
// }
// .used-time,
// .expired-info {
// font-size: 20rpx;
// color: #999;
// text-align: center;
// }
// .expired-info {
// color: #ff5722;
// }
// }
// }
.ticket {
display: flex;
.left {
width: 70%;
padding: 20rpx;
background-color: white; //rgb(255, 245, 244);
border-radius: 20rpx;
border-right: dashed 2rpx rgb(224, 215, 211);
display: flex;
.picture {
//width: 172rpx;
border-radius: 20rpx;
width: 190rpx;
height: 190rpx;
position: relative;
flex-shrink: 0;
.coupon-type {
font-size: 24rpx;
color: #ffffff;
background-color: #f9ae3d;
padding: 5rpx 10rpx;
border-radius: 10rpx;
margin-right: 10rpx;
position: absolute;
top: 10rpx;
right: 0rpx;
}
}
.introduce {
margin-left: 10rpx;
.top {
color: $uv-warning;
font-size: 28rpx;
.big {
font-size: 60rpx;
font-weight: bold;
margin-right: 10rpx;
max-width: 300rpx;
}
}
.type {
font-size: 28rpx;
color: $uv-info-dark;
}
.date {
margin-top: 10rpx;
font-size: 20rpx;
color: $uv-info-dark;
}
}
}
.right {
width: 30%;
padding: 40rpx 20rpx;
background-color: white; //rgb(255, 245, 244);
border-radius: 20rpx;
display: flex;
align-items: center;
.use {
height: auto;
padding: 0 20rpx;
font-size: 24rpx;
border-radius: 40rpx;
color: #ffffff !important;
background-color: $uv-warning !important;
line-height: 40rpx;
color: rgb(117, 142, 165);
margin-left: 20rpx;
}
.status-text {
font-size: 24rpx;
margin-bottom: 10rpx;
padding: 0 20rpx;
margin-left: 20rpx;
&.used {
color: #999;
}
&.expired {
color: #999;
}
}
.used {
height: auto;
padding: 0 20rpx;
font-size: 24rpx;
border-radius: 40rpx;
//color: #ffffff!important;
//background-color: $u-type-warning!important;
line-height: 40rpx;
//color: rgb(117, 142, 165);
margin-left: 20rpx;
}
}
.right_log {
text-align: center;
width: 30%;
padding: 80rpx 0rpx;
background-color: white; //rgb(255, 245, 244);
border-radius: 20rpx;
align-items: center;
}
}
/* 模态框样式 */
.modal-content {
padding: 30rpx;
}
.use-coupon-btn {
width: 95%;
border-radius: 50rem !important;
background-color: #52ac41 !important;
}
</style>

View File

@ -11,7 +11,7 @@
<!-- 用户信息 -->
<view class="user-info">
<image class="avatar" :src="userAvatar" mode="aspectFill"></image>
<image class="avatar" :src="userAvatar" mode="aspectFill" @tap="goToEditInfo"></image>
<view class="user-detail">
<view class="flex-row" style="align-items: center">
<text class="username">Hi,{{ userName }}</text>
@ -238,6 +238,13 @@ const goToApplyForm = () => {
});
};
//
const goToEditInfo = () => {
uni.navigateTo({
url: `/pages/components/pages/fenxiao/fenxiaorequestform?id=${userId.value}&isEdit=true`,
});
};
//
const showInviteCodePopup = () => {
showInvitePopup.value = true;

View File

@ -1,5 +1,5 @@
<template>
<uv-navbar :fixed="false" title="申请成为分销员" left-arrow @leftClick="$onClickLeft" />
<uv-navbar :fixed="false" :title="isEdit ? '编辑分销员信息' : '申请成为分销员'" left-arrow @leftClick="$onClickLeft" />
<view class="page">
<!-- 头部导航 -->
<!-- <view class="header">
@ -58,8 +58,8 @@
<!-- 提交按钮 -->
<view class="action-area">
<view class="submit-btn" @tap="submitForm">提交</view>
<view class="benefit-box">
<view class="submit-btn" @tap="submitForm">{{ isEdit ? '保存修改' : '提交' }}</view>
<view class="benefit-box" v-if="!isEdit">
<view class="benefit-title" style="font-weight: bold; font-size: 36rpx;">为什么要加入我们</view>
<view class="benefit-item">
<text class="benefit-text">加入我们的分销团队不仅能共享潜力巨大的健康市场红利更是爱的传递</text>
@ -81,11 +81,14 @@ import { ref, reactive } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { useMainStore } from "@/store/store";
import { storeToRefs } from "pinia";
import { createDistributor } from "@/api/distributor";
import { createDistributor, getDistributorInfo, editDistributor } from "@/api/distributor";
const main = useMainStore();
const { isLogin, member } = storeToRefs(main);
//
const isEdit = ref(false);
//
const formData = reactive({
id: "", // ID
@ -155,35 +158,82 @@ const validateForm = () => {
return true;
};
//
const loadDistributorInfo = async (id) => {
try {
const data = await getDistributorInfo({ id });
if (data) {
formData.id = data.id;
formData.name = data.name;
formData.phone = data.phone;
formData.workUnit = data.workUnit || "";
formData.recipientName = data.recipientName;
formData.bankCardNumber = data.bankCardNumber;
formData.bankName = data.bankName;
}
} catch (error) {
console.error("加载分销商信息失败:", error);
uni.showToast({
title: "加载信息失败",
icon: "none",
});
}
};
//
const submitForm = async () => {
if (!validateForm()) return;
uni.showLoading({
title: "提交中...",
title: isEdit.value ? "保存中..." : "提交中...",
});
//
// /
const requestData = {
memberId: member.value.id, // ID
memberId: member.value.id,
name: formData.name,
phone: formData.phone,
workUnit: formData.workUnit || "", //
recipientName: formData.recipientName, //
bankCardNumber: formData.bankCardNumber, //
bankName: formData.bankName, //
workUnit: formData.workUnit || "",
recipientName: formData.recipientName,
bankCardNumber: formData.bankCardNumber,
bankName: formData.bankName,
};
let data = await createDistributor(requestData);
if (data) {
uni.showToast({
title: "申请提交成功",
icon: "success",
});
try {
// let data = await createDistributor(requestData);
let data = null;
if (isEdit.value) {
data = await editDistributor({
id: formData.id,
memberId: member.value.id,
name: formData.name,
phone: formData.phone,
workUnit: formData.workUnit || "",
recipientName: formData.recipientName,
bankCardNumber: formData.bankCardNumber,
bankName: formData.bankName,
});
} else {
data = await createDistributor(requestData);
}
if (data) {
uni.showToast({
title: isEdit.value ? "保存成功" : "申请提交成功",
icon: "success",
});
//
setTimeout(() => {
uni.navigateBack();
}, 1500);
//
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
} catch (error) {
console.error("提交失败:", error);
uni.showToast({
title: "提交失败",
icon: "none",
});
} finally {
uni.hideLoading();
}
};
@ -196,11 +246,18 @@ const goBack = () => {
onLoad((options) => {
if (!isLogin.value) {
uni.navigateTo({ url: "/pages/components/pages/login/login" });
return;
}
// id
if (options && options.id) {
//
if (options && options.isEdit === "true") {
isEdit.value = true;
}
// id
if (options && options.id && options.isEdit === "true") {
formData.id = options.id;
loadDistributorInfo(options.id);
}
});
</script>

View File

@ -77,6 +77,7 @@ const isChecked = ref(false)
const openid = ref(main.openid)
const uToast = ref()
const uCode = ref()
const couponId = ref('');
const captchaStyle = computed(() => {
let style = {};
@ -87,6 +88,12 @@ const captchaStyle = computed(() => {
return style;
});
onLoad(async (options) => {
if (options.couponId) {
couponId.value = options.couponId;
}
})
onShow(() => {
// #ifdef MP-WEIXIN
@ -140,7 +147,13 @@ const loginForWechatMini = async (e) => {
type: 'success'
});
setTimeout(function() {
uni.navigateBack();
if(couponId.value != ''){
uni.redirectTo({
url: '/pages/components/pages/coupons/coupons?couponId=' + couponId.value
})
}else{
uni.navigateBack();
}
}, 2000);
}
}else {

View File

@ -78,9 +78,12 @@ const questionList = ref([
]);
const phoneNumber = ref('');
onLoad((options) => {
if (options && options.phone) {
phoneNumber.value = options.phone;
if (options && options.phoneNumber) {
console.log("电话:==============",options.phoneNumber)
phoneNumber.value = options.phoneNumber;
}
});
@ -121,7 +124,7 @@ const closePopup = () => {
//
const callService = () => {
uni.makePhoneCall({
phoneNumber: '15251830311' //
phoneNumber: phoneNumber.value //
});
}
</script>

View File

@ -248,6 +248,14 @@
<view>优惠金额</view>
<view class="font-weight-bold">{{ order.couponPrice }}</view>
</view>
<view class="pay-cell" v-if="order.couponType == 2">
<view>商品券</view>
<view class="font-weight-bold">{{ order.title }}</view>
</view>
<view class="pay-cell" v-if="order.couponType == 2">
<view>商品券规格</view>
<view class="font-weight-bold">{{ order.couponProductSpec }}</view>
</view>
<view class="pay-cell">
<view>实付金额</view>
<view class="font-weight-bold">{{ order.payPrice }}</view>

View File

@ -1,20 +1,17 @@
<template>
<uv-navbar
:fixed="false"
:title="title"
left-arrow
@leftClick="$onClickLeft"
/>
<uv-navbar :fixed="false" :title="title" left-arrow @leftClick="$onClickLeft" />
<view class="container position-relative w-100 h-100 overflow-hidden">
<uv-empty v-if="coupons.length == 0" mode="coupon"></uv-empty>
<scroll-view scroll-y class="coupon-list">
<view class="wrapper">
<view class="coupon" v-for="(item, index) in coupons" :key="index" @tap="openDetailModal(item, index)">
<view class="taobao">
<view class="ticket" :style="{border: item.id == coupon_id ? '1rpx solid red':''}">
<view class="taobao" v-if="item.couponType == 1">
<view class="ticket" :style="{ border: item.id == coupon_id ? '1rpx solid red' : '' }">
<view class="left">
<image class="picture" :src="item.image" mode="aspectFill"></image>
<image class="picture" :src="item.image" mode="aspectFill">
<view class="coupon-type">满减券</view>
</image>
<view class="introduce">
<view class="top">
@ -22,12 +19,38 @@
<view>{{ item.least }}{{ item.value }}</view>
</view>
<view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{formatDateTime(item.startTime, 'yyyy-MM-dd')}}-{{formatDateTime(item.endTime, 'yyyy-MM-dd')}}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd') }}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 0">
<view v-if="item.id != coupon_id" class="use immediate-use" :round="true" @tap="useCouponWith(item)">立即使用</view>
<view v-else class="use immediate-use" :round="true" @tap="cancelCoupon(item)">取消使用</view>
<view v-if="item.id != coupon_id" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view>
<view v-else class="use immediate-use" :round="true" @tap="cancelCoupon(item)">取消使用
</view>
</view>
</view>
</view>
<view class="taobao" v-if="item.couponType == 2">
<view class="ticket" :style="{ border: item.id == coupon_id ? '1rpx solid red' : '' }">
<view class="left">
<image class="picture" :src="item.image" mode="aspectFill">
<view class="coupon-type">商品券</view>
</image>
<view class="introduce">
<view class="top">
商品名称<text class="big" style=" font-size: 32rpx;">{{ item.productName }}</text>
</view>
<view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd') }}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view>
</view>
</view>
<view class="right" @click.stop="" v-if="activeTabIndex == 0">
<view v-if="item.id != coupon_id" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view>
<view v-else class="use immediate-use" :round="true" @tap="cancelCoupon(item)">取消使用
</view>
</view>
</view>
</view>
@ -37,10 +60,16 @@
<modal custom :show="detailModalVisible" @cancel="closeDetailModal" width="90%">
<view class="modal-content">
<view class="d-flex font-size-extra-lg text-color-base just-content-center mb-20">{{ coupon.title }}</view>
<view class="d-flex font-size-sm text-color-base mb-20">有效期{{formatDateTime(coupon.startTime, 'yyyy-MM-dd')}}{{formatDateTime(coupon.endTime, 'yyyy-MM-dd')}}</view>
<view class="d-flex font-size-sm text-color-base mb-20">领取时间{{formatDateTime(coupon.createTime, 'yyyy-MM-dd')}}</view>
<view class="d-flex font-size-sm text-color-base mb-20">卷价值{{ coupon.least }}{{ coupon.value }}</view>
<view class="d-flex font-size-extra-lg text-color-base just-content-center mb-20">{{ coupon.title }}
</view>
<view class="d-flex font-size-sm text-color-base mb-20">有效期{{ formatDateTime(coupon.startTime,
'yyyy-MM-dd') }}{{ formatDateTime(coupon.endTime, 'yyyy-MM-dd') }}</view>
<view class="d-flex font-size-sm text-color-base mb-20">领取时间{{ formatDateTime(coupon.createTime,
'yyyy-MM-dd') }}</view>
<view class="d-flex font-size-sm text-color-base mb-20" v-if="coupon.couponType == 1">卷价值{{
coupon.least }}{{ coupon.value }}</view>
<view class="d-flex font-size-sm text-color-base mb-20" v-else>券价值使用此券免费领取 {{ coupon.productName }} 一份
</view>
<view class="d-flex font-size-sm text-color-base mb-20">适用范围{{ typeInfo(coupon.type) }}</view>
<view class="d-flex font-size-sm text-color-base mb-20">适用店铺{{ coupon.shopName }}</view>
<view class="d-flex align-items-center just-content-center" v-if="activeTabIndex == 0">
@ -56,16 +85,16 @@
<script setup>
import {
ref,
watch,
toRaw
ref,
watch,
toRaw
} from 'vue'
import { useMainStore } from '@/store/store'
import { storeToRefs } from 'pinia'
import { onLoad,onShow ,onPullDownRefresh,onHide} from '@dcloudio/uni-app'
import { formatDateTime,prePage } from '@/utils/util'
import { onLoad, onShow, onPullDownRefresh, onHide } from '@dcloudio/uni-app'
import { formatDateTime, prePage } from '@/utils/util'
import {
couponMine
couponCanUserMine
} from '@/api/coupon'
const main = useMainStore()
const { isLogin } = storeToRefs(main)
@ -115,8 +144,8 @@ const typeInfo = (type) => {
return '外卖';
}
}
const getCoupons = async() => {
let data = await couponMine({shopId: shop_id.value, type: type.value, page:1, pagesize:10000});
const getCoupons = async () => {
let data = await couponCanUserMine({ shopId: shop_id.value, type: type.value, page: 1, pagesize: 10000 });
uni.stopPullDownRefresh();
if (data) {
coupons.value = data;
@ -151,26 +180,35 @@ const useCoupon = () => {
return;
}
buttonLock.value = true
console.log('coupon:',coupon.value);
if (parseFloat(coupon.value.least) > parseFloat(amount.value)) {
//console.log('pages3:');
uToast.value.show({
message: '订单金额满'+coupon.value.least+'才能使用',
type: 'error'
});
buttonLock.value = false
} else {
main.SET_COUPON(coupon)
console.log('main.myconpon:',main.mycoupon)
console.log('coupon-------:', coupon.value);
//
if (coupon.value.couponType == 1) {
if (parseFloat(coupon.value.least) > parseFloat(amount.value)) {
//console.log('pages3:');
uToast.value.show({
message: '订单金额满' + coupon.value.least + '才能使用',
type: 'error'
});
buttonLock.value = false
} else {
main.SET_COUPON(coupon)
console.log('main.myconpon:', main.mycoupon)
//prePage().coupon = coupon.value;
//prePage().coupons = 1; //
uni.navigateBack({
})
}
} else if (coupon.value.couponType == 2) {
main.SET_COUPON(coupon)
console.log('main.myconpon:', main.mycoupon)
//prePage().coupon = coupon.value;
//prePage().coupons = 1; //
uni.navigateBack({
})
}
}
@ -182,6 +220,7 @@ const useCoupon = () => {
page {
height: 100%;
}
/* #endif */
.container {
@ -192,7 +231,7 @@ page {
.coupon-list {
margin-top: 30rpx;
height:calc(100vh - 120rpx);
height: calc(100vh - 120rpx);
// height: calc(100vh - 120rpx - 200rpx);
/* #ifdef H5 */
// height: calc(100vh - 120rpx - 200rpx - 44px);
@ -266,25 +305,30 @@ page {
.taobao {
background-color: white;
.title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
font-size: 30rpx;
.left {
display: flex;
align-items: center;
}
.store {
font-weight: 500;
}
.buddha {
width: 70rpx;
height: 70rpx;
border-radius: 10rpx;
margin-right: 10rpx;
}
.entrance {
color: $uv-info;
border: solid 2rpx $uv-info;
@ -294,8 +338,10 @@ page {
border-radius: 15px;
}
}
.ticket {
display: flex;
.left {
width: 70%;
padding: 20rpx;
@ -303,27 +349,48 @@ page {
border-radius: 20rpx;
border-right: dashed 2rpx rgb(224, 215, 211);
display: flex;
.picture {
//width: 172rpx;
border-radius: 20rpx;
width: 190rpx;
height: 190rpx;
position: relative;
flex-shrink: 0;
.coupon-type {
font-size: 24rpx;
color: #ffffff;
background-color: #f9ae3d;
padding: 5rpx 10rpx;
border-radius: 10rpx;
margin-right: 10rpx;
position: absolute;
top: 10rpx;
right: 0rpx;
}
}
.introduce {
margin-left: 10rpx;
.top {
color: $uv-warning;
font-size: 28rpx;
.big {
font-size: 60rpx;
font-weight: bold;
margin-right: 10rpx;
max-width: 300rpx;
}
}
.type {
font-size: 28rpx;
color: $uv-info-dark;
}
.date {
margin-top: 10rpx;
font-size: 20rpx;
@ -331,6 +398,7 @@ page {
}
}
}
.right {
width: 30%;
padding: 40rpx 20rpx;
@ -338,6 +406,7 @@ page {
border-radius: 20rpx;
display: flex;
align-items: center;
.use {
height: auto;
padding: 0 20rpx;
@ -349,6 +418,7 @@ page {
color: rgb(117, 142, 165);
margin-left: 20rpx;
}
.used {
height: auto;
padding: 0 20rpx;
@ -361,6 +431,7 @@ page {
margin-left: 20rpx;
}
}
.right_log {
text-align: center;
width: 30%;

View File

@ -48,7 +48,19 @@
</template>
<template>
<list-cell arrow class="meal-time" v-if="orderType == 'takein'">
<list-cell v-if="applicablePromotions.length > 0">
<view class="flex-fill d-flex justify-content-between align-items-center">
<view class="text-color-base">优惠活动</view>
<radio-group @change="onPromotionChange">
<block v-for="(promotion, index) in applicablePromotions" :key="promotion.id">
<radio :value="promotion.id" :checked="promotion.id === selectedPromotion.id" />
<text>{{ promotion.name }}</text>
</block>
</radio-group>
</view>
</list-cell>
<list-cell class="meal-time" v-if="orderType == 'takein'">
<view class="flex-fill d-flex justify-content-between align-items-center" @click="takeinTIme = !takeinTIme">
<view class="title">取餐时间</view>
<view class="time">
@ -174,19 +186,40 @@
<!-- <view v-if="orderType == 'takeout'">餐盒费({{ store.canheDesc}}){{ store.canhePrice }}</view> -->
<view v-if="coupons == 0" class="text-color-base">暂无可用</view>
<view v-else-if="coupon.title" class="text-color-danger">
<view v-else-if="selectedPromotion" class="text-color-base">
参与优惠活动无法使用优惠券
</view>
<view v-else-if="coupon.title && coupon.couponType == 1" class="text-color-danger">
{{ coupon.title }}({{ coupon.least }}{{ coupon.value }})
</view>
<view v-else-if="coupon.title && coupon.couponType == 2" class="text-color-danger">
{{ coupon.productName }}
</view>
<view v-else class="text-color-primary">可用优惠券{{ coupons }}</view>
</view>
</list-cell>
<list-cell v-if="coupon.title && coupon.couponType == 2 && couponProduct != null">
<view class="good-properties">
<view class="property-item" v-for="(item, index) in couponProduct.productAttr" :key="index">
<view class="property-name">
<text class="name">{{ item.attrName }}</text>
</view>
<view class="property-values">
<view class="property-value" v-for="(value, key) in item.attrValueArr" :key="key"
:class="{ selected: value == selectedSpecs[index] }" @tap="changeSpecDefault(index, key, false)">
{{ value }}
</view>
</view>
</view>
</view>
</list-cell>
<list-cell last>
<view class="flex-fill d-flex justify-content-end align-items-center">
<view>
总计{{ total }}
<!-- <text v-if="orderType == 'takeout'">,配送费{{ store.deliveryPrice }}</text> -->
<text v-if="coupon.value">,-{{ coupon.value }}</text>
<text v-if="coupon.value && coupon.couponType == 1">,-{{ coupon.value }}</text>
,实付
</view>
<view class="font-size-extra-lg font-weight-bold">{{ amount }}</view>
@ -200,7 +233,7 @@
<!-- 支付方式 begin -->
<view class="payment">
<list-cell last :hover="false"><text>支付方式</text></list-cell>
<!-- <list-cell>
<!-- <list-cell>
<view class="d-flex align-items-center justify-content-between w-100 disabled"
@click="setPayType('yue')">
<view class="iconfont iconbalance line-height-100 payment-icon"></view>
@ -303,7 +336,57 @@ import { formatDateTime, isWeixin } from "@/utils/util";
import debounce from "@/uni_modules/uv-ui-tools/libs/function/debounce";
import { orderSubmit, payUnify, getWechatConfig } from "@/api/order";
import { getGoods } from '@/api/goods';
import { couponCount } from "@/api/coupon";
import { getPromotionactivityInfo } from "@/api/activity"; //
const promotionActivities = ref([]); //
const selectedPromotion = ref(null); //
const showPromotionModal = ref(false); //
const applicablePromotions = ref([]); //
const selectedPromotionId = ref(null);
//
// const changeSpecDefault = (index, key) => {
// selectedSpecs.value[index] = couponProduct.value.productAttr[index].attrValueArr[key];
// console.log(selectedSpecs, "-------------")
// };
const valueStr = ref('');
const changeSpecDefault = (index, key, isDefault) => {
// console.log("good:", good.value);
if (isDefault) {
selectedSpecs.value = [];
for (let i = 0; i < couponProduct.value.productAttr.length; i++) {
selectedSpecs.value[i] = couponProduct.value.productAttr[i].attrValueArr[0];
}
} else {
selectedSpecs.value[index] = couponProduct.value.productAttr[index].attrValueArr[key];
}
valueStr.value = selectedSpecs.value.join(",");
//
const sortedArray = valueStr.value.split(",").sort((a, b) => {
return a.charCodeAt(0) - b.charCodeAt(0); // Unicode
});
//
valueStr.value = sortedArray.join(",");
// console.log('valueStr:000000000000', valueStr.value);
// console.log('selectedSpecs.value:', selectedSpecs.value);
};
const onPromotionChange = (e) => {
// 1. ID
const selectedId = e.detail.value;
// 2. ID
selectedPromotion.value = applicablePromotions.value.find(
p => p.id == parseInt(selectedId)
);
selectedPromotionId.value = selectedPromotion.value.id
// console.log("ssssssssssssssss", selectedPromotion.value);
};
// #ifdef H5
import * as jweixin from "weixin-js-sdk";
// #endif
@ -371,6 +454,10 @@ const subscribeMss = ref({
}); //
const uToast = ref();
//
const couponProduct = ref(null);
const selectedSpecs = ref([]);
// script
// const getPackingFee = computed(() => {
// // boxFee
@ -380,7 +467,15 @@ const getPackingFee = computed(() => {
if (orderType.value === 'takein' && !needPacking.value) {
return 0;
}
return cart.value.reduce((acc, cur) => acc + cur.number * (cur.boxFee || 0), 0);
let totalboxFee = cart.value.reduce((acc, cur) => acc + cur.number * (cur.boxFee || 0), 0);
//
if (coupon.value && coupon.value.couponType == 2) {
// console.log(":", couponProduct.value?.boxFee);
totalboxFee += parseFloat(couponProduct.value?.boxFee || 0);
}
return totalboxFee;
});
// total
@ -392,12 +487,19 @@ const total = computed(() => {
total += parseFloat(store.value.deliveryPrice);
}
return total.toFixed(2);
});
// amount
const amount = computed(() => {
let amount = cart.value.reduce((acc, cur) => acc + cur.number * cur.price, 0) + getPackingFee.value;
let amount;
if (selectedPromotion.value != null) {
amount = cart.value.reduce((acc, cur) => acc + cur.number * cur.price * selectedPromotion.value.discountRate, 0) + getPackingFee.value;
} else {
amount = cart.value.reduce((acc, cur) => acc + cur.number * cur.price, 0) + getPackingFee.value;
}
//
if (store.value.distance > 0 && orderType.value == "takeout" && store.value.freeDeliveryPrice != 1) {
@ -405,7 +507,7 @@ const amount = computed(() => {
}
//
if (main.mycoupon.hasOwnProperty("id")) {
if (main.mycoupon.hasOwnProperty("id") && main.mycoupon.couponType == 1) {
amount -= parseFloat(main.mycoupon.value);
}
return amount.toFixed(2);
@ -413,6 +515,15 @@ const amount = computed(() => {
onShow(() => {
coupon.value = main.mycoupon;
// console.log(coupon.value,"--------------")
if (coupon.value.couponType == 2) {
getGoods(coupon.value.productId).then(res => {
couponProduct.value = res;
// console.log(couponProduct.value, "couponProduct.value==============");
changeSpecDefault(0, 0, true);
})
}
let date = new Date(new Date().getTime() + 1800000); //
let hour = date.getHours();
let minute = date.getMinutes();
@ -451,8 +562,33 @@ onHide(() => {
subscribeMss.value = [];
coupons.value = 0;
});
onLoad((option) => {
onLoad(async (option) => {
//
coupon.value = {};
main.DEL_COUPON();
// ID
if (option.promotionId) {
// id,id
// console.log(selectedPromotionId.value,"--------");
console.log(option.promotionId, "option.promotionId11111111111")
if (option.promotionId != null && option.promotionId != undefined && option.promotionId != 'undefined' && option.promotionId != '') {
selectedPromotionId.value = option.promotionId;
}
console.log(selectedPromotionId.value, "--------");
}
cart.value = uni.getStorageSync("cart");
//
await loadPromotionActivities();
//
isPromotionActivity();
if (option.remark) {
form.value.remark = option.remark;
}
@ -460,6 +596,10 @@ onLoad((option) => {
if (option.distributorId) {
distributorId.value = option.distributorId;
}
});
const getSubscribeMss = async () => {
@ -572,6 +712,13 @@ const chooseAddress = () => {
});
};
const goToPackages = () => {
if (selectedPromotion.value) {
uToast.value.show({
message: '参与优惠活动无法使用优惠券',
type: 'error'
});
return
}
let newamount = amount.value;
let coupon_id = coupon.value.id ? coupon.value.id : 0;
let type = orderType.value == "takein" ? 1 : 2;
@ -687,6 +834,8 @@ const pay = async () => {
couponId: coupon.value.id ? coupon.value.id : 0, // id
distributorId: distributorId.value ? distributorId.value : null, // id
needPacking: needPacking.value, //
promotionActivityId: selectedPromotion.value ? selectedPromotion.value.id : 0, // id
couponProductSpec: valueStr.value,
};
cart.value.forEach((item, index) => {
@ -883,6 +1032,88 @@ const fillMobile = () => {
});
}
};
const loadPromotionActivities = async () => {
try {
const data = { pageNo: 1, pageSize: 100, shopId: store.value.id ? store.value.id : 0 }
const res = await getPromotionactivityInfo(data);
// console.log("res:", res);
if (res) {
promotionActivities.value = res.list; // data
console.log("promotionActivities活动信息000000000:", promotionActivities.value);
} else {
console.error('获取优惠活动失败:', res.msg);
}
} catch (error) {
console.error('获取优惠活动异常:', error);
}
};
//
const isPromotionActivity = () => {
// console.log("===============", cart.value);
// applicablePromotions.value = [];
const applicablePromotionsCopy = [];
if (promotionActivities.value && promotionActivities.value.length > 0) {
promotionActivities.value.forEach(promo => {
const productIds = promo.productId ? promo.productId.split(',') : [];
//
let activityAmount = 0;
let activityCount = 0;
cart.value.forEach(item => {
if (productIds.includes(String(item.id))) {
activityAmount += item.price * item.number;
activityCount += item.number;
}
});
if (activityAmount >= promo.minAmount && activityCount >= promo.minQuantity) {
applicablePromotionsCopy.push(promo);
}
});
console.log(applicablePromotionsCopy.length, "applicablePromotionssssssssssssssss:", applicablePromotionsCopy);
// ID
if (selectedPromotionId.value) {
let hasMatch = false;
for (let i = 0; i < applicablePromotionsCopy.length; i++) {
console.log("11111222222", selectedPromotionId.value, "=======", applicablePromotionsCopy[i].id);
console.log(selectedPromotionId.value == applicablePromotionsCopy[i].id);
if (selectedPromotionId.value == applicablePromotionsCopy[i].id) {
selectedPromotion.value = applicablePromotionsCopy[i];
hasMatch = true;
break;
}
}
console.log("hasMatch", hasMatch);
// ,使
if (!hasMatch && applicablePromotionsCopy.length > 0) {
selectedPromotionId.value = applicablePromotionsCopy[0].id;
selectedPromotion.value = applicablePromotionsCopy[0];
} else if (applicablePromotionsCopy.length < 1) {
selectedPromotionId.value = null;
selectedPromotion.value = null;
}
} else if (applicablePromotionsCopy.length > 0) {
selectedPromotionId.value = applicablePromotionsCopy[0].id;
selectedPromotion.value = applicablePromotionsCopy[0];
}
applicablePromotions.value = applicablePromotionsCopy;
// console.log("", selectedPromotion.value);
// console.log("", applicablePromotions.value);
} else {
selectedPromotionId.value = null;
selectedPromotion.value = null;
}
if (selectedPromotion.value) {
coupon.value = {};
main.DEL_COUPON();
}
};
</script>
<style lang="scss" scoped>
@ -1016,4 +1247,37 @@ const fillMobile = () => {
font-size: 0.8em;
text-align: right;
}
.good-properties {
margin: 20rpx 0;
.property-item {
margin-bottom: 30rpx;
.property-name {
font-size: 28rpx;
color: #666;
margin-bottom: 15rpx;
}
.property-values {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
.property-value {
padding: 10rpx 25rpx;
border: 1rpx solid #52ac41;
border-radius: 8rpx;
transition: all 0.3s;
&.selected {
border-color: #52ac41;
color: #52ac41;
background: #e8f7e6;
}
}
}
}
}
</style>

View File

@ -144,10 +144,6 @@ const takeout = () => {
const coupons = () => {
console.log("--> % orderType:\n", main.orderType)
console.log("--> % isLogin:\n", main.isLogin)
if (!main.isLogin) {
uni.navigateTo({ url: '/pages/components/pages/login/login' })
return
}
uni.navigateTo({
url: '/pages/components/pages/coupons/coupons'
})

217
pages/menu/coupon-float.vue Normal file
View File

@ -0,0 +1,217 @@
<template>
<uv-popup ref="popup" mode="center" width="90%" @change="onChange" :safe-area-inset-bottom="true" class="coupon-float-popup" :zIndex="9999">
<view class="coupon-float-container">
<view class="header">
<image class="icon" src="/static/images/coupon-icon.png" />
<text class="title">我的优惠券</text>
<view class="close-btn" @tap="onClose">
<uv-icon name="close" size="28" color="#999" />
</view>
</view>
<scroll-view scroll-y class="coupon-list">
<uv-empty v-if="coupons.length === 0" mode="coupon" text="暂无可用优惠券"></uv-empty>
<view v-else class="wrapper">
<view class="coupon-card" v-for="(item, index) in coupons" :key="item.id">
<view class="left">
<image class="picture" :src="item.image" mode="aspectFill">
<view class="coupon-type">{{ item.couponType === 1 ? '满减券' : '商品券' }}</view>
</image>
<view class="introduce">
<view class="top">
<view v-if="item.couponType === 1" class="price">
<text></text>
<text class="big">{{ item.value }}</text>
</view>
<view v-if="item.couponType === 2" class="price">
<text>商品</text>
<text class="big">{{ item.productName }}</text>
</view>
<view v-if="item.couponType === 1">{{ item.least }}{{ item.value }}</view>
</view>
<view class="type">{{ item.title }}</view>
<view class="date">有效期{{ formatDate(item.startTime) }} - {{ formatDate(item.endTime) }}</view>
</view>
</view>
<view class="right">
<uv-icon name="gift" size="32" color="#f9ae3d" />
</view>
</view>
</view>
</scroll-view>
</view>
</uv-popup>
</template>
<script setup>
import { defineProps, defineEmits, watch, ref, nextTick } from 'vue'
import { formatDateTime } from '@/utils/util'
const props = defineProps({
show: {
type: Boolean,
default: false
},
coupons: {
type: Array,
default: () => []
}
})
const popup = ref(null)
//
watch(() => props.show, (newVal) => {
// console.log(':', newVal)
if (newVal) {
nextTick(() => {
popup.value?.open()
})
} else {
popup.value?.close()
}
})
watch(() => props.coupons, (newVal) => {
// console.log(':', newVal)
}, { deep: true })
const emit = defineEmits(['close'])
const onChange = (e) => {
// console.log(':', e)
if (!e.show) {
emit('close')
}
}
const formatDate = (date) => formatDateTime(date, 'yyyy-MM-dd')
const onClose = () => {
popup.value?.close()
}
</script>
<style lang="scss" scoped>
.coupon-float-popup {
z-index: 3002;
}
.coupon-float-container {
background: #fff;
border-radius: 24rpx;
padding: 0 0 32rpx 0;
min-height: 400rpx;
max-height: 70vh;
box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.08);
overflow: hidden;
}
.header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding: 32rpx 0 16rpx 0;
.icon {
width: 48rpx;
height: 48rpx;
margin-right: 12rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
color: #f9ae3d;
letter-spacing: 2rpx;
}
.close-btn {
position: absolute;
right: 32rpx;
top: 32rpx;
z-index: 2;
padding: 8rpx;
}
}
.coupon-list {
max-height: 60vh;
min-height: 200rpx;
padding: 0 32rpx 0 32rpx;
}
.wrapper {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.coupon-card {
display: flex;
align-items: stretch;
background: linear-gradient(90deg, #fffbe6 0%, #fff 100%);
border-radius: 18rpx;
box-shadow: 0 2rpx 12rpx rgba(249,174,61,0.08);
padding: 24rpx 20rpx;
position: relative;
.left {
display: flex;
flex: 1;
.picture {
width: 120rpx;
height: 120rpx;
border-radius: 16rpx;
margin-right: 18rpx;
position: relative;
.coupon-type {
position: absolute;
left: 0;
bottom: 0;
background: #f9ae3d;
color: #fff;
font-size: 22rpx;
border-radius: 0 0 0 12rpx;
padding: 2rpx 10rpx;
}
}
.introduce {
display: flex;
flex-direction: column;
justify-content: center;
.top {
font-size: 28rpx;
color: #f9ae3d;
.price {
display: flex;
align-items: baseline;
.big {
font-weight: bold;
margin: 0 8rpx;
}
}
.product {
display: flex;
align-items: baseline;
font-size: 28rpx;
color: #333;
.big {
font-size: 48rpx;
font-weight: bold;
margin: 0 8rpx;
}
}
}
.type {
font-size: 26rpx;
color: #666;
margin: 4rpx 0 0 0;
}
.date {
font-size: 22rpx;
color: #aaa;
margin-top: 6rpx;
}
}
}
.right {
display: flex;
align-items: center;
justify-content: center;
width: 60rpx;
margin-left: 10rpx;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,7 @@
<view class="block_1 flex-row justify-end" @tap="
serv({
type: 'pages',
pages: '/pages/components/pages/coupons/coupons',
pages: '/pages/components/pages/coupons/mycoupons',
})
">
<text class="text_10">去使用</text>
@ -237,7 +237,7 @@ const serv = (item) => {
item.id +
"&name=" +
item.name +
"&phone=" +
"&phoneNumber=" +
item.phone,
});
break;

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -15,7 +15,8 @@
/* 颜色变量 */
/* 行为相关颜色 */
// $color-primary: #ADB838;
$color-primary: #09b4f1;
// $color-primary: #09b4f1;
$color-primary: #52ac41;
$color-success: #4cd964;
$color-warning: #FAB714;
$color-error: #D12E32;