Răsfoiți Sursa

优化:优惠券布局优化,满减券背景图修改

haiyang 7 luni în urmă
părinte
comite
f9fff59668

+ 15 - 0
src/App.vue

@@ -40,4 +40,19 @@ onHide(() => {
 
 <style lang="scss">
 @import 'uview-plus/index.scss';
+
+@font-face {
+    font-family: "Alibaba PuHuiTi", "PingFang SC", sans-serif;
+    font-weight: normal;
+    font-style: normal;
+    font-display: swap;
+}
+
+/* 应用到所有元素 */
+page,
+body,
+view,
+text {
+    font-family: "Alibaba PuHuiTi", "PingFang SC", sans-serif;
+}
 </style>

+ 377 - 0
src/components/NewSpendAndSaveCoupon_large.vue

@@ -0,0 +1,377 @@
+<script lang="ts" setup>
+import { computed, ref } from 'vue'
+import ShortLinkModal from '@/components/ShortLinkModal.vue'
+import { getLastPartAfterSlash } from '@/utils/couponClassFormat'
+import { getImageUrl } from '@/utils/imageUtil'
+import { useCouponShare } from '../hooks/useCouponShare'
+import { getPlatformSource } from '@/utils/platformSource'
+
+const props = defineProps({
+    coupon: {
+        type: Object,
+        default: () => ({
+            ruleDiscountRate: '0',
+            ruleMinSpendAmount: '0',
+            relatedName: '',
+            ruleDiscountCapAmount: '0',
+            validityType: '1',
+        }),
+    },
+    scrollViewId: {
+        type: String,
+        default: '',
+    },
+    enableCustomShare: {
+        type: Boolean,
+        default: false,
+    }
+})
+const emit = defineEmits(['shareClick'])
+const coupon = computed(() => props.coupon)
+const couponTemplateId = computed(() => coupon.value.templateId || '')
+const deadline = computed(() => {
+    const type = props.coupon.validityType
+    if (type === '2') {
+        return `自领取之日起${props.coupon.validDays}日内使用`
+    }
+    else if (type === '1') {
+        return `限${props.coupon.validStartTime}到${props.coupon.validEndTime}日内使用`
+    }
+    return '长期有效'
+})
+const category = computed(() => {
+    const relatedType = coupon.value.relatedType
+    if (coupon.value?.relatedName && coupon.value?.relatedName !== 'null') {
+        let relatedName = coupon.value.relatedName
+        if (relatedType === '2') {
+            relatedName = getLastPartAfterSlash(coupon.value.relatedName)
+            return `限${relatedName}分类商品使用`
+        }
+        else {
+            return `限${relatedName}商品使用`
+        }
+    }
+    return '不限商品分类'
+})
+
+// 分享弹窗相关 - 使用v-model控制弹窗显示
+const showShareModal = ref(false)
+const couponShareLink = ref('')
+const isExpand = ref(false);
+
+const { shareCoupon, ...shareHooks } = useCouponShare()
+
+// 调用分享弹窗
+async function handleShareCoupon() {
+    // 确保有优惠券数据
+    if (coupon.value.templateId) {
+        // uni.showLoading({
+        //     title: '获取分享链接中...',
+        //     mask: true,
+        // })
+        try {
+            const shareLink = await shareCoupon(couponTemplateId.value)
+            if (shareLink) {
+                couponShareLink.value = shareLink
+                showShareModal.value = true
+                if (props.enableCustomShare) {
+                    emit('shareClick', couponShareLink.value, shareHooks)
+                }
+            }
+        }
+        finally {
+            // uni.hideLoading()
+        }
+    }
+}
+
+// 关闭分享弹窗
+function handleCloseShareModal() {
+    console.log('分享弹窗已关闭')
+}
+</script>
+
+<template>
+    <view class="discount-coupon">
+        <image class="coupon-bg" :src="getImageUrl('@img/index/coupon4.png')" mode="aspectFill" />
+        <view class="discount-coupon-content">
+            <view class="discount-coupon-info-container">
+                <view class="discount-coupon-info-content">
+                    <view class="discount-coupon-info-price">
+                        <text class="discount-coupon-info-number">
+                            {{ coupon?.ruleReductionAmount }}
+                        </text>
+                        <view class="discount-coupon-info-unit">
+                            ¥
+                        </view>
+                    </view>
+                    <view class="discount-coupon-info-desc">
+                        <view class="desc-text">
+                            满{{ coupon.ruleMinSpendAmount }}元可用
+                        </view>
+                        <view class="desc-type" @tap="isExpand = !isExpand">
+                            {{ category }}
+                            <up-icon v-if="isExpand" name="arrow-up-fill" />
+                            <up-icon v-else name="arrow-down-fill" />
+                        </view>
+                    </view>
+                </view>
+                <view class="discount-coupon-info-time">
+                    {{ deadline }}
+                </view>
+            </view>
+            <view class="discount-btn-container">
+                <view class="btn-bg">
+                    <button class="discount-btn" @click="handleShareCoupon">
+                        <view>立即</view>
+                        <view>发券</view>
+                    </button>
+                </view>
+            </view>
+        </view>
+        <!-- <up-transition :show="isExpand" mode="fade-down" duration="300"> -->
+        <view :class="['coupon-info', isExpand ? 'expanded' : 'collapsed']">
+            <view class="info-content">
+                <view class="info-item">
+                    <view class="item-title">券类型:</view>
+                    <view class="item-content">{{ coupon.type === '2' ? '折扣券' : '满减券' }}</view>
+                </view>
+                <view class="info-item">
+                    <view class="item-title">限定地区:</view>
+                    <view class="item-content">
+                        {{ coupon?.locality && coupon?.locality !== 'null' ?
+                            `仅可在${coupon?.locality}地区的商户使用` : '不限地区' }}
+                    </view>
+                </view>
+                <view class="info-item">
+                    <view class="item-title">限定商户:</view>
+                    <view class="item-content">
+                        {{ coupon?.storeName && coupon?.storeName !== 'null' ?
+                            `仅限${coupon?.storeName}商户使用` : '不限商户' }}
+                    </view>
+                </view>
+                <view class="info-item">
+                    <view class="item-title">限定商品:</view>
+                    <view class="item-content">{{ category }}</view>
+                </view>
+                <view class="info-item">
+                    <view class="item-title">限定平台:</view>
+                    <view class="item-content">{{ getPlatformSource(coupon?.platformSource) }}</view>
+                </view>
+            </view>
+        </view>
+        <!-- </up-transition> -->
+        <template v-if="!enableCustomShare">
+            <ShortLinkModal v-model:show-modal="showShareModal" :coupon-id="couponTemplateId" :share-hooks="shareHooks"
+                :share-link="couponShareLink" :scroll-view-id="scrollViewId" @close="handleCloseShareModal" />
+        </template>
+    </view>
+</template>
+
+<style lang="scss" scoped>
+.discount-coupon {
+    width: 100%;
+    max-width: 800rpx;
+    min-height: 209rpx;
+    position: relative;
+
+    .coupon-bg {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 209rpx;
+        object-fit: cover;
+        z-index: 1;
+    }
+
+    .discount-coupon-content {
+        width: 100%;
+        display: flex;
+        flex-direction: row;
+        flex-wrap: nowrap;
+        height: 209rpx;
+        // padding: 14rpx 0 17rpx 12rpx;
+
+        .discount-coupon-info-container {
+            width: 73.47%;
+            display: flex;
+            flex-direction: column;
+            padding: 14rpx 0 17rpx 12rpx;
+            z-index: 1;
+
+            .discount-coupon-info-content {
+                width: 100%;
+                height: 78.09%;
+                display: flex;
+                flex-direction: row;
+                flex-wrap: nowrap;
+                // justify-content: center;
+                gap: 20rpx;
+
+                .discount-coupon-info-price {
+                    width: 199rpx;
+                    display: flex;
+                    flex-direction: row;
+                    flex-wrap: nowrap;
+                    align-items: flex-end;
+                    padding-bottom: 22rpx;
+                    justify-content: center;
+                    padding-left: 10rpx;
+                    gap: 5rpx;
+
+                    .discount-coupon-info-number {
+                        height: 132rpx;
+                        font-weight: 500;
+                        font-size: 80rpx;
+                        color: #f8204b;
+                        line-height: 168rpx;
+                        letter-spacing: -8rpx;
+                    }
+
+                    .discount-coupon-info-unit {
+                        font-weight: 500;
+                        font-size: 27rpx;
+                        color: #f8204b;
+                        line-height: 56rpx;
+                    }
+                }
+
+                .discount-coupon-info-desc {
+                    display: flex;
+                    flex-direction: column;
+                    justify-content: center;
+                    gap: 13rpx;
+
+                    .desc-text {
+                        font-weight: bold;
+                        font-size: 30rpx;
+                        color: #333333;
+                    }
+
+                    .desc-type {
+                        font-weight: 400;
+                        font-size: 22rpx;
+                        color: #333333;
+                        line-height: 30rpx;
+                        display: flex;
+                        flex-wrap: nowrap;
+
+
+                    }
+                }
+            }
+
+            .discount-coupon-info-time {
+                padding-top: 16rpx;
+                padding-bottom: 8rpx;
+                flex: 1;
+                width: 100%;
+                font-weight: 400;
+                font-size: 18rpx;
+                color: #666666;
+                line-height: 18rpx;
+                padding-left: 1rem;
+            }
+        }
+
+        .discount-btn-container {
+            height: 100%;
+            width: 26.53%;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+
+            .btn-bg {
+                width: 125rpx;
+                height: 125rpx;
+                transform: translate(10%, 0);
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                z-index: 2;
+
+                .discount-btn {
+                    font-weight: bold;
+                    font-size: 28rpx;
+                    color: #ffffff;
+                    display: flex;
+                    flex-direction: column;
+                    justify-content: center;
+                    gap: 10rpx;
+                    padding: 0;
+                    background-color: transparent;
+                    border: none;
+                    line-height: 28rpx;
+
+                    &::after {
+                        border: none;
+                        position: unset;
+                        height: inherit;
+                    }
+                }
+            }
+        }
+    }
+
+    .coupon-info {
+        padding: 20rpx 30rpx;
+        width: calc(100% - 82rpx);
+        border-left: 11rpx solid #ffd8d3;
+        border-right: 11rpx solid #ffd8d3;
+        border-bottom: 11rpx solid #ffd8d3;
+        background-color: #fefefe;
+        border-bottom-left-radius: 11rpx;
+        border-bottom-right-radius: 11rpx;
+        overflow: hidden;
+        transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
+        box-shadow: 1rpx 1rpx 10rpx #fff9f4;
+
+        .info-content {
+            display: flex;
+            flex-direction: column;
+            align-items: flex-start;
+            gap: 20rpx;
+
+            .info-item {
+                display: flex;
+                flex-direction: row;
+                flex-wrap: nowrap;
+                align-items: center;
+                justify-content: flex-start;
+                gap: 10rpx;
+                color: #666;
+                font-weight: bold;
+                font-size: 26rpx;
+
+                .item-title {
+                    flex: 1;
+                    width: 160rpx;
+                    font-weight: bolder;
+                }
+            }
+        }
+    }
+
+    /* 收起状态 */
+    .coupon-info.collapsed {
+        opacity: 0;
+        max-height: 0;
+        padding-top: 0;
+        padding-bottom: 0;
+        border-top-width: 0;
+        border-bottom-width: 0;
+    }
+
+    /* 展开状态 */
+    .coupon-info.expanded {
+        opacity: 1;
+        max-height: 500rpx;
+        /* 设置一个足够大的值以容纳所有内容 */
+        padding-top: 20rpx;
+        padding-bottom: 20rpx;
+        border-top-width: 11rpx;
+        border-bottom-width: 11rpx;
+    }
+}
+</style>

+ 16 - 12
src/components/discountCoupon.vue

@@ -42,8 +42,9 @@ const deadline = computed(() => {
 const category = computed(() => {
     const relatedType = coupon.value.relatedType
     if (coupon.value?.relatedName && coupon.value?.relatedName !== 'null') {
-        const relatedName = getLastPartAfterSlash(coupon.value.relatedName)
+        let relatedName = coupon.value.relatedName
         if (relatedType === '2') {
+            relatedName = getLastPartAfterSlash(coupon.value.relatedName)
             return `限${relatedName}分类商品使用`
         }
         else {
@@ -108,10 +109,10 @@ function handleCloseShareModal() {
                         <view class="desc-text">
                             满{{ coupon.ruleMinSpendAmount }}元可用
                         </view>
-                        <view class="desc-type">
+                        <view class="desc-type" @tap="isExpand = !isExpand">
                             {{ category }}
-                            <up-icon v-if="isExpand" name="arrow-up-fill" @tap="isExpand = false" />
-                            <up-icon v-else name="arrow-down-fill" @tap="isExpand = true" />
+                            <up-icon v-if="isExpand" name="arrow-up-fill" />
+                            <up-icon v-else name="arrow-down-fill" />
                         </view>
                     </view>
                 </view>
@@ -206,7 +207,7 @@ function handleCloseShareModal() {
                 flex-direction: row;
                 flex-wrap: nowrap;
                 // justify-content: center;
-                gap: 30rpx;
+                gap: 20rpx;
 
                 .discount-coupon-info-price {
                     width: 199rpx;
@@ -215,19 +216,22 @@ function handleCloseShareModal() {
                     flex-wrap: nowrap;
                     align-items: flex-end;
                     padding-bottom: 22rpx;
-                    justify-content: flex-end;
+                    justify-content: center;
+                    padding-left: 10rpx;
+                    gap: 6rpx;
 
                     .discount-coupon-info-number {
                         height: 132rpx;
                         font-weight: 500;
-                        font-size: 132rpx;
+                        font-size: 80rpx;
                         color: #f8204b;
                         line-height: 168rpx;
+                        letter-spacing: -8rpx;
                     }
 
                     .discount-coupon-info-unit {
                         font-weight: 500;
-                        font-size: 56rpx;
+                        font-size: 36rpx;
                         color: #f8204b;
                         line-height: 56rpx;
                     }
@@ -279,16 +283,16 @@ function handleCloseShareModal() {
             align-items: center;
 
             .btn-bg {
-                width: 115rpx;
-                height: 115rpx;
-                transform: translate(10%, -4%);
+                width: 125rpx;
+                height: 125rpx;
+                transform: translate(10%, -8%);
                 display: flex;
                 justify-content: center;
                 align-items: center;
                 z-index: 2;
 
                 .discount-btn {
-                    font-weight: 500;
+                    font-weight: bold;
                     font-size: 28rpx;
                     color: #ffffff;
                     display: flex;

+ 1 - 1
src/pages-A/newCouponList/index.vue

@@ -5,7 +5,7 @@ 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/SpendAndSaveCoupon_large.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'

+ 32 - 25
src/pages-A/sharePage/index.vue

@@ -90,7 +90,7 @@ import { nextTick, ref } from 'vue'
 import { getCouponDetail } from '@/api/home'
 import { getWxQcCode, markSuccess } from '@/api/receiveCoupon'
 import { useInviteCodeStore } from '@/store/inviteQCCode'
-import { parseUrlParams, safeAreaInsets } from '@/utils'
+import { parseUrlParams, safeAreaInsets, isPcWeixinMp } from '@/utils'
 import { getLastPartAfterSlash } from '@/utils/couponClassFormat'
 
 definePage({
@@ -322,7 +322,6 @@ async function drawCompleteShareImage(ctx, canvas, qrCodeImg) {
 
 // 绘制顶部装饰
 function drawTopDecoration(ctx, width, height, scale) {
-    // 现代化渐变背景 - 美团橙色系
     const gradient = ctx.createLinearGradient(0, 0, width, 0)
     gradient.addColorStop(0, '#FF7A75')
     gradient.addColorStop(0.98, '#ED6B66')
@@ -494,16 +493,20 @@ function drawCouponDescription(ctx, width, height, scale) {
     const relatedType = couponDetail.value.relatedType
     let descriptionText = ''
     if (couponDetail.value?.relatedName && couponDetail.value?.relatedName !== 'null') {
-        const relatedName = getLastPartAfterSlash(couponDetail.value.relatedName)
-
+        let relatedName = couponDetail.value.relatedName
         if (relatedType === '2') {
+            relatedName = getLastPartAfterSlash(couponDetail.value.relatedName)
             descriptionText = `限${relatedName}分类商品使用`
         }
         else {
             descriptionText = `限${relatedName}商品使用`
         }
+    } else if (couponDetail.value?.storeName && couponDetail.value?.storeName !== 'null') {
+        descriptionText = `仅限${couponDetail.value?.storeName}商户使用`
+    } else if (couponDetail.value?.locality && couponDetail.value?.locality !== 'null') {
+        descriptionText = `仅限${couponDetail.value?.locality}使用`
     } else {
-        descriptionText = `不限商品分类使用`
+        throw new Error('无法获取该优惠券内容')
     }
 
     // 使用自动换行函数绘制
@@ -633,27 +636,31 @@ async function drawQRCodeArea(ctx, canvas, qrCodeImg, width, height, scale) {
                     }
                 }
 
-                // 尝试使用临时文件方式
-                try {
-                    // #ifdef MP-WEIXIN
-                    const fs = uni.getFileSystemManager()
-                    const tempFileName = `temp_qr_${Date.now()}.png`
-                    tempFilePath = `${uni.env.USER_DATA_PATH}/${tempFileName}`
-
-                    // 提取base64数据部分
-                    let base64Data = imgSrc
-                    if (imgSrc.includes('base64,')) {
-                        base64Data = imgSrc.split('base64,')[1]
-                    }
+                if (!isPcWeixinMp()) {
+                    // 尝试使用临时文件方式
+                    try {
+                        // #ifdef MP-WEIXIN
+                        const fs = uni.getFileSystemManager()
+                        const tempFileName = `temp_qr_${Date.now()}.png`
+                        tempFilePath = `${uni.env.USER_DATA_PATH}/${tempFileName}`
+
+                        // 提取base64数据部分
+                        let base64Data = imgSrc
+                        if (imgSrc.includes('base64,')) {
+                            base64Data = imgSrc.split('base64,')[1]
+                        }
 
-                    fs.writeFileSync(tempFilePath, base64Data, 'base64')
-                    console.log('Base64已转换为临时文件:', tempFilePath)
-                    // #endif
-                }
-                catch (fileError) {
-                    console.warn('转换base64为临时文件失败,尝试直接使用base64:', fileError)
-                    // 清除临时文件路径,确保使用base64
-                    tempFilePath = ''
+                        fs.writeFileSync(tempFilePath, base64Data, 'base64')
+                        console.log('Base64已转换为临时文件:', tempFilePath)
+                        // #endif
+                    }
+                    catch (fileError) {
+                        console.warn('转换base64为临时文件失败,尝试直接使用base64:', fileError)
+                        // 清除临时文件路径,确保使用base64
+                        tempFilePath = ''
+                    }
+                } else {
+                    console.log('电脑版微信小程序,直接使用base64数据')
                 }
             }
 

+ 13 - 0
src/utils/index.ts

@@ -6,6 +6,19 @@ import { menuButtonInfo, pxToRpx, rpxToPx, safeAreaInsets, systemInfo } from './
 
 export type PageInstance = Page.PageInstance<AnyObject, object> & { $page: Page.PageInstance<AnyObject, object> & { fullPath: string } }
 
+
+export const isPcWeixinMp = (): boolean => {
+    if (!isMpWeixin) return false
+
+    try {
+        // 电脑版微信小程序的userAgent包含Windows或Macintosh
+        const userAgent = navigator.userAgent || ''
+        return /Windows|Macintosh/i.test(userAgent)
+    } catch (e) {
+        return false
+    }
+}
+
 export function getLastPage() {
     // getCurrentPages() 至少有1个元素,所以不再额外判断
     // const lastPage = getCurrentPages().at(-1)