380 lines
9.9 KiB
Vue
380 lines
9.9 KiB
Vue
<template>
|
||
<view class="container">
|
||
<view class="header-wrap">
|
||
<view class="fixed-header-wrap">
|
||
<scroll-view scroll-x direction="horizontal" class="tabs-wrap" :scroll-left="scrollLeft" scroll-with-animation>
|
||
<view
|
||
class="item"
|
||
:class="{ active: couponType == item.value }"
|
||
v-for="(item, index) in emunList.couponTypes"
|
||
:key="index"
|
||
:data-index="index"
|
||
@click="changeType(item.value, index)"
|
||
>
|
||
<div class="item-flex">
|
||
<text class="t">{{ item.label }}</text>
|
||
</div>
|
||
</view>
|
||
</scroll-view>
|
||
<view class="search-wrap">
|
||
<view class="ipt">
|
||
<u-input
|
||
placeholder="搜索优惠券名称"
|
||
shape="circle"
|
||
prefixIcon="search"
|
||
prefixIconStyle="font-size: 22px;color: #909399"
|
||
clearable
|
||
:customStyle="{ border: 'none', background: '#F9F9F9' }"
|
||
v-model="tableData.query"
|
||
@confirm="searchHandle"
|
||
@clear="searchHandle"
|
||
></u-input>
|
||
</view>
|
||
<div class="btn">
|
||
<u-button shape="circle" type="primary" :customStyle="{ height: '36px' }" @click="searchHandle">搜索</u-button>
|
||
</div>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="my-coupon-item-list">
|
||
<view class="my-coupon-item-item" v-for="item in tableData.list" :key="item.id">
|
||
<my-coupon-item :item="item" @delete="deleteHandle" @editor="editorHandle">
|
||
<view class="my-coupon-item-row-wrap">
|
||
<template v-if="couponType == 5">
|
||
<view class="row">
|
||
<text class="title">赠券门槛</text>
|
||
<text class="info">满{{ item.fullAmount }}元赠送</text>
|
||
</view>
|
||
<view class="row">
|
||
<text class="title">每人限量</text>
|
||
<text class="info">
|
||
<template v-if="item.useLimit == -10086">无限制</template>
|
||
<template v-else>{{ item.useLimit }}张</template>
|
||
</text>
|
||
</view>
|
||
<view class="row">
|
||
<text class="title">每次赠送</text>
|
||
<text class="info">
|
||
<template v-if="item.getLimit == -10086">无限制</template>
|
||
<template v-else>{{ item.getLimit }}张</template>
|
||
</text>
|
||
</view>
|
||
</template>
|
||
<template v-else>
|
||
<view class="row" v-if="couponType == 1">
|
||
<text class="title">使用门槛</text>
|
||
<text class="info">满{{ item.fullAmount }}元减{{ item.discountAmount }}元</text>
|
||
</view>
|
||
<view class="row" v-if="couponType == 2">
|
||
<text class="title">使用门槛</text>
|
||
<text class="info">满{{ item.fullAmount }}元可用</text>
|
||
</view>
|
||
<view class="row" v-if="couponType == 3">
|
||
<text class="title">最大折扣金额</text>
|
||
<text class="info">满{{ item.discountAmount }}元可用</text>
|
||
</view>
|
||
<view class="row">
|
||
<text class="title">发放方式</text>
|
||
<text class="info">用户{{ getEmunListLabel('getType', item.getType) }}</text>
|
||
</view>
|
||
<view class="row">
|
||
<text class="title">有效期</text>
|
||
<text class="info">
|
||
<template v-if="item.validType == 'fixed'">领券后{{ item.validDays }}天过期</template>
|
||
<template v-if="item.validType == 'custom'">
|
||
{{ dayjs(item.validStartTime).format('YYYY-MM-DD') }} 至 {{ dayjs(item.validEndTime).format('YYYY-MM-DD') }}
|
||
</template>
|
||
</text>
|
||
</view>
|
||
</template>
|
||
<view class="row">
|
||
<text class="title">创建时间</text>
|
||
<text class="info">{{ item.createTime }}</text>
|
||
</view>
|
||
</view>
|
||
</my-coupon-item>
|
||
</view>
|
||
</view>
|
||
<u-loadmore :status="tableData.status"></u-loadmore>
|
||
<my-footer-btn :confirmText="`+ 添加${getEmunListLabel('couponTypes', couponType)}`" @confirm="toAddCounpon"></my-footer-btn>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import dayjs from 'dayjs';
|
||
import { ref, reactive, nextTick, getCurrentInstance } from 'vue';
|
||
import go from '@/commons/utils/go.js';
|
||
import { couponPage, couponDel, getConsumerCouponPage, deleteConsumerCoupon } from '@/http/api/market/index.js';
|
||
import { onLoad, onShow, onReachBottom } from '@dcloudio/uni-app';
|
||
import { getEmunListLabel, emunList } from '../utils/couponUtils.js';
|
||
|
||
const instance = getCurrentInstance();
|
||
const couponType = ref(1); // 当前选中的优惠券类型
|
||
const scrollLeft = ref(0); // scroll-view的滚动距离
|
||
|
||
// 切换类型
|
||
function changeType(type, index) {
|
||
couponType.value = type;
|
||
resetGetData();
|
||
calculateCenterScroll(index);
|
||
}
|
||
|
||
// item居中
|
||
const calculateCenterScroll = (activeIndex) => {
|
||
// 1. 查询ScrollView的可视宽度(全局查询,无需ref)
|
||
uni.createSelectorQuery()
|
||
.select('.tabs-wrap')
|
||
.boundingClientRect((scrollViewRect) => {
|
||
if (!scrollViewRect) return;
|
||
const viewWidth = scrollViewRect.width; // ScrollView可视宽度(px)
|
||
const viewHalfWidth = viewWidth / 2; // ScrollView中心位置(px)
|
||
|
||
// 2. 查询所有Tab项的DOM信息(计算选中项左侧总宽度)
|
||
uni.createSelectorQuery()
|
||
.selectAll('.item')
|
||
.boundingClientRect((tabItemsRect) => {
|
||
if (!tabItemsRect || tabItemsRect.length === 0) return;
|
||
|
||
// 3. 计算选中项左侧所有Tab的宽度总和
|
||
let leftTotalWidth = 0;
|
||
for (let i = 0; i < activeIndex; i++) {
|
||
leftTotalWidth += tabItemsRect[i].width; // 累加左侧Tab宽度(px)
|
||
leftTotalWidth += 28 * (uni.getSystemInfoSync().windowWidth / 750); // 累加左侧Tab的margin-left(28rpx转px)
|
||
}
|
||
|
||
// 4. 获取选中项自身的宽度和位置
|
||
const activeItemRect = tabItemsRect[activeIndex];
|
||
const itemHalfWidth = activeItemRect.width / 2; // 选中项中心位置(px)
|
||
|
||
// 5. 计算最终滚动距离(核心公式)
|
||
let targetScrollLeft = leftTotalWidth + itemHalfWidth - viewHalfWidth;
|
||
|
||
// 6. 边界处理:不小于0,不超过最大滚动距离
|
||
uni.createSelectorQuery()
|
||
.select('.tabs-wrap')
|
||
.scrollOffset((scrollOffset) => {
|
||
const maxScrollLeft = scrollOffset.scrollWidth - viewWidth; // 最大滚动距离
|
||
targetScrollLeft = Math.max(0, Math.min(targetScrollLeft, maxScrollLeft));
|
||
scrollLeft.value = targetScrollLeft; // 赋值滚动距离
|
||
console.log('最终滚动距离:', targetScrollLeft);
|
||
})
|
||
.exec();
|
||
})
|
||
.exec();
|
||
})
|
||
.exec();
|
||
};
|
||
|
||
// 搜索
|
||
function searchHandle() {
|
||
resetGetData();
|
||
}
|
||
|
||
// 重置获取数据
|
||
function resetGetData() {
|
||
tableData.page = 1;
|
||
tableData.status = 'loading';
|
||
tableData.list = [];
|
||
couponPageAjax();
|
||
}
|
||
|
||
function toAddCounpon() {
|
||
if (couponType.value == 5) {
|
||
go.to('PAGES_ADD_CONSUME_COUPON');
|
||
} else {
|
||
go.to('PAGES_ADD_COUPON', { couponType: couponType.value });
|
||
}
|
||
}
|
||
|
||
// 删除
|
||
function deleteHandle(item) {
|
||
console.log('del', item);
|
||
uni.showModal({
|
||
title: '注意',
|
||
content: `确定要删除${item.title || '该'}优惠券吗?`,
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
uni.showLoading({
|
||
title: '删除中...',
|
||
mask: true
|
||
});
|
||
if (couponType.value == 5) {
|
||
await deleteConsumerCoupon(item.id);
|
||
} else {
|
||
await couponDel(item.id);
|
||
}
|
||
uni.showToast({
|
||
title: '已删除',
|
||
icon: 'none'
|
||
});
|
||
let index = tableData.list.findIndex((val) => val.id == item.id);
|
||
tableData.list.splice(index, 1);
|
||
} catch (error) {
|
||
console.log(error);
|
||
}
|
||
setTimeout(() => {
|
||
uni.hideLoading();
|
||
}, 500);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// 编辑
|
||
function editorHandle(item) {
|
||
console.log('editor', item);
|
||
if (couponType.value == 5) {
|
||
go.to('PAGES_ADD_CONSUME_COUPON', {
|
||
couponType: item.couponType,
|
||
id: item.id
|
||
});
|
||
} else {
|
||
go.to('PAGES_ADD_COUPON', {
|
||
couponType: item.couponType,
|
||
id: item.id
|
||
});
|
||
}
|
||
}
|
||
|
||
// 获取优惠券
|
||
const tableData = reactive({
|
||
query: '',
|
||
status: 'loading',
|
||
page: 1,
|
||
size: 10,
|
||
list: []
|
||
});
|
||
async function couponPageAjax() {
|
||
try {
|
||
tableData.status = 'loading';
|
||
let res = null;
|
||
let params = {
|
||
couponType: couponType.value,
|
||
title: tableData.query,
|
||
page: tableData.page,
|
||
size: tableData.size
|
||
};
|
||
|
||
if (couponType.value == 5) {
|
||
res = await getConsumerCouponPage(params);
|
||
res.records.map((item) => {
|
||
item.couponType = 5;
|
||
});
|
||
} else {
|
||
res = await couponPage(params);
|
||
}
|
||
|
||
if (tableData.page == 1) {
|
||
tableData.list = res.records;
|
||
} else {
|
||
tableData.list.push(...res.records);
|
||
}
|
||
if (res.pageNumber >= res.totalPage) {
|
||
tableData.status = 'nomore';
|
||
}
|
||
} catch (error) {
|
||
console.log(error);
|
||
}
|
||
}
|
||
|
||
onReachBottom(() => {
|
||
if (tableData.status != 'nomore') {
|
||
tableData.page++;
|
||
couponPageAjax();
|
||
}
|
||
});
|
||
|
||
onShow(() => {
|
||
couponPageAjax();
|
||
});
|
||
|
||
onLoad((options) => {
|
||
couponType.value = options.couponType;
|
||
const index = emunList.couponTypes.findIndex((item) => item.value == couponType.value);
|
||
setTimeout(() => {
|
||
if (index !== -1) {
|
||
calculateCenterScroll(index);
|
||
}
|
||
}, 100);
|
||
});
|
||
</script>
|
||
|
||
<style>
|
||
page {
|
||
background-color: #f7f7f7;
|
||
}
|
||
</style>
|
||
<style scoped lang="scss">
|
||
.container {
|
||
padding: 28upx;
|
||
}
|
||
.header-wrap {
|
||
$height: 86px;
|
||
$scrollHeight: 30px;
|
||
$color: #318afe;
|
||
width: 100%;
|
||
height: $height;
|
||
.fixed-header-wrap {
|
||
width: 100%;
|
||
height: $height;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 99;
|
||
background-color: #fff;
|
||
.tabs-wrap {
|
||
white-space: nowrap;
|
||
width: 100%;
|
||
height: $scrollHeight;
|
||
box-sizing: border-box;
|
||
.item {
|
||
height: 100%;
|
||
display: inline-block;
|
||
margin-left: 28upx;
|
||
&:last-child {
|
||
margin-right: 28upx;
|
||
}
|
||
&.active {
|
||
.item-flex {
|
||
background-color: #e6f0ff;
|
||
.t {
|
||
color: $color;
|
||
}
|
||
}
|
||
}
|
||
.item-flex {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 20upx;
|
||
border-radius: 4upx;
|
||
background-color: #f7f7fa;
|
||
.t {
|
||
color: #666666;
|
||
font-size: 28upx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.search-wrap {
|
||
height: $height - $scrollHeight;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 10px 28upx 10px;
|
||
gap: 28upx;
|
||
box-sizing: border-box;
|
||
.ipt {
|
||
flex: 1;
|
||
height: 36px;
|
||
}
|
||
.btn {
|
||
width: 160upx;
|
||
height: 36px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|