Files
cashier_wx/pages/order/coupon.vue

945 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="container">
<view class="header-wrap">
<!-- <view class="search-wrap">
<view class="input-wrap">
<view class="icon left">
<u-icon name="search" size="26"></u-icon>
</view>
<input v-model="querForm.searchValue" class="ipt left" type="text" placeholder="搜索" @confirm="searchHandle" />
</view>
<view class="input-wrap" @click="show = true">
<view class="icon right">
<u-icon name="arrow-right" size="16"></u-icon>
</view>
<input v-model="querForm.shopName" class="ipt right" type="text" placeholder="请选择" disabled />
</view>
</view> -->
<view class="status-wrap">
<view class="item" :class="{ active: querForm.statusActiveIndex == 0 }" @click="tabChange(0)">
<text class="t">商品兑换券 {{returnSelNumber(0)}}</text>
</view>
<view class="item" :class="{ active: querForm.statusActiveIndex == 1 }" @click="tabChange(1)">
<text class="t">折扣优惠券{{returnSelNumber(1)}}</text>
</view>
<view class="icon-wrap"
:style="{ width: `${100 / statusList.length}%`, left: `${(100 / statusList.length) * querForm.statusActiveIndex}%` }">
<image class="active-icon"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAMCAYAAAB4MH11AAACKUlEQVR4AaRTS2sTURT+MpkxUzNtRpuYpBsrqbgQH6BYRHGllloQN+5cuNeV6MKfoj9BwYUgIsXHRqE+SluliFLbBqmENJNMMpMm87qecw1NglJse+HMPeee833nce8oIgpFq7wsGt/fCb9eFrtdfmNd1L+9Jc4Vog6E4jcqqM0/R+3zNOwv09jtshdfSx7mDJwKqAMfIvAAEaFVWkLgWjvOEbVdtNa+Epf4wxmFUNTkfqiDGUkqogDu8icKiKS9nQ9jnR8fqM5AwuJJE/G9JhRF05E8dAqIKeDlrsyiWVxg9f+Fum+uzsFZmtnEGIVxKHsGIFn1bAF6ZlQ6I28D9uIrmYSrkodbfETgw6GubZo9Yzk0kR4Fc7IuE8QUFebJKSTSB/kM4UYd1scnKL18QInm4Vk/6W6qiNoOwpZDuoX2elFWXHrzELW5Z+RzJVbPHca+09doIKq0ZQLW+C44iWbm2ZQSNMqozj5F5f1jWCSVGd4fwerstYUXCOplGcsfxponJqHS7Nlm2UyAWAza0AFkzt1A6uhFqEYaoHsR9BLCpg2vukZVr5IU4dV+yU7oRikkTrHDMI9PIHPhJrhQ9Kxugs6hkkhi8Mh5DI9flyCep6ImOt7uxnF6dgypYxMy1hg7S0/yH3FdSL+mpbIw6CVwVSNX7yM/eQfZS7eQu3wb+St3MTJ1D2nq1iicgZbK9YN7rL866PH1qfGBIWj0v/Do4rrR59vK+A0AAP//GfTndQAAAAZJREFUAwCu+SjIaSGpLwAAAABJRU5ErkJggg==">
</image>
</view>
</view>
</view>
<view class="list-wrap">
<view class="tips">
<text class="t">使用商品兑换券的商品不再计入同享优惠券门槛和折扣计算</text>
</view>
<view class="title-wrap">
<text class="t">可用红包</text>
<text class="n">{{list.canUseCoupons.length}}</text>
</view>
<view class="item" v-for="item in list.canUseCoupons" :key="item.id" @click="changeSelCoupon(item)">
<view class="top">
<view class="icon">
<couponIcon :item="item" />
</view>
<view class="info">
<view class="view name">
<text class="t">{{ item.name }}</text>
</view>
<view class="view time">
<text class="t">{{ dayjs(item.effectStartTime).format('YYYY.M.D') }} -
{{ dayjs(item.effectEndTime).format('YYYY.M.D') }}</text>
</view>
</view>
<view class="btn">
<view class="active" v-if="isActive(item)">
<up-icon name="checkmark-circle-fill" size="24" color="#FF3232"></up-icon>
</view>
<view class="round" v-else></view>
</view>
</view>
<view class="btm">
<view class="left">1可适用门店{{ item.useShops }}
2可适用商品{{ item.foods }}3可使用类型{{ convertValuesToLabels(item.useType) }}</view>
<view class="right" @click.stop="showDetailHandle(item)">
<text class="t">查看详情</text>
</view>
</view>
</view>
<view class="title-wrap">
<text class="t">不可用红包</text>
<text class="n">{{list.noCanUseCoupons.length}}</text>
</view>
<view class="item disabled" v-for="item in list.noCanUseCoupons" :key="item.id">
<view class="top">
<view class="icon">
<couponIcon :item="item" />
</view>
<view class="info">
<view class="view name">
<text class="t">{{ item.name }}</text>
</view>
<view class="view time">
<text class="t">{{ dayjs(item.effectStartTime).format('YYYY.M.D') }} -
{{ dayjs(item.effectEndTime).format('YYYY.M.D') }}</text>
</view>
</view>
<view class="btn">
<view class="round"></view>
</view>
</view>
<view class="btm">
<!-- <view class="left">1可适用门店{{ item.useShops }} 2可适用商品{{ item.foods }}3可使用类型{{ convertValuesToLabels(item.useType) }}</view>
<view class="right" @click.stop="showDetailHandle(item)">
<text class="t">查看详情</text>
</view> -->
<view class="error">
<text class="t t1">不可用原因</text>
<text class="t t2">{{returnNoUseRestrictions(item) }}</text>
</view>
</view>
</view>
</view>
<u-loadmore :status="list.status"></u-loadmore>
<u-popup :show="show" round="20" closeable @close="show = false">
<view class="shoplist-popup">
<view class="title">
<text class="t">店铺列表</text>
</view>
<scroll-view class="popup-list" direction="vertical" @scrollend="scrollBottom">
<view class="item" v-for="item in shopList" :key="item.shopId" @click="selectShopHandle(item)">
<text class="t">{{ item.shopName }}</text>
<text class="intro">地址{{ item.shopAddress }}</text>
</view>
<u-loadmore status="nomore"></u-loadmore>
</scroll-view>
</view>
</u-popup>
<u-popup :show="showDetail" round="20" closeable @close="showDetail = false">
<view class="shoplist-popup">
<view class="title">
<text class="t">详情说明</text>
</view>
<scroll-view class="popup-list" direction="vertical">
<view class="ul">
<view class="li" v-for="(item, index) in selectListItemDetails" :key="index">
{{ index + 1 }}{{ item }}
</view>
</view>
</scroll-view>
</view>
</u-popup>
</view>
</template>
<script setup>
import dayjs from 'dayjs';
import {
ref,
reactive,
onMounted,
computed,
watch
} from 'vue';
import {
onLoad,
onReady,
onShow,
onPageScroll,
onReachBottom,
onBackPress
} from '@dcloudio/uni-app';
import {
APIcouponfindByUserId,
APIfindCoupon,
getCouponShops
} from '@/common/api/member.js';
import {
findCoupon
} from '@/common/api/market/coupon.js';
import couponIcon from '@/pages/user/components/coupon-icon.vue';
import * as UTILS from '@/utils/goods-utils.js'
import {
useCartsStore
} from '@/stores/carts.js';
const cartStore = useCartsStore()
//返回不可用原因
function returnNoUseRestrictions(item) {
if (item.noUseRestrictions) {
return item.noUseRestrictions
}
if (item.canuseResult) {
return item.canuseResult.reason
}
return ''
}
const show = ref(false);
const querForm = ref({
searchValue: '',
shopId: '',
shopName: '',
statusActiveIndex: 0
});
const statusList = ref([{
value: 0,
label: '商品兑换券',
bg: '#333333',
color: '#ffffff'
},
{
value: 1,
label: '折扣优惠券',
bg: '#F8F8F8',
color: '#999999'
}
]);
const list = reactive({
page: 1,
size: 10,
status: 'nomore',
data: [],
noCanUseCoupons: [],
canUseCoupons: []
});
const showDetail = ref(false);
const selectListItem = ref('');
const selectListItemDetails = ref([]);
function showDetailHandle(item) {
showDetail.value = true;
selectListItemDetails.value = [
`可适用门店:${item.useShops}`,
`可适用商品:${item.foods}`,
`可使用类型:${convertValuesToLabels(item.useType)}`,
`可用时间段:${item.useTimeType == 'all' ? '全段时间可用' : `${item.useStartTime} - ${item.useEndTime}`}`,
`限量规则:每人限领${item.getLimit == -10086 ? `无限张` : `${item.getLimit}`},每日最多可使用${item.useLimit == -10086 ? `无限张` : `${item.useLimit}`}`,
`同享规则:${item.vipPriceShare ? '与限时折扣同享、与会员价同享' : '不与限时折扣同享、与会员价同享'}`,
`其它说明:${item.ruleDetails || '无'}`
];
if (item.type == 2 || item.type == 4 || item.type == 6) {
selectListItemDetails.value.splice(2, 0, `使用规则:${item.useRule == 'price_asc' ? '从最低价开始抵扣' : '从最高价开始抵扣'}`);
}
if (item.type == 3) {
selectListItemDetails.value.unshift(`最高抵扣${item.maxDiscountAmount}`);
}
}
function returnSelNumber(index) {
if (index) {
if (couponSel.value.id) {
return '(1)'
} else {
return '(0)'
}
} else {
if (goodsCouponSel.value.id) {
return '(1)'
} else {
return '(0)'
}
}
}
// 搜索
function searchHandle() {
list.page = 1;
list.status = 'nomore';
getCouponList();
}
// 切换类型
function tabChange(index) {
querForm.value.statusActiveIndex = index;
list.page = 1;
list.status = 'nomore';
getCouponList();
}
function changeSelCoupon(item) {
if (querForm.value.statusActiveIndex) {
if (couponSel.value.id == item.id) {
couponSel.value = {
id: ''
}
} else {
couponSel.value = item
}
} else {
if (goodsCouponSel.value.id == item.id) {
goodsCouponSel.value = {
id: ''
}
} else {
goodsCouponSel.value = item
}
}
}
const couponSel = ref({
id: ''
})
const goodsCouponSel = ref({
id: ''
})
const quansSelArr = computed(() => {
return [couponSel.value, goodsCouponSel.value].filter((v) => v.id);
})
function isActive(item) {
if (querForm.value.statusActiveIndex) {
return couponSel.value.id == item.id
} else {
return goodsCouponSel.value.id == item.id
}
}
// 获取优惠券列表
async function getCouponList() {
try {
uni.showLoading({
title: '加载中...',
mask: true
});
const res = await findCoupon({
shopUserId: uni.cache.get('shopUserInfo').id,
});
let canUseGoodsCoupon = []
let canUseDiscountCoupon = []
let noUseGoodsCoupon = []
let noUseDiscountCoupon = []
const user = uni.cache.get('shopUserInfo')
let shopInfo = uni.cache.get('shopInfo') || {}
if (!shopInfo.isMemberPrice) {
shopInfo = {}
}
const goodsOrderPrice = uni.getStorageSync('goodsOrderPrice') || 0
const dinnerType = cartStore.dinnerType
const canDikouGoodsArr = UTILS.returnCanDikouGoods(cartStore.allGoods, [], user);
const shopId = uni.cache.get('shopId')
for (let i = 0; i < res.length; i++) {
const coupon = res[i]
const canuseResult = UTILS.returnCouponCanUse({
canDikouGoodsArr,
coupon,
goodsOrderPrice,
user,
selCoupon: quansSelArr.value,
shopInfo
})
const {
canUse,
reason
} = canuseResult
if (coupon.type == 2) {
if (canUse || goodsCouponSel.value.id == coupon.id) {
canUseGoodsCoupon.push(coupon)
} else {
noUseGoodsCoupon.push({
...coupon,
canuseResult
})
}
} else {
if (canUse || couponSel.value.id == coupon.id) {
canUseDiscountCoupon.push(coupon)
} else {
noUseDiscountCoupon.push({
...coupon,
canuseResult
})
}
}
}
//商品券
canUseGoodsCoupon = canUseGoodsCoupon.map(v => {
const discount = UTILS.returnCouponDiscount(
canDikouGoodsArr,
v,
user,
goodsOrderPrice,
quansSelArr.value,
shopInfo
);
return {
...v,
discount,
discountAmount: discount ? discount.discountPrice : v.discountAmount,
};
})
//非商品券
canUseDiscountCoupon = canUseDiscountCoupon.map(v => {
const discount = UTILS.returnCouponDiscount(
canDikouGoodsArr,
v,
user,
goodsOrderPrice,
quansSelArr.value,
shopInfo
);
return {
...v,
discount,
discountAmount: discount ? discount.discountPrice : v.discountAmount,
}
})
if (querForm.value.statusActiveIndex == 0) {
list.noCanUseCoupons = noUseGoodsCoupon
list.canUseCoupons = canUseGoodsCoupon
} else {
list.noCanUseCoupons = noUseDiscountCoupon
list.canUseCoupons = canUseDiscountCoupon
}
console.log('canUseGoodsCoupon', canUseGoodsCoupon);
console.log('noUseGoodsCoupon', noUseGoodsCoupon);
console.log('canUseDiscountCoupon', canUseDiscountCoupon);
console.log('noUseDiscountCoupon', noUseDiscountCoupon);
} catch (error) {
console.log(error);
}
uni.hideLoading();
}
// 店铺列表滚动到底部了
function scrollBottom() {
console.log('店铺列表滚动到底部了');
}
/**
* 将value数组字符串转换为对应的label拼接字符串
* @param {Array} options - 包含value和label的选项数组格式如[{value: 'xxx', label: 'xxx'}, ...]
* @param {string} valueStr - 包含value的数组字符串格式如'["dine","pickup"]'
* @param {string} separator - 标签拼接分隔符,默认值为'、'
* @returns {string} 拼接后的label字符串如"堂食、自取"
*/
function convertValuesToLabels(valueStr, options, separator = '、') {
try {
options = [{
value: 'dine',
label: '堂食'
},
{
value: 'pickup',
label: '自取'
},
{
value: 'deliv',
label: '配送'
},
{
value: 'express',
label: '快递'
}
];
// 验证输入参数
if (!Array.isArray(options)) {
throw new Error('options必须是数组');
}
if (typeof valueStr !== 'string') {
throw new Error('valueStr必须是字符串');
}
// 解析value数组字符串
const values = JSON.parse(valueStr);
if (!Array.isArray(values)) {
throw new Error('解析后的valueStr必须是数组');
}
// 构建value到label的映射表
const valueLabelMap = new Map();
options.forEach((item) => {
if (item && typeof item.value !== 'undefined' && typeof item.label !== 'undefined') {
valueLabelMap.set(item.value, item.label);
}
});
// 匹配并收集label
const labels = values.map((value) => valueLabelMap.get(value)).filter(Boolean); // 过滤未匹配到的项
// 拼接结果
return labels.join(separator);
} catch (error) {
console.error('转换失败:', error.message);
return ''; // 出错时返回空字符串
}
}
// 选择店铺
function selectShopHandle(item) {
querForm.value.shopId = item.shopId;
querForm.value.shopName = item.shopName;
list.page = 1;
show.value = false;
getCouponList();
}
// 获取当前店铺会员信息
const shopList = ref([]);
async function getCouponShopsAjax() {
try {
const res = await getCouponShops();
shopList.value = res;
} catch (error) {
console.log(error);
}
}
onShow(() => {
const couponArr = cartStore.backendCoupons.filter(v => v.type != 2)
const goodsCouponArr = cartStore.backendCoupons.filter(v => v.type == 2)
if (couponArr.length) {
couponSel.value = couponArr[0]
}
if (goodsCouponArr.length) {
goodsCouponSel.value = goodsCouponArr[0]
}
getCouponList();
});
onLoad(() => {
getCouponShopsAjax();
});
watch(() => quansSelArr.value, (newval) => {
const user = uni.cache.get('shopUserInfo')
let shopInfo = uni.cache.get('shopInfo') || {}
if (!shopInfo.isMemberPrice) {
shopInfo = {}
}
const goodsOrderPrice = uni.getStorageSync('goodsOrderPrice') || 0
const dinnerType = cartStore.dinnerType
const canDikouGoodsArr = UTILS.returnCanDikouGoods(cartStore.allGoods, [], user);
const shopId = uni.cache.get('shopId')
let goodsCoupon = newval.filter(v => v.type == 2)
let otherCoupon = newval.filter(v => v.type != 2)
goodsCoupon = goodsCoupon.map(v => {
const discount = UTILS.returnCouponDiscount(
canDikouGoodsArr,
v,
user,
goodsOrderPrice,
[],
shopInfo
);
return {
...v,
discount,
discountAmount: discount ? discount.discountPrice : v.discountAmount,
}
})
otherCoupon = otherCoupon.map(v => {
const discount = UTILS.returnCouponDiscount(
canDikouGoodsArr,
v,
user,
goodsOrderPrice,
goodsCoupon,
shopInfo
);
return {
...v,
discount,
discountAmount: discount ? discount.discountPrice : v.discountAmount,
}
})
uni.$emit('selCoupon', [...goodsCoupon, ...otherCoupon])
}, {
deep: true
})
</script>
<style>
page {
background-color: #f7f7f7;
}
</style>
<style scoped lang="scss">
.container {
padding: 136upx 28upx 28upx;
}
.header-wrap {
width: 100%;
background-color: #fff;
position: fixed;
top: 0;
left: 0;
z-index: 99;
padding: 28upx;
.search-wrap {
display: flex;
gap: 28upx;
.input-wrap {
flex: 1;
height: 70upx;
border: 1px solid #ececec;
border-radius: 8upx;
position: relative;
&:first-child {
flex: 1.5;
}
.icon {
position: absolute;
top: 50%;
transform: translateY(-50%);
&.left {
left: 14upx;
}
&.right {
right: 14upx;
}
}
.ipt {
width: 100%;
height: 100%;
&.left {
padding-left: 68upx;
}
&.right {
padding: 0 56upx 0 28upx;
}
}
}
}
.status-wrap {
display: flex;
// padding-top: 28upx;
position: relative;
.icon-wrap {
height: 12upx;
position: absolute;
bottom: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease-in-out;
padding-right: 28upx;
.active-icon {
width: 24upx;
height: 12upx;
z-index: 9;
}
}
.item {
flex: 1;
height: 80upx;
display: flex;
align-items: center;
justify-content: center;
.t {
font-size: 32upx;
color: #666666;
transition: all 0.3s ease-in-out 0.1s;
}
&.active {
.t {
color: #e3ad7f;
}
}
}
}
}
.list-wrap {
padding-top: 28upx;
.tips {
.t {
color: #666;
font-size: 24upx;
}
}
.title-wrap {
display: flex;
align-items: center;
gap: 10upx;
padding-top: 28upx;
.t {
font-size: 32upx;
color: #333;
}
.n {
color: #666;
font-size: 24upx;
}
}
.item {
border-radius: 18upx;
background-color: #fff;
padding: 28upx;
&:not(:first-child) {
margin-top: 28upx;
}
&.disabled {
.top {
position: relative;
&::after {
content: '';
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 10;
background-color: rgba(255, 255, 255, 0.6);
}
.btn {
.round {
background-color: #f0f0f0;
}
}
}
}
.top {
display: flex;
align-items: center;
padding-bottom: 28upx;
.icon {
$size: 140upx;
width: $size;
height: $size;
margin-right: 28upx;
}
.info {
flex: 1;
display: flex;
justify-content: center;
flex-direction: column;
gap: 8upx;
padding: 0 28upx;
position: relative;
&::after {
$height: 100upx;
content: '';
height: $height;
border-left: 1upx solid #f7f7f7;
position: absolute;
top: 50%;
margin-top: $height * 0.5 * -1;
left: 0;
}
.view {
flex: 1;
&.name {
font-size: 32upx;
color: #333;
}
&.time {
.t {
font-size: 24upx;
color: #999;
}
}
}
}
.btn {
width: 40upx;
height: 40upx;
display: flex;
align-items: center;
justify-content: center;
.round {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #fff;
border: 1px solid #d9d9d9;
}
}
}
.btm {
display: flex;
align-items: center;
padding: 28upx 0 14upx;
border-top: 1upx solid #f7f7f7;
.left {
flex: 1;
height: 40upx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 24upx;
color: #999;
}
.right {
flex: 1;
display: flex;
align-items: center;
padding-left: 28upx;
justify-content: flex-end;
.t {
font-size: 24upx;
color: #333;
}
}
.error {
flex: 1;
.t {
font-size: 24upx;
&.t1 {
color: #ff1c1c;
}
&.t2 {
color: #333;
}
}
}
}
}
}
.shoplist-popup {
padding: 0 28upx 28upx;
.title {
padding: 28upx 0;
display: flex;
align-items: center;
justify-content: center;
.t {
font-size: 32upx;
font-weight: bold;
color: #333;
}
}
.popup-list {
max-height: 50vh;
.item {
padding: 28upx;
border-radius: 12upx;
background-color: #f7f7f7;
margin-bottom: 28upx;
display: flex;
flex-direction: column;
padding: 28upx;
.t {
font-size: 28upx;
font-weight: bold;
color: #333;
/* 必须设置宽度 */
width: 600upx;
/* 或具体像素值 */
/* 关键属性 */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
/* 显示2行 */
overflow: hidden;
/* 文本溢出省略号 */
text-overflow: ellipsis;
/* 可选:防止行高度,确保计算准确 */
line-height: 1.5;
word-break: break-all;
/* 允许在单词内换行 */
word-wrap: break-word;
/* 允许长单词或URL换行 */
}
.intro {
font-size: 28upx;
color: #999;
/* 必须设置宽度 */
width: 600upx;
/* 或具体像素值 */
/* 关键属性 */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
/* 显示2行 */
overflow: hidden;
/* 文本溢出省略号 */
text-overflow: ellipsis;
/* 可选:防止行高度,确保计算准确 */
line-height: 1.5;
word-break: break-all;
/* 允许在单词内换行 */
word-wrap: break-word;
/* 允许长单词或URL换行 */
}
}
.ul {
.li {
color: #999;
font-size: 28upx;
padding: 8upx 0;
.t {
color: #999;
font-size: 28upx;
}
}
}
}
}
</style>