Files
cashier_wx/pages/user/coupon.vue
2025-09-25 11:12:15 +08:00

480 lines
12 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.shopId" class="ipt right" type="text" placeholder="请选择" disabled />
</view>
</view>
<view class="status-wrap">
<view class="item" :class="{ active: querForm.statusActiveIndex == index }" v-for="(item, index) in statusList" :key="item.value" @click="tabChange(index)">
<text class="t">{{ item.label }}</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="item" v-for="item in list.data" :key="item.id">
<view class="top">
<view class="icon"></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.MM.DD') }} - {{ dayjs(item.effectEndTime).format('YYYY.MM.DD') }}</text>
</view>
</view>
<view class="btn">
<text class="t">去使用</text>
</view>
</view>
<view class="btm">
<view class="left">
<text class="t">1可适用门店{{ item.useShops }} 2可适用商品{{ item.foods }}3可使用类型{{ convertValuesToLabels(item.useType) }}</text>
</view>
<view class="right" @click="showDetailHandle(item)">
<text class="t">查看详情</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">
<view class="item">
<text class="t">我是店铺1111</text>
</view>
<view class="item">
<text class="t">我是店铺2222</text>
</view>
<view class="item">
<text class="t">我是店铺3333</text>
</view>
<view class="item">
<text class="t">我是店铺4444</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">1可适用门店{{ selectListItem.useShops }}</view>
<view class="li">2可适用商品{{ selectListItem.foods }}</view>
<view class="li" v-if="selectListItem.useType">3可使用类型{{ convertValuesToLabels(selectListItem.useType) }}</view>
<view class="li">
4可用时间段
<text class="t" v-if="selectListItem.useTimeType == 'all'">全段时间可用</text>
<text class="t" v-else>
{{ selectListItem.useStartTime - selectListItem.useEndTime }}
</text>
</view>
<view class="li">5限量规则{{ selectListItem.getLimit == -10086 ? '无限' : `${selectListItem.getLimit}` }}</view>
<view class="li">6同享规则{{ selectListItem.vipPriceShare ? '与限时折扣同享、与会员价同享' : '不与限时折扣同享、与会员价同享' }}</view>
<view class="li">7其它说明{{ selectListItem.ruleDetails || '无' }}</view>
</view>
</scroll-view>
</view>
</u-popup>
</view>
</template>
<script setup>
import dayjs from 'dayjs';
import { ref, reactive, onMounted } from 'vue';
import { onLoad, onReady, onShow, onPageScroll, onReachBottom } from '@dcloudio/uni-app';
import { APIcouponfindByUserId, APIfindCoupon } from '@/common/api/member.js';
const show = ref(false);
const querForm = ref({
searchValue: '',
shopId: '',
statusActiveIndex: 0
});
// 状态
const statusList = ref([
{
value: 0,
label: '未使用',
bg: '#333333',
color: '#ffffff'
},
{
value: 1,
label: '已使用',
bg: '#F8F8F8',
color: '#999999'
},
{
value: 2,
label: '已失效',
bg: '#F8F8F8',
color: '#999999'
}
]);
const list = reactive({
page: 1,
size: 10,
status: 'nomore',
data: []
});
onReachBottom(() => {
console.log('到底了');
list.page++;
getCouponList();
});
const showDetail = ref(false);
const selectListItem = ref('');
function showDetailHandle(item) {
showDetail.value = true;
selectListItem.value = item;
}
// 搜索
function searchHandle() {
list.page = 1;
getCouponList();
}
// 切换类型
function tabChange(index) {
querForm.value.statusActiveIndex = index;
list.page = 1;
getCouponList();
}
// 获取优惠券列表
async function getCouponList() {
try {
uni.showLoading({
title: '加载中...',
mask: true
});
const res = await APIcouponfindByUserId({
userId: uni.cache.get('userInfo').id,
name: querForm.value.searchValue,
status: statusList.value[querForm.value.statusActiveIndex].value,
shopId: querForm.value.shopId ? querForm.value.shopId : uni.cache.get('shopId'),
page: list.page,
size: list.size
});
if (list.page == 1) {
list.data = res.records;
} else {
list.data.push(...res.records);
}
} catch (error) {
console.log(error);
}
uni.hideLoading();
}
/**
* 将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 ''; // 出错时返回空字符串
}
}
onShow(() => {
getCouponList();
});
</script>
<style>
page {
background-color: #f7f7f7;
}
</style>
<style scoped lang="scss">
.container {
padding: 238upx 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;
.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;
.item {
border-radius: 18upx;
background-color: #fff;
padding: 28upx;
&:not(:first-child) {
margin-top: 28upx;
}
.top {
display: flex;
align-items: center;
padding-bottom: 28upx;
.icon {
$size: 120upx;
width: $size;
height: $size;
background-color: #f7f7f7;
border-radius: 12upx;
}
.info {
flex: 1;
display: flex;
flex-direction: column;
gap: 8upx;
padding-left: 28upx;
.view {
flex: 1;
&.name {
font-size: 32upx;
color: #333;
}
&.time {
.t {
font-size: 24upx;
color: #999;
}
}
}
}
.btn {
width: 120upx;
height: 48upx;
border-radius: 48upx;
display: flex;
align-items: center;
justify-content: center;
background-color: #333;
.t {
font-size: 24upx;
color: #fff;
}
}
}
.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;
.t {
font-size: 24upx;
color: #999;
}
}
.right {
display: flex;
align-items: center;
padding-left: 28upx;
.t {
font-size: 24upx;
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 {
height: 100upx;
border-radius: 12upx;
background-color: #f7f7f7;
margin-bottom: 28upx;
display: flex;
align-items: center;
padding: 28upx;
.t {
font-size: 28upx;
color: #555;
}
}
.ul {
.li {
color: #999;
font-size: 28upx;
padding: 8upx 0;
.t {
color: #999;
font-size: 28upx;
}
}
}
}
}
</style>