|
|
@@ -0,0 +1,532 @@
|
|
|
+<script lang="ts" setup>
|
|
|
+import { usePagination, useRequest, actionDelegationMiddleware } from 'alova/client'
|
|
|
+import { storeToRefs } from 'pinia'
|
|
|
+import { reactive, ref, watch } from 'vue'
|
|
|
+import { getCategoryList, getNewCouponList } from '@/api/home'
|
|
|
+import CustomNavigationBar from '@/components/CustomNavigationBar.vue'
|
|
|
+import DiscountCoupon from '@/components/DiscountCoupon.vue'
|
|
|
+import SpendAndSaveCoupon from "@/components/NewSpendAndSaveCoupon_large.vue";
|
|
|
+import MescrollUni from '@/components/mescroll.vue'
|
|
|
+import MescrollEmpty from '@/custom-components/custom-mescroll/components/mescroll-empty.vue'
|
|
|
+import { useAreaStore } from '@/store/area'
|
|
|
+
|
|
|
+import { safeAreaInsets } from '@/utils'
|
|
|
+
|
|
|
+definePage({
|
|
|
+ style: {
|
|
|
+ navigationBarTitleText: '优惠券',
|
|
|
+ navigationStyle: 'custom',
|
|
|
+ },
|
|
|
+})
|
|
|
+
|
|
|
+const areaStore = useAreaStore()
|
|
|
+const { currentCity } = storeToRefs(areaStore)
|
|
|
+
|
|
|
+const topSafeAreaHeight = safeAreaInsets?.top || 0
|
|
|
+const activeIndex = ref('0')
|
|
|
+const categoryType = ref('');
|
|
|
+const couponType = ref('0')
|
|
|
+const searchValue = ref('')
|
|
|
+const showLoading = ref(false)
|
|
|
+const isSearching = ref(false)
|
|
|
+let mescroll = null
|
|
|
+
|
|
|
+
|
|
|
+const { send, data, pageCount, loading, page, pageSize, isLastPage, refresh, reload, onSuccess, onError } = usePagination((page, pageSize) => getNewCouponList({
|
|
|
+ pageNo: page,
|
|
|
+ pageSize,
|
|
|
+ name: searchValue.value === '' ? '' : searchValue.value,
|
|
|
+ relatedId: categoryType.value,
|
|
|
+ type: couponType.value === '0' ? '' : couponType.value,
|
|
|
+ country: currentCity.value?.value === 'all' ? '' : currentCity.value?.value,
|
|
|
+}), {
|
|
|
+ initialPage: 1,
|
|
|
+ initialPageSize: 7,
|
|
|
+ append: true,
|
|
|
+ preloadNextPage: false,
|
|
|
+ preloadPreviousPage: false,
|
|
|
+ total: response => response?.total || 0,
|
|
|
+ data: response => response?.records || [],
|
|
|
+ immediate: false,
|
|
|
+ middleware: actionDelegationMiddleware('GetCouponList')
|
|
|
+})
|
|
|
+
|
|
|
+const { send: sendCategoryList, data: categoryList, loading: categoryLoading } = useRequest(getCategoryList, {
|
|
|
+ initialData: [{
|
|
|
+ name: '全部',
|
|
|
+ value: '0',
|
|
|
+ }],
|
|
|
+ immediate: false,
|
|
|
+})
|
|
|
+
|
|
|
+
|
|
|
+watch(() => data.value, (newVal) => {
|
|
|
+ if (newVal.length === 0 && !loading.value) {
|
|
|
+ mescroll.showEmpty()
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ mescroll.removeEmpty()
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// watch(() => searchValue.value, () => {
|
|
|
+// // 判断当前是否是搜索状态
|
|
|
+// if (searchValue.value !== '') {
|
|
|
+// isSearching.value = true
|
|
|
+// } else {
|
|
|
+// isSearching.value = false
|
|
|
+// }
|
|
|
+// })
|
|
|
+
|
|
|
+// ============ mescroll 配置 ============
|
|
|
+const downOption = reactive({
|
|
|
+ use: true,
|
|
|
+ auto: false,
|
|
|
+ bgColor: '#f8f8fa',
|
|
|
+ textColor: '#666',
|
|
|
+ textInOffset: '下拉刷新',
|
|
|
+ textOutOffset: '释放刷新',
|
|
|
+ textLoading: '刷新中...',
|
|
|
+ offset: 80,
|
|
|
+})
|
|
|
+
|
|
|
+const upOption = reactive({
|
|
|
+ use: true,
|
|
|
+ auto: false,
|
|
|
+ noMoreSize: 6,
|
|
|
+ isBounce: true,
|
|
|
+ page: {
|
|
|
+ num: page.value,
|
|
|
+ size: pageSize.value,
|
|
|
+ },
|
|
|
+ empty: {
|
|
|
+ use: true,
|
|
|
+ tip: '暂无优惠券',
|
|
|
+ // btnText: '刷新试试',
|
|
|
+ icon: '/static/images/mescroll-empty1.png',
|
|
|
+ fixed: false,
|
|
|
+ },
|
|
|
+ textNoMore: '--- 已经到底了 ---',
|
|
|
+ toTop: {
|
|
|
+ src: '/static/images/mescroll-totop.png',
|
|
|
+ offset: 1000,
|
|
|
+ bottom: 120,
|
|
|
+ },
|
|
|
+ // 开启虚拟列表优化(大数据量时)
|
|
|
+ virtual: {
|
|
|
+ use: false,
|
|
|
+ rowHeight: 300,
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+function mescrollInit(ref) {
|
|
|
+ mescroll = ref
|
|
|
+}
|
|
|
+
|
|
|
+async function downCallback() {
|
|
|
+
|
|
|
+ await refresh()
|
|
|
+}
|
|
|
+
|
|
|
+async function upCallback() {
|
|
|
+ if (isLastPage.value) {
|
|
|
+ console.log(mescroll)
|
|
|
+ mescroll.endSuccess(0, false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ page.value++
|
|
|
+}
|
|
|
+
|
|
|
+onSuccess(() => {
|
|
|
+ let isNextPage = isLastPage.value
|
|
|
+ // 此处处理alova isLastPage默认值为true,首次请求出现的无法加载下一页问题
|
|
|
+ if (pageCount.value > page.value && isLastPage.value) {
|
|
|
+ isNextPage = false
|
|
|
+ }
|
|
|
+ // mescroll.setPageNum(page.value - 1)
|
|
|
+ mescroll.endSuccess(data.value.length, !isNextPage)
|
|
|
+})
|
|
|
+
|
|
|
+onError(() => {
|
|
|
+ console.log('请求失败')
|
|
|
+ mescroll.showEmpty()
|
|
|
+ mescroll.endErr()
|
|
|
+})
|
|
|
+
|
|
|
+onLoad(async () => {
|
|
|
+ await sendCategoryList()
|
|
|
+ await send()
|
|
|
+})
|
|
|
+
|
|
|
+onHide(() => {
|
|
|
+
|
|
|
+ areaStore.resetCurrentCity();
|
|
|
+})
|
|
|
+
|
|
|
+onUnload(() => {
|
|
|
+ areaStore.resetCurrentCity();
|
|
|
+})
|
|
|
+
|
|
|
+async function tabChange(item) {
|
|
|
+ // 切换tab时,重置上拉加载
|
|
|
+ // mescroll.resetUpScroll(true)
|
|
|
+ // mescroll.showDownScroll()
|
|
|
+ categoryType.value = item.index === 0 ? '' : item.value
|
|
|
+ if (couponType.value !== '0') {
|
|
|
+ couponType.value = '0'
|
|
|
+ }
|
|
|
+ mescroll.endUpScroll(false)
|
|
|
+ mescroll.scrollTo(0, 0)
|
|
|
+ showLoading.value = true
|
|
|
+ await reload()
|
|
|
+ showLoading.value = false
|
|
|
+}
|
|
|
+
|
|
|
+async function search() {
|
|
|
+ if (activeIndex.value !== '0') {
|
|
|
+ activeIndex.value = '0'
|
|
|
+ }
|
|
|
+ categoryType.value = ''
|
|
|
+ if (couponType.value !== '0') {
|
|
|
+ couponType.value = '0'
|
|
|
+ }
|
|
|
+ mescroll.endUpScroll(false)
|
|
|
+ mescroll.scrollTo(0, 0)
|
|
|
+ showLoading.value = true
|
|
|
+ // 判断当前是否是搜索状态
|
|
|
+ if (searchValue.value !== '') {
|
|
|
+ isSearching.value = true
|
|
|
+ } else {
|
|
|
+ isSearching.value = false
|
|
|
+ }
|
|
|
+ await reload()
|
|
|
+ showLoading.value = false
|
|
|
+}
|
|
|
+
|
|
|
+async function clear() {
|
|
|
+ searchValue.value = ''
|
|
|
+ mescroll.endUpScroll(false)
|
|
|
+ mescroll.scrollTo(0, 0)
|
|
|
+ showLoading.value = true
|
|
|
+ await reload()
|
|
|
+ isSearching.value = false
|
|
|
+ showLoading.value = false
|
|
|
+}
|
|
|
+
|
|
|
+function searchChange(value) {
|
|
|
+ searchValue.value = value
|
|
|
+}
|
|
|
+
|
|
|
+function regionSelection() {
|
|
|
+ uni.navigateTo({
|
|
|
+ url: '/pages-A/regionSelection/index',
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+async function couponTypeChange(type) {
|
|
|
+ couponType.value = type
|
|
|
+ mescroll.endUpScroll(false)
|
|
|
+ mescroll.scrollTo(0, 0)
|
|
|
+ showLoading.value = true
|
|
|
+ await reload()
|
|
|
+ showLoading.value = false
|
|
|
+}
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <view class="invite-container">
|
|
|
+ <custom-navigation-bar :show-back="true" title="优惠券" />
|
|
|
+ <view class="invite-content"
|
|
|
+ :style="{ marginTop: `calc(${topSafeAreaHeight}px + 88rpx)`, height: `calc(100vh - ${topSafeAreaHeight}px - 88rpx)` }">
|
|
|
+ <view class="invite-content-search bg-white">
|
|
|
+ <view class="content-search-prefix" @tap="regionSelection">
|
|
|
+ <text>
|
|
|
+ {{ currentCity.name }}
|
|
|
+ </text>
|
|
|
+ <up-icon style="margin-left: 9rpx;margin-right: 20rpx;" name="arrow-down-fill" />
|
|
|
+ </view>
|
|
|
+ <up-input style="height: 100%;" v-model="searchValue" border="none" prefix-icon="search"
|
|
|
+ class="invite-content-search-input" placeholder="请输入关键字" :clearable="true" @confirm="search"
|
|
|
+ @clear="clear" />
|
|
|
+ <view class="content-search-suffix">
|
|
|
+ <view class="search-button">
|
|
|
+ <up-button type="text" @tap="search">
|
|
|
+ 搜索
|
|
|
+ </up-button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view :class="[!isSearching ? 'expanded' : 'collapsed']" class="tabs-container flex mt-20rpx bg-white">
|
|
|
+ <up-tabs v-model:current="activeIndex" :scrollable="true" class="tabs-content" :list="categoryList"
|
|
|
+ line-height="6rpx" line-width="67rpx" line-color="#ed6b66"
|
|
|
+ :active-style="{ fontWeight: '500', fontSize: '30rpx', color: '#ed6b66' }"
|
|
|
+ :inactive-style="{ fontWeight: '400', fontSize: '30rpx', color: '#333333' }"
|
|
|
+ item-style="height: 88rpx;" @change="tabChange" />
|
|
|
+ </view>
|
|
|
+ <view
|
|
|
+ class="coupon-types h-70rpx bg-#ffffff pr-20rpx pl-20rpx flex flex-row flex-nowrap justify-start items-center gap-34rpx">
|
|
|
+ <view class="py-8rpx px-34rpx" :class="{ 'coupon_type_active': couponType === '0' }"
|
|
|
+ @click="couponTypeChange('0')">
|
|
|
+ 全部
|
|
|
+ </view>
|
|
|
+ <view class="py-8rpx px-22rpx" :class="{ 'coupon_type_active': couponType === '2' }"
|
|
|
+ @click="couponTypeChange('2')">
|
|
|
+ 折扣券
|
|
|
+ </view>
|
|
|
+ <view class="py-8rpx px-22rpx" :class="{ 'coupon_type_active': couponType === '3' }"
|
|
|
+ @click="couponTypeChange('3')">
|
|
|
+ 满减券
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="invite-content-list"
|
|
|
+ :style="{ height: `calc(100vh - ${topSafeAreaHeight}px - 70rpx - 88rpx - 20rpx - 176rpx)` }">
|
|
|
+ <mescroll-uni id="mescrollContainer" :down="downOption" :up="upOption" :fixed="false" offset="70"
|
|
|
+ @init="mescrollInit" @down="downCallback" @up="upCallback" @emptyclick="reload">
|
|
|
+ <template v-if="isSearching">
|
|
|
+ <view v-for="(item, index) in data" :key="index" class="mt-27rpx">
|
|
|
+ <view v-if="Array.isArray(item.data)" class="text-34rpx text-#333333 border">{{ item.name }}
|
|
|
+ </view>
|
|
|
+ <view v-for="coupon in item.data" :key="item.templateId" class="invite-content-item">
|
|
|
+ <!-- <invite-item :data="item" /> -->
|
|
|
+ <!-- 优惠券类型:2-折扣券,3-满减券 -->
|
|
|
+ <template v-if="coupon.type === '2'">
|
|
|
+ <discount-coupon :coupon="coupon" />
|
|
|
+ </template>
|
|
|
+ <template v-else-if="coupon.type === '3'">
|
|
|
+ <spend-and-save-coupon :coupon="coupon" />
|
|
|
+ </template>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <view v-for="item in data" :key="item.templateId" class="invite-content-item">
|
|
|
+ <!-- <invite-item :data="item" /> -->
|
|
|
+ <!-- 优惠券类型:2-折扣券,3-满减券 -->
|
|
|
+ <template v-if="item.type === '2'">
|
|
|
+ <discount-coupon :coupon="item" />
|
|
|
+ </template>
|
|
|
+ <template v-else-if="item.type === '3'">
|
|
|
+ <spend-and-save-coupon :coupon="item" />
|
|
|
+ </template>
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
+ <!-- 滚动区域内的loading遮罩 -->
|
|
|
+ <view v-if="showLoading && loading" class="loading-mask">
|
|
|
+ <view class="loading-spinner" />
|
|
|
+ <text class="loading-text">加载中...</text>
|
|
|
+ </view>
|
|
|
+ </mescroll-uni>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.invite-container {
|
|
|
+ // height: 100vh;
|
|
|
+ background-color: #f8f8fa;
|
|
|
+
|
|
|
+ .invite-content {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .invite-content-search {
|
|
|
+ padding: 10rpx 24rpx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+
|
|
|
+ .content-search-prefix {
|
|
|
+ min-width: 127rpx;
|
|
|
+ background-color: #f6f8fc;
|
|
|
+ border-top-left-radius: 10rpx;
|
|
|
+ border-bottom-left-radius: 10rpx;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ text {
|
|
|
+ font-weight: 400;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #333333;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ overflow: hidden;
|
|
|
+ white-space: nowrap;
|
|
|
+ padding-left: 30rpx;
|
|
|
+ max-width: 110rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ right: 0;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ width: 1rpx;
|
|
|
+ height: 23rpx;
|
|
|
+ background: #888888;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .invite-content-search-input {
|
|
|
+ height: 60rpx;
|
|
|
+ background-color: #f6f8fc;
|
|
|
+ padding-left: 20rpx !important;
|
|
|
+ padding-right: 20rpx !important;
|
|
|
+ border-radius: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .content-search-suffix {
|
|
|
+ border-bottom-right-radius: 10rpx;
|
|
|
+ border-top-right-radius: 10rpx;
|
|
|
+ background-color: #f6f8fc;
|
|
|
+
|
|
|
+ .search-button {
|
|
|
+ width: 83rpx;
|
|
|
+ font-weight: 400;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #333333;
|
|
|
+ padding-left: 13rpx;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ &:after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ width: 1rpx;
|
|
|
+ height: 23rpx;
|
|
|
+ background: #888888;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .tabs-container {
|
|
|
+ height: 88rpx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ justify-content: space-around;
|
|
|
+ align-items: center;
|
|
|
+ box-shadow: 0rpx 1rpx 4rpx 0rpx rgba(213, 213, 213, 0.29);
|
|
|
+ overflow: hidden;
|
|
|
+ transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
|
+
|
|
|
+ .tabs-content {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 收起状态 */
|
|
|
+ .tabs-container.collapsed {
|
|
|
+ opacity: 0;
|
|
|
+ max-height: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 展开状态 */
|
|
|
+ .tabs-container.expanded {
|
|
|
+ opacity: 1;
|
|
|
+ max-height: 90rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .coupon-types {
|
|
|
+
|
|
|
+ view {
|
|
|
+ width: 115rpx;
|
|
|
+ height: 38rpx;
|
|
|
+ background-color: #f6f8fc;
|
|
|
+ border-radius: 5rpx;
|
|
|
+ text-align: center;
|
|
|
+ font-weight: 400;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #888888;
|
|
|
+ }
|
|
|
+
|
|
|
+ .coupon_type_active {
|
|
|
+ color: #ed6b66;
|
|
|
+ font-weight: bold;
|
|
|
+ // background-color: #ed6b66;
|
|
|
+ // border: 1px solid #ed6b66;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ .invite-content-list {
|
|
|
+ background-color: #f8f8fa;
|
|
|
+ padding: 0 24rpx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ #mescrollContainer {
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .invite-content-item {
|
|
|
+ margin-top: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ // loading遮罩样式
|
|
|
+ .loading-mask {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background-color: rgba(255, 255, 255, 0.9);
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ z-index: 999;
|
|
|
+ }
|
|
|
+
|
|
|
+ .loading-spinner {
|
|
|
+ width: 40rpx;
|
|
|
+ height: 40rpx;
|
|
|
+ border: 4rpx solid #e0e0e0;
|
|
|
+ border-top-color: #ed6b66;
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: spin 1s linear infinite;
|
|
|
+ }
|
|
|
+
|
|
|
+ .loading-text {
|
|
|
+ margin-top: 16rpx;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes spin {
|
|
|
+ to {
|
|
|
+ transform: rotate(360deg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .accordion-enter-active,
|
|
|
+ .accordion-leave-active {
|
|
|
+ transition: all 0.4s ease;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ .accordion-enter-from,
|
|
|
+ .accordion-leave-to {
|
|
|
+ max-height: 0;
|
|
|
+ opacity: 0;
|
|
|
+ transform: translateY(-10rpx);
|
|
|
+ }
|
|
|
+
|
|
|
+ .accordion-enter-to,
|
|
|
+ .accordion-leave-from {
|
|
|
+ max-height: 88rpx;
|
|
|
+ opacity: 1;
|
|
|
+ transform: translateY(0);
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|