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

86
App.vue
View File

@ -12,56 +12,78 @@ import {
import { APP_ID } from '@/config' import { APP_ID } from '@/config'
// //
const checkUpdate = () => { const autoUpdate = () => {
// //
if (!uni.canIUse('getUpdateManager')) { if (uni.canIUse("getUpdateManager")) {
uni.showModal({ //
title: '提示',
content: '当前微信版本过低,请升级后使用',
showCancel: false
});
return;
}
const updateManager = uni.getUpdateManager(); const updateManager = uni.getUpdateManager();
//1.
//
updateManager.onCheckForUpdate((res) => { updateManager.onCheckForUpdate((res) => {
//
if (res.hasUpdate) { if (res.hasUpdate) {
uni.showLoading({ title: '检测到新版本,下载中...' }); //
}
});
//
updateManager.onUpdateReady(() => {
uni.hideLoading();
uni.showModal({ uni.showModal({
title: '更新提示', title: "更新提示",
content: '新版本已准备好,立即重启应用?', content: "检测到新版本,是否下载新版本并重启小程序?",
showCancel: false,
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
// //2.
updateManager.applyUpdate(); 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(() => { updateManager.onUpdateFailed(() => {
uni.hideLoading(); //
uni.showModal({ uni.showModal({
title: '提示', title: "已经有新版本了哟~",
content: '更新失败,请删除小程序后重新搜索打开', content: "新版本已经上线啦~,请您删除当前小程序,重新搜索打开哟~",
showCancel: false
}); });
}); });
}; }
onLaunch(() => { onLaunch(() => {
checkUpdate(); autoUpdate();
console.log('App Launch') 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 }) 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); * couponIndex let couponCount = (params = {}) => vm.$u.get('/coupon/count', params);
*/ */

View File

@ -21,3 +21,11 @@ export function getDistributorInfo(id) {
export function getCommissionList(data) { export function getCommissionList(data) {
return api.get(`/order/listByDistributor`, data, { login: true }) 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 }) 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", "path": "pages/coupons/coupons",
"style": { "style": {
"enablePullDownRefresh": true, "enablePullDownRefresh": true,
"navigationBarTitleText": "我的卡券", "navigationBarTitleText": "领取卡券",
"navigationBarTextStyle": "black", "navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff" "navigationBarBackgroundColor": "#ffffff"
} }
}, { }, {
"path": "pages/coupons/mycoupons",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "我的卡券",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
},{
"path": "pages/mine/userinfo", "path": "pages/mine/userinfo",
"style": { "style": {
"navigationBarTitleText": "用户信息", "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> <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"> <view class="container position-relative w-100 h-100 overflow-hidden">
<view class="exchange-box"> <view class="exchange-box">
<view class="input-box"> <view class="input-box">
<input type="text" v-model="exchange_code" placeholder="请输入兑换码" <input type="text" v-model="exchange_code" placeholder="请输入兑换码"
@ -8,15 +9,15 @@
<button type="primary" style="background-color: #52ac41;" @click="exchange">兑换</button> <button type="primary" style="background-color: #52ac41;" @click="exchange">兑换</button>
</view> </view>
</view> </view>
<view class="tabbar"> <!-- <view class="tabbar">
<view class="tab" :class="{ active: activeTabIndex == index }" v-for="(item, index) in tabs" :key="index" <view class="tab" :class="{ active: activeTabIndex == index }" v-for="(item, index) in tabs" :key="index"
@tap="handleTab(index)"> @tap="handleTab(index)">
<view class="title">{{ item.title }}</view> <view class="title">{{ item.title }}</view>
</view> </view>
</view> </view> -->
<view class="flex-fill"> <view class="flex-fill">
<scroll-view scroll-y class="coupon-list" @scrolltolower="getCoupons(activeTabIndex)"> <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> <uv-empty v-if="myCoupons.length == 0" mode="list"></uv-empty>
<view class="coupon" v-for="(item, index) in myCoupons" :key="index" <view class="coupon" v-for="(item, index) in myCoupons" :key="index"
@tap="openDetailModal(item, index)"> @tap="openDetailModal(item, index)">
@ -34,14 +35,18 @@
</view> </view>
<view class="type">{{ item.title }}</view> <view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime, <view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd')}}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view> '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> </view>
<view class="right" @click.stop="" v-if="activeTabIndex == 0"> <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" <view v-if="item.status == 0" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view> @tap="useCouponWith(item)">立即使用</view>
<view v-else class="used">已使用</view> <view v-else class="used">已使用</view>
@ -50,14 +55,17 @@
</view> </view>
</view> </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> <uv-empty v-if="notCoupons.length == 0" mode="list"></uv-empty>
<view class="coupon" v-for="(item, index) in notCoupons" :key="index" <view class="coupon" v-for="(item, index) in notCoupons" :key="index"
@tap="openDetailModal(item, index)"> @tap="openDetailModal(item, index)">
<view class="taobao"> <view class="taobao" v-if="item.couponType == 1">
<view class="ticket"> <view class="ticket">
<view class="left"> <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="introduce">
<view class="top"> <view class="top">
@ -68,14 +76,51 @@
</view> </view>
<view class="type">{{ item.title }}</view> <view class="type">{{ item.title }}</view>
<view class="date u-line-1">{{ formatDateTime(item.startTime, <view class="date u-line-1">{{ formatDateTime(item.startTime,
'yyyy-MM-dd')}}-{{ formatDateTime(item.endTime, 'yyyy-MM-dd') }}</view> '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> </view>
<view class="right" @click.stop="" v-if="activeTabIndex == 0"> <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" <view v-if="item.status == 0" class="use immediate-use" :round="true"
@tap="useCouponWith(item)">立即使用</view> @tap="useCouponWith(item)">立即使用</view>
<view v-else class="used">已使用</view> <view v-else class="used">已使用</view>
@ -86,21 +131,26 @@
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<modal custom :show="detailModalVisible" @cancel="closeDetailModal" width="90%"> <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 class="d-flex font-size-extra-lg text-color-base just-content-center mb-20">{{ coupon.title }}
</view> </view>
<view class="d-flex font-size-sm text-color-base mb-20"> <view class="d-flex font-size-sm text-color-base mb-20">
有效期{{ formatDateTime(coupon.startTime, 'yyyy-MM-dd') }}-{{ formatDateTime(coupon.endTime, 领取期限{{ formatDateTime(coupon.startTime, 'yyyy-MM-dd') }}-{{ formatDateTime(coupon.endTime,
'yyyy-MM-dd')}} 'yyyy-MM-dd') }}
</view> </view>
<view class="d-flex font-size-sm text-color-base mb-20"> <view class="d-flex font-size-sm text-color-base mb-20" v-if="coupon.expiryDays != null">
领取时间{{ formatDateTime(coupon.createTime) }} 有效期自领取后{{ 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>
<view class="d-flex font-size-sm text-color-base mb-20"> <view class="d-flex font-size-sm text-color-base mb-20">
券价值{{ coupon.least }}{{ coupon.value }} 券价值{{ coupon.least }}{{ coupon.value }}
</view> </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 }} 每人限领{{ coupon.limit }}
</view> </view>
<view class="d-flex font-size-sm text-color-base mb-20"> <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"> <view class="d-flex font-size-sm text-color-base mb-20">
适用店铺{{ coupon.shopName }} 适用店铺{{ coupon.shopName }}
</view> </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> <button type="primary" @tap="useCoupon" class="use-coupon-btn">立即使用</button>
</view> </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"> <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>
</view> </view>
</modal> </modal>
@ -138,16 +227,16 @@ import {
couponIndexApi couponIndexApi
} from '@/api/coupon' } from '@/api/coupon'
const main = useMainStore() const main = useMainStore()
const { isLogin } = storeToRefs(main) const { member, isLogin } = storeToRefs(main);
const title = ref('优惠券') const title = ref('领取优惠券')
const tabs = ref([ const tabs = ref([
// {
// title: '', page: 1, pagesize: 10,
// coupons: []
// },
{ {
title: '我的优惠券', page: 1, pagesize: 10, title: '领优惠券', page: 1, pagesize: 10,
coupons: []
},
{
title: '未领优惠券', page: 1, pagesize: 10,
coupons: [] coupons: []
} }
]) ])
@ -160,7 +249,50 @@ const uToast = ref()
const myCoupons = ref([]) const myCoupons = ref([])
const notCoupons = 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(() => { onShow(() => {
// console.log("444444444444", member);
// if (!main.isLogin) {
// uni.navigateTo({ url: '/pages/components/pages/login/login' })
// return
// }
getCoupons(0) getCoupons(0)
}) })
onPullDownRefresh(() => { onPullDownRefresh(() => {
@ -188,9 +320,14 @@ const exchange = async () => {
tabs.value[0].coupons = []; tabs.value[0].coupons = [];
tabs.value[0].page = 1; tabs.value[0].page = 1;
getCoupons(0) getCoupons(0)
tabs.value[1].coupons = []; setTimeout(() => {
tabs.value[1].page = 1; uni.redirectTo({
getCoupons(1) 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 pagesize = tabs.value[type].pagesize;
// //
let data = []; let data = [];
if (type == 0) { // if (type == 1) {
myCoupons.value = await couponMine({ page: page, pagesize: pagesize }); // myCoupons.value = await couponMine({ page: page, pagesize: pagesize });
} // }
// //
// if (type == 1) { // if (type == 1) {
// notCoupons.value = await couponIndexApi({page:page,pagesize:pagesize}); // notCoupons.value = await couponIndexApi({page:page,pagesize:pagesize});
// } // }
if (type == 1) { if (type == 0) {
notCoupons.value = await couponIndexApi({ page: page, pagesize: pagesize }); notCoupons.value = await couponIndexApi({ page: page, pagesize: pagesize });
// //
notCoupons.value = notCoupons.value.filter(notCoupon => notCoupons.value = notCoupons.value.filter(notCoupon =>
@ -273,6 +410,12 @@ const receive = async (coupon, index) => {
type: 'success' type: 'success'
}); });
detailModalVisible.value = false detailModalVisible.value = false
setTimeout(() => {
uni.redirectTo({
url: '/pages/components/pages/coupons/mycoupons'
})
}, 1000)
} }
} }
@ -449,6 +592,7 @@ page {
align-items: center; align-items: center;
} }
.store { .store {
font-weight: 500; font-weight: 500;
} }
@ -486,6 +630,21 @@ page {
border-radius: 20rpx; border-radius: 20rpx;
width: 190rpx; width: 190rpx;
height: 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 { .introduce {
@ -499,6 +658,7 @@ page {
font-size: 60rpx; font-size: 60rpx;
font-weight: bold; font-weight: bold;
margin-right: 10rpx; 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"> <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="user-detail">
<view class="flex-row" style="align-items: center"> <view class="flex-row" style="align-items: center">
<text class="username">Hi,{{ userName }}</text> <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 = () => { const showInviteCodePopup = () => {
showInvitePopup.value = true; showInvitePopup.value = true;

View File

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

View File

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

View File

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

View File

@ -248,6 +248,14 @@
<view>优惠金额</view> <view>优惠金额</view>
<view class="font-weight-bold">{{ order.couponPrice }}</view> <view class="font-weight-bold">{{ order.couponPrice }}</view>
</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 class="pay-cell">
<view>实付金额</view> <view>实付金额</view>
<view class="font-weight-bold">{{ order.payPrice }}</view> <view class="font-weight-bold">{{ order.payPrice }}</view>

View File

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

View File

@ -48,7 +48,19 @@
</template> </template>
<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="flex-fill d-flex justify-content-between align-items-center" @click="takeinTIme = !takeinTIme">
<view class="title">取餐时间</view> <view class="title">取餐时间</view>
<view class="time"> <view class="time">
@ -174,19 +186,40 @@
<!-- <view v-if="orderType == 'takeout'">餐盒费({{ store.canheDesc}}){{ store.canhePrice }}</view> --> <!-- <view v-if="orderType == 'takeout'">餐盒费({{ store.canheDesc}}){{ store.canhePrice }}</view> -->
<view v-if="coupons == 0" class="text-color-base">暂无可用</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 }}) {{ coupon.title }}({{ coupon.least }}{{ coupon.value }})
</view> </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 v-else class="text-color-primary">可用优惠券{{ coupons }}</view>
</view> </view>
</list-cell> </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> <list-cell last>
<view class="flex-fill d-flex justify-content-end align-items-center"> <view class="flex-fill d-flex justify-content-end align-items-center">
<view> <view>
总计{{ total }} 总计{{ total }}
<!-- <text v-if="orderType == 'takeout'">,配送费{{ store.deliveryPrice }}</text> --> <!-- <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>
<view class="font-size-extra-lg font-weight-bold">{{ amount }}</view> <view class="font-size-extra-lg font-weight-bold">{{ amount }}</view>
@ -303,7 +336,57 @@ import { formatDateTime, isWeixin } from "@/utils/util";
import debounce from "@/uni_modules/uv-ui-tools/libs/function/debounce"; import debounce from "@/uni_modules/uv-ui-tools/libs/function/debounce";
import { orderSubmit, payUnify, getWechatConfig } from "@/api/order"; import { orderSubmit, payUnify, getWechatConfig } from "@/api/order";
import { getGoods } from '@/api/goods';
import { couponCount } from "@/api/coupon"; 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 // #ifdef H5
import * as jweixin from "weixin-js-sdk"; import * as jweixin from "weixin-js-sdk";
// #endif // #endif
@ -371,6 +454,10 @@ const subscribeMss = ref({
}); // }); //
const uToast = ref(); const uToast = ref();
//
const couponProduct = ref(null);
const selectedSpecs = ref([]);
// script // script
// const getPackingFee = computed(() => { // const getPackingFee = computed(() => {
// // boxFee // // boxFee
@ -380,7 +467,15 @@ const getPackingFee = computed(() => {
if (orderType.value === 'takein' && !needPacking.value) { if (orderType.value === 'takein' && !needPacking.value) {
return 0; 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 // total
@ -392,12 +487,19 @@ const total = computed(() => {
total += parseFloat(store.value.deliveryPrice); total += parseFloat(store.value.deliveryPrice);
} }
return total.toFixed(2); return total.toFixed(2);
}); });
// amount // amount
const amount = computed(() => { 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) { 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); amount -= parseFloat(main.mycoupon.value);
} }
return amount.toFixed(2); return amount.toFixed(2);
@ -413,6 +515,15 @@ const amount = computed(() => {
onShow(() => { onShow(() => {
coupon.value = main.mycoupon; 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 date = new Date(new Date().getTime() + 1800000); //
let hour = date.getHours(); let hour = date.getHours();
let minute = date.getMinutes(); let minute = date.getMinutes();
@ -451,8 +562,33 @@ onHide(() => {
subscribeMss.value = []; subscribeMss.value = [];
coupons.value = 0; 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"); cart.value = uni.getStorageSync("cart");
//
await loadPromotionActivities();
//
isPromotionActivity();
if (option.remark) { if (option.remark) {
form.value.remark = option.remark; form.value.remark = option.remark;
} }
@ -460,6 +596,10 @@ onLoad((option) => {
if (option.distributorId) { if (option.distributorId) {
distributorId.value = option.distributorId; distributorId.value = option.distributorId;
} }
}); });
const getSubscribeMss = async () => { const getSubscribeMss = async () => {
@ -572,6 +712,13 @@ const chooseAddress = () => {
}); });
}; };
const goToPackages = () => { const goToPackages = () => {
if (selectedPromotion.value) {
uToast.value.show({
message: '参与优惠活动无法使用优惠券',
type: 'error'
});
return
}
let newamount = amount.value; let newamount = amount.value;
let coupon_id = coupon.value.id ? coupon.value.id : 0; let coupon_id = coupon.value.id ? coupon.value.id : 0;
let type = orderType.value == "takein" ? 1 : 2; let type = orderType.value == "takein" ? 1 : 2;
@ -687,6 +834,8 @@ const pay = async () => {
couponId: coupon.value.id ? coupon.value.id : 0, // id couponId: coupon.value.id ? coupon.value.id : 0, // id
distributorId: distributorId.value ? distributorId.value : null, // id distributorId: distributorId.value ? distributorId.value : null, // id
needPacking: needPacking.value, // needPacking: needPacking.value, //
promotionActivityId: selectedPromotion.value ? selectedPromotion.value.id : 0, // id
couponProductSpec: valueStr.value,
}; };
cart.value.forEach((item, index) => { 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -1016,4 +1247,37 @@ const fillMobile = () => {
font-size: 0.8em; font-size: 0.8em;
text-align: right; 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> </style>

View File

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

View File

@ -35,7 +35,8 @@
<view class="iconfont iconarrow-right"></view> <view class="iconfont iconarrow-right"></view>
</view> </view>
<view class="store-location flex-row align-center"> <view class="store-location flex-row align-center">
<text class="small" v-if="store.distance > 0 && orderType == 'takeout'">(配送距离: {{ <text class="small" v-if="store.distance > 0 && orderType == 'takeout'">(配送距离:
{{
store.distance }}km)</text> store.distance }}km)</text>
<text class="small" v-else-if="orderType == 'takeout'">(本店不支持外卖)</text> <text class="small" v-else-if="orderType == 'takeout'">(本店不支持外卖)</text>
</view> </view>
@ -62,18 +63,11 @@
</view> </view>
<!-- #ifdef H5 -->
<view class="content" :style="{ <view class="content" :style="{
height: 'calc(100vh - ' + (headerHeight + (store.notice ? 0 : 60) + footerHeight) + 'rpx)', height: 'calc(100vh - ' + (headerHeight + (store.notice ? 0 : 60) + footerHeight) + 'rpx)',
paddingBottom: '160rpx' paddingBottom: '160rpx'
}"> }">
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="content" :style="{
height: 'calc(100vh - ' + (headerHeight + (store.notice ? 0 : 60) + footerHeight) + 'rpx)',
paddingBottom: '160rpx'
}">
<!-- #endif -->
<scroll-view class="menu-sidebar" :scroll-into-view="menuScrollIntoView" scroll-with-animation <scroll-view class="menu-sidebar" :scroll-into-view="menuScrollIntoView" scroll-with-animation
scroll-y> scroll-y>
<view class="sidebar-wrapper"> <view class="sidebar-wrapper">
@ -105,12 +99,19 @@
<view class="good-card" v-for="(good, key) in item.goodsList" :key="key" <view class="good-card" v-for="(good, key) in item.goodsList" :key="key"
:class="{ 'sold-out': good.stock <= 0, 'large-image': good.bigImage === 1 }"> :class="{ 'sold-out': good.stock <= 0, 'large-image': good.bigImage === 1 }">
<template v-if="good.bigImage === 1"> <template v-if="good.bigImage === 1">
<view class="promotion-large-tags"
v-if="getGoodPromotions(good.id).length > 0">
<text class="promotion-large-tag"
v-for="promo in getGoodPromotions(good.id)" :key="promo.id">{{
promo.name }}</text>
</view>
<image mode="aspectFill" :src="good.image" class="good-image-large" <image mode="aspectFill" :src="good.image" class="good-image-large"
@tap="showGoodDetailModal(item, good)"></image> @tap="showGoodDetailModal(item, good)"></image>
<view class="good-details-large flex-row justify-between"> <view class="good-details-large flex-row justify-between">
<view class="good-info" style="flex: 2; padding-right: 20rpx;"> <view class="good-info" style="flex: 2; padding-right: 20rpx;">
<text class="good-name">{{ good.storeName }}</text> <text class="good-name">{{ good.storeName }}</text>
<text class="good-description">{{ good.storeInfo }}</text> <text class="good-description">{{ good.storeInfo
}}</text>
</view> </view>
<view class="price-action" style="flex: 1;"> <view class="price-action" style="flex: 1;">
<text class="good-price big-good-price">{{ good.price <text class="good-price big-good-price">{{ good.price
@ -149,13 +150,18 @@
</template> </template>
<template v-else> <template v-else>
<view class="promotion-tags"
v-if="getGoodPromotions(good.id).length > 0">
<text class="promotion-tag"
v-for="promo in getGoodPromotions(good.id)" :key="promo.id">{{
promo.name }}</text>
</view>
<image mode="aspectFill" :src="good.image" class="good-image" <image mode="aspectFill" :src="good.image" class="good-image"
@tap="showGoodDetailModal(item, good)"></image> @tap="showGoodDetailModal(item, good)"></image>
<view class="good-details"> <view class="good-details">
<text class="good-name">{{ good.storeName }}</text> <text class="good-name">{{ good.storeName }}</text>
<text class="good-description">{{ good.storeInfo }}</text> <text class="good-description">{{ good.storeInfo }}</text>
<view <view class="price-action flex-row justify-between align-center">
class="price-action flex-row justify-between align-center">
<text class="good-price">{{ good.price }}</text> <text class="good-price">{{ good.price }}</text>
<!-- <view class="action-buttons" v-if="good.stock > 0"> <!-- <view class="action-buttons" v-if="good.stock > 0">
<button type="primary" class="spec-button" <button type="primary" class="spec-button"
@ -236,7 +242,8 @@
<!-- 购物车栏 begin --> <!-- 购物车栏 begin -->
<view class="cart-bar" v-if="cart.length > 0 && isCartShow"> <view class="cart-bar" v-if="cart.length > 0 && isCartShow">
<view class="cart-icon-container"> <view class="cart-icon-container">
<image src="/static/images/menu/cart.png" class="cart-icon" @tap="openCartPopup"></image> <image src="/static/images/menu/cart.png" class="cart-icon" @tap="openCartPopup">
</image>
<view class="cart-badge">{{ getCartGoodsNumber }}</view> <view class="cart-badge">{{ getCartGoodsNumber }}</view>
</view> </view>
<view style="display: flex; flex-direction: column;"> <view style="display: flex; flex-direction: column;">
@ -250,6 +257,20 @@
预估配送费 {{ store.deliveryPrice }} 预估配送费 {{ store.deliveryPrice }}
</view> </view>
</view> </view>
<view class="promotion-selection" v-if="applicablePromotions.length > 0">
<view class="promotion-list">
<!-- 模板部分无需传递参数 -->
<radio-group @change="onPromotionChange" style="transform: scale(0.7);">
<label v-for="promo in applicablePromotions" :key="promo.id" class="">
<view class="promotion-content">
<radio :value="promo.id" style="transform: scale(0.7);"
:checked="promo.id === selectedPromotion.id" />
<text class="promotion-name">{{ promo.name }}</text>
</view>
</label>
</radio-group>
</view>
</view>
<button type="primary" class="checkout-button" @tap="toPay" :disabled="disabledPay" <button type="primary" class="checkout-button" @tap="toPay" :disabled="disabledPay"
:class="{ disabled: disabledPay }"> :class="{ disabled: disabledPay }">
{{ disabledPay ? `${spread}元起送` : '去结算' }} {{ disabledPay ? `${spread}元起送` : '去结算' }}
@ -260,7 +281,7 @@
<!-- 商品详情模态框 begin --> <!-- 商品详情模态框 begin -->
<modal :show="goodDetailModalVisible" class="good-detail-modal" color="#5A5B5C" width="90%" custom <modal :show="goodDetailModalVisible" class="good-detail-modal" color="#5A5B5C" width="90%" custom
padding="0rpx" radius="12rpx"> padding="0rpx" radius="12rpx" @cancel="closeGoodDetailModal">
<template #default> <template #default>
<view class="modal-header"> <view class="modal-header">
<view class="close-button"> <view class="close-button">
@ -372,8 +393,23 @@
</view> </view>
</template> </template>
</uv-popup> </uv-popup>
</view>
<!-- 优惠活动选择弹窗 -->
<modal :show="showPromotionModal" class="promotion-select-modal" color="#5A5B5C" width="80%" custom
padding="20rpx" radius="12rpx">
<template #default>
<view class="modal-header">
<text class="modal-title">选择优惠活动</text>
</view>
<scroll-view class="modal-body" scroll-y>
<view class="promotion-item" v-for="promo in applicablePromotions" :key="promo.id">
<text class="promo-name">{{ promo.name }}</text>
<!-- 根据需要显示更多优惠信息 -->
</view>
</scroll-view>
</template>
</modal>
</view>
<!--轻提示--> <!--轻提示-->
<view class="loading" v-if="loading"> <view class="loading" v-if="loading">
<uv-loading-icon color="#DA5650" size=40 mode="circle"></uv-loading-icon> <uv-loading-icon color="#DA5650" size=40 mode="circle"></uv-loading-icon>
@ -391,6 +427,15 @@
mode="widthFix" /> mode="widthFix" />
</view> </view>
</view> </view>
<!-- 浮窗优惠券按钮 -->
<view class="coupon-float-btn" @tap="showCouponFlo" v-if="!loading">
<image src="/static/images/coupon-icon.png" class="coupon-float-icon" />
<text class="coupon-float-text">优惠券</text>
</view>
<!-- 优惠券弹窗 -->
<coupon-float :show="showCouponFloat" :coupons="allCoupons" @close="closeCouponFloat"/>
</layout> </layout>
</template> </template>
@ -423,6 +468,26 @@ import {
import { import {
menuAds menuAds
} from "@/api/market"; } from "@/api/market";
import { getPromotionactivityInfo } from "@/api/activity"; //
import CouponFloat from './coupon-float.vue'
import { couponMine } from '@/api/coupon'
const promotionActivities = ref([]); //
const selectedPromotion = ref(null); //
const showPromotionModal = ref(false); //
const applicablePromotions = ref([]); //
//
const onPromotionChange = (e) => {
// 1. ID
const selectedId = e.detail.value;
// 2. ID
selectedPromotion.value = applicablePromotions.value.find(
p => p.id == parseInt(selectedId)
);
// console.log("ssssssssssssssss", selectedPromotion.value);
};
const main = useMainStore(); const main = useMainStore();
const { const {
orderType, orderType,
@ -461,6 +526,10 @@ const newkmUnit = computed(() => (param) => {
console.log("param:", param); console.log("param:", param);
return "10km"; return "10km";
}); });
const showCouponFloat = ref(false)
const allCoupons = ref([])
const goodCartNum = computed(() => { const goodCartNum = computed(() => {
// //
return (id) => return (id) =>
@ -491,11 +560,19 @@ const getCartGoodsNumber = computed(() => {
// }); // });
const getCartGoodsPrice = computed(() => { const getCartGoodsPrice = computed(() => {
let total = cart.value.reduce((acc, cur) => { let total = cart.value.reduce((acc, cur) => {
const itemPrice = cur.price * cur.number; let itemPrice = cur.price * cur.number;
//
if (selectedPromotion.value) {
// console.log("11111111111",selectedPromotion.value)
itemPrice = selectedPromotion.value.discountRate * itemPrice;
}
const boxPrice = (cur.boxFee || 0) * cur.number; const boxPrice = (cur.boxFee || 0) * cur.number;
return acc + itemPrice + boxPrice; // boxPrice return acc + itemPrice + boxPrice; // boxPrice
}, 0); }, 0);
// //
return parseFloat(total.toFixed(2)); // parseFloat return parseFloat(total.toFixed(2)); // parseFloat
}); });
@ -522,8 +599,7 @@ uni.$on("refreshMenu", () => {
onPullDownRefresh(() => { onPullDownRefresh(() => {
init(); init();
}); });
onLoad((options) => { onLoad(async (options) => {
// //
if (options && options.q) { if (options && options.q) {
try { try {
@ -546,6 +622,7 @@ onLoad((options) => {
} }
init(); init();
refreshCart(); refreshCart();
nextTick(() => { nextTick(() => {
if (goods.value.length > 0) { if (goods.value.length > 0) {
currentCateId.value = goods.value[0].id; currentCateId.value = goods.value[0].id;
@ -561,10 +638,14 @@ onShow(() => {
//init(); //init();
refreshCart(); refreshCart();
shopAd.value = uni.getStorageSync("shopAd"); shopAd.value = uni.getStorageSync("shopAd");
// //
nextTick(() => { nextTick(() => {
calcLayoutHeights(); calcLayoutHeights();
}); });
//
loadAllCoupons();
}); });
const openCartShow = () => { const openCartShow = () => {
@ -681,6 +762,9 @@ const getShopList = async (res) => {
// uni.stopPullDownRefresh(); // uni.stopPullDownRefresh();
// //
showPopup.value = true showPopup.value = true
//
loadPromotionActivities();
} }
} }
uni.stopPullDownRefresh(); uni.stopPullDownRefresh();
@ -703,9 +787,28 @@ const refreshCart = () => {
} }
cart.value = tmpCart; cart.value = tmpCart;
cartPopupVisible.value = false; cartPopupVisible.value = false;
//
isPromotionActivity();
} }
} }
}; };
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 getAds = async (shop_id) => { const getAds = async (shop_id) => {
let data = await menuAds({ let data = await menuAds({
shop_id: shop_id ? shop_id : 0 shop_id: shop_id ? shop_id : 0
@ -850,9 +953,9 @@ const calcSize = () => {
} }
h += data.height; h += data.height;
item.bottom = h; item.bottom = h;
console.log( // console.log(
`Category ${item.id} calculated: top=${item.top}, height=${data.height}, bottom=${item.bottom}` // `Category ${item.id} calculated: top=${item.top}, height=${data.height}, bottom=${item.bottom}`
); // );
} }
} }
) )
@ -955,7 +1058,12 @@ const handleAddToCart = (cate, newGood, num) => {
}); });
} }
uni.setStorageSync("cart", JSON.parse(JSON.stringify(cart.value))); uni.setStorageSync("cart", JSON.parse(JSON.stringify(cart.value)));
//
isPromotionActivity()
}; };
const handleReduceFromCart = (item, good) => { const handleReduceFromCart = (item, good) => {
const index = cart.value.findIndex((item) => item.id === good.id); const index = cart.value.findIndex((item) => item.id === good.id);
cart.value[index].number -= 1; cart.value[index].number -= 1;
@ -996,6 +1104,22 @@ const handleGoodReduce = (currentGood) => {
cart.value.splice(cartIndex, 1); cart.value.splice(cartIndex, 1);
} }
} }
//
isPromotionActivity();
};
const getGoodPromotions = (productId) => {
if (!promotionActivities.value || promotionActivities.value.length === 0) {
return [];
}
return promotionActivities.value.filter(promo => {
if (promo.productId) {
const productIds = promo.productId.split(',');
return productIds.includes(String(productId));
}
return false;
});
}; };
const showGoodDetailModal = (item, newGood) => { const showGoodDetailModal = (item, newGood) => {
@ -1020,7 +1144,7 @@ const closeGoodDetailModal = () => {
const changePropertyDefault = (index, key, isDefault) => { const changePropertyDefault = (index, key, isDefault) => {
// //
let valueStr = ""; let valueStr = "";
console.log("good:", good.value); // console.log("good:", good.value);
if (isDefault) { if (isDefault) {
newValue.value = []; newValue.value = [];
for (let i = 0; i < good.value.productAttr.length; i++) { for (let i = 0; i < good.value.productAttr.length; i++) {
@ -1033,16 +1157,26 @@ const changePropertyDefault = (index, key, isDefault) => {
// //valueStr = newValue.value.join(',') // //valueStr = newValue.value.join(',')
// } // }
valueStr = newValue.value.join(","); valueStr = newValue.value.join(",");
//
const sortedArray = valueStr.split(",").sort((a, b) => {
return a.charCodeAt(0) - b.charCodeAt(0); // Unicode
});
//
valueStr = sortedArray.join(",");
// let productValue = good.value.productValue[valueStr] // let productValue = good.value.productValue[valueStr]
// if(!productValue) { // if(!productValue) {
// let skukey = JSON.parse(JSON.stringify(newValue.value)) // let skukey = JSON.parse(JSON.stringify(newValue.value))
// skukey.sort((a, b) => a.localeCompare(b))
// //console.log('skukey:',skukey) // //console.log('skukey:',skukey)
// valueStr = skukey.join(',') // valueStr = skukey.sort((a, b) => a.charCodeAt(0) - b.charCodeAt(0)).join(',')
// productValue = good.value.productValue[valueStr] // productValue = good.value.productValue[valueStr]
} }
// console.log('valueStr--------:', valueStr)
let productValue = good.value.productValue[valueStr]; let productValue = good.value.productValue[valueStr];
// console.log('productValue--------------:', productValue)
good.value.number = 1; good.value.number = 1;
good.value.price = parseFloat(productValue.price).toFixed(2); good.value.price = parseFloat(productValue.price).toFixed(2);
good.value.stock = productValue.stock; good.value.stock = productValue.stock;
@ -1065,10 +1199,96 @@ const handleAddToCartInModal = () => {
return; return;
} }
// //
// const currentGoodToAdd = JSON.parse(JSON.stringify(good.value));
// const currentCategory = JSON.parse(JSON.stringify(category.value));
// const currentNum = good.value.number;
// //
// let tempCart = JSON.parse(JSON.stringify(cart.value));
// const existingCartItemIndex = tempCart.findIndex(item => item.id === currentGoodToAdd.id && item.valueStr === currentGoodToAdd.valueStr);
// if (existingCartItemIndex > -1) {
// tempCart[existingCartItemIndex].number += currentNum;
// } else {
// tempCart.push({
// id: currentGoodToAdd.id,
// cate_id: currentCategory.id,
// name: currentGoodToAdd.storeName,
// price: currentGoodToAdd.price,
// number: currentNum,
// image: currentGoodToAdd.image,
// valueStr: currentGoodToAdd.valueStr,
// boxFee: currentGoodToAdd.boxFee || 0,
// aloneSell: currentGoodToAdd.aloneSell,
// });
// }
// let cartTotalAmount = tempCart.reduce((acc, cur) => acc + cur.number * cur.price, 0);
// console.log('good:',good.value,'category:',category.value) // console.log('good:',good.value,'category:',category.value)
handleAddToCart(category.value, good.value, good.value.number); handleAddToCart(category.value, good.value, good.value.number);
closeGoodDetailModal(); closeGoodDetailModal();
}; };
//
const isPromotionActivity = () => {
// console.log("===============", cart.value);
// applicablePromotions.value = [];
const applicablePromotionsCopy = [];
if (promotionActivities.value && promotionActivities.value.length > 0) {
promotionActivities.value.forEach(promo => {
let activityAmount = 0;
let activityCount = 0;
const productIds = promo.productId ? promo.productId.split(',') : [];
//
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);
if (selectedPromotion.value == null) {
selectedPromotion.value = applicablePromotionsCopy[0];
}
if (applicablePromotionsCopy.length == 0) {
selectedPromotion.value = null;
}
if (applicablePromotionsCopy.length == 1) {
selectedPromotion.value = applicablePromotionsCopy[0];
}
applicablePromotions.value = applicablePromotionsCopy;
// console.log("", selectedPromotion.value);
// console.log("", applicablePromotions.value);
}
// console.log('applicablePromotions:', applicablePromotions.value);
// if (applicablePromotions.value.length > 1) {
// //
// showPromotionModal.value = true;
// //
// } else if (applicablePromotions.value.length === 1) {
// //
// selectedPromotion.value = applicablePromotions.value[0];
// // applyPromotionAndAddToCart(currentCategory, currentGoodToAdd, currentNum);
// closeGoodDetailModal();
// } else {
// //
// selectedPromotion.value = null; //
// // handleAddToCart(currentCategory, currentGoodToAdd, currentNum);
// closeGoodDetailModal();
// }
};
const openCartPopup = () => { const openCartPopup = () => {
///popup ///popup
popup.value.open(); popup.value.open();
@ -1092,6 +1312,9 @@ const handleCartClear = () => {
const handleCartItemAdd = (index) => { const handleCartItemAdd = (index) => {
cart.value[index].number += 1; cart.value[index].number += 1;
uni.setStorageSync("cart", JSON.parse(JSON.stringify(cart.value))); uni.setStorageSync("cart", JSON.parse(JSON.stringify(cart.value)));
//
isPromotionActivity();
}; };
const handleCartItemReduce = (index) => { const handleCartItemReduce = (index) => {
if (cart.value[index].number === 1) { if (cart.value[index].number === 1) {
@ -1103,6 +1326,9 @@ const handleCartItemReduce = (index) => {
cartPopupVisible.value = false; cartPopupVisible.value = false;
} }
uni.setStorageSync("cart", JSON.parse(JSON.stringify(cart.value))); uni.setStorageSync("cart", JSON.parse(JSON.stringify(cart.value)));
//
isPromotionActivity();
}; };
const toPay = () => { const toPay = () => {
if (!isLogin.value) { if (!isLogin.value) {
@ -1135,7 +1361,7 @@ const toPay = () => {
}); });
uni.setStorageSync("cart", JSON.parse(JSON.stringify(cart.value))); uni.setStorageSync("cart", JSON.parse(JSON.stringify(cart.value)));
uni.navigateTo({ uni.navigateTo({
url: `/pages/components/pages/pay/pay?distributorId=${distributorId.value}`, url: `/pages/components/pages/pay/pay?distributorId=${distributorId.value}&promotionId=${selectedPromotion.value?.id}`,
}); });
} }
@ -1165,6 +1391,26 @@ const isSpecialPackage = (name) => {
const closePopup = () => { const closePopup = () => {
showPopup.value = false showPopup.value = false
} }
//
const loadAllCoupons = async () => {
const data = await couponMine({ page: 1, pagesize: 100 });
if (data) allCoupons.value = data;
console.log(data,"------------")
console.log(allCoupons.value,"------------")
}
const showCouponFlo = () => {
console.log("我点击了优惠券按钮")
showCouponFloat.value = true;
}
const closeCouponFloat = () => {
showCouponFloat.value = false;
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -1854,6 +2100,41 @@ button[type="primary"] {
padding-bottom: 150px; padding-bottom: 150px;
} }
/* 优惠活动选择弹窗样式 */
.promotion-select-modal .modal-header {
display: flex;
justify-content: center;
align-items: center;
padding-bottom: 20rpx;
border-bottom: 1px solid #eee;
}
.promotion-select-modal .modal-title {
font-size: 30rpx;
font-weight: 500;
}
.promotion-select-modal .modal-body {
max-height: 60vh;
padding-top: 20rpx;
}
.promotion-item {
// padding: 20rpx;
// border-bottom: 1px solid #f5f5f5;
// display: flex;
// justify-content: space-between;
// align-items: center;
}
.promotion-item:last-child {
border-bottom: none;
}
.promo-name {
font-size: 28rpx;
}
.new-spec-button { .new-spec-button {
border-radius: 15rpx; // border-radius: 15rpx; //
margin-right: 10rpx; // margin-right: 10rpx; //
@ -1914,4 +2195,144 @@ button[type="primary"] {
width: 100%; width: 100%;
border-radius: 10px; border-radius: 10px;
} }
.promotion-selection {
// padding: 20rpx;
// background: #fff;
}
.promotion-list {
// margin-top: 10rpx;
}
.promotion-item {
display: flex;
padding: 20rpx 0;
border-bottom: 1px solid #eee;
}
.promotion-content {
// display: flex;
// align-items: center;
// gap: 20rpx;
}
.promotion-name {
font-size: 24rpx;
color: #333;
}
.promotion-tags {
width: 200rpx;
position: absolute;
display: flex;
flex-wrap: wrap;
gap: 8rpx;
/* 标签之间的间距 */
margin-top: 8rpx;
/* 标签与上方内容的间距 */
}
.promotion-tag {
display: inline-flex;
/* 使内容居中 */
align-items: center;
padding: 4rpx 12rpx;
/* 上下左右内边距 */
background-color: #ffe0b2;
/* 柔和的背景色,例如浅橙色 */
color: #e65100;
/* 标签文字颜色,与背景色搭配 */
border: 1rpx solid #ffb74d;
/* 边框 */
border-radius: 8rpx;
/* 圆角 */
font-size: 18rpx;
/* 字体大小 */
font-weight: bold;
/* 字体加粗 */
white-space: nowrap;
/* 防止标签文字换行 */
overflow: hidden;
text-overflow: ellipsis;
/* 文字溢出时显示省略号 */
max-width: 100%;
/* 确保标签不会超出父容器 */
}
.promotion-large-tags {
position: absolute;
display: flex;
flex-wrap: wrap;
gap: 8rpx;
/* 标签之间的间距 */
margin-top: 8rpx;
/* 标签与上方内容的间距 */
}
.promotion-large-tag {
display: inline-flex;
/* 使内容居中 */
align-items: center;
padding: 4rpx 12rpx;
/* 上下左右内边距 */
background-color: #ffe0b2;
/* 柔和的背景色,例如浅橙色 */
color: #e65100;
/* 标签文字颜色,与背景色搭配 */
border: 1rpx solid #ffb74d;
/* 边框 */
border-radius: 8rpx;
/* 圆角 */
font-size: 22rpx;
/* 字体大小 */
font-weight: bold;
/* 字体加粗 */
white-space: nowrap;
/* 防止标签文字换行 */
overflow: hidden;
text-overflow: ellipsis;
/* 文字溢出时显示省略号 */
max-width: 100%;
/* 确保标签不会超出父容器 */
}
/* 您还可以添加一些悬停或点击效果,如果需要的话 */
.promotion-tag:active {
opacity: 0.8;
}
.coupon-float-btn {
width: 120rpx;
height: 120rpx;
position: fixed;
right: 40rpx;
top: 550rpx;
display: flex;
flex-direction: column;
align-items: center;
background: linear-gradient(135deg, #fffbe6 0%, #ffe0b2 100%);
border-radius: 50%;
box-shadow: 0 4rpx 16rpx rgba(249, 174, 61, 0.18);
padding: 18rpx 18rpx 10rpx 18rpx;
cursor: pointer;
transition: box-shadow 0.2s;
&:active {
box-shadow: 0 2rpx 8rpx rgba(249, 174, 61, 0.12);
}
}
.coupon-float-icon {
width: 60rpx;
height: 60rpx;
margin-bottom: 6rpx;
}
.coupon-float-text {
font-size: 22rpx;
color: #f9ae3d;
font-weight: bold;
letter-spacing: 2rpx;
}
</style> </style>

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

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