任务6-4操作步骤:礼品卡功能

任务六 工单4

效果图


6.4.1 礼品卡列表效果图.png

1、“礼品卡列表”界面为顾客提供了多种活动相关的优惠卡片。这些卡片基于当前的活动及节气而设计,并提供相应的折扣优惠。用户可在该界面浏览并选择需要使用的卡片进行购买。通过如图6.4.2所示的功能入口,用户能够轻松进入该界面,开始他们的卡片消费体验。


6.4.2 礼品卡界面交互处.png

       在 pages 文件夹中创建并部署 giftcard.vue 按钮位于功能列表的左侧侧边栏中。遵循项目统一的样式规范,在 pages.json 文件中完成相关配置设置。设置完成后,跳转至 主界面中的 giftCards 按钮下方,用户即可通过 @tap="giftCards" 交互进入该页面。
       mine.vue 值得注意的是,此页面已启用 disableScroll 属性,具体效果为:disableScroll 设置为 true 时,页面将彻底禁止任何上下方向的滚动操作,这一设置仅在页面配置中有效。

    {
        "path" : "pages/giftcard/giftcard",
        "style" : {
            "navigationBarTitleText": "礼品卡列表",
            "navigationBarTextStyle": "black",
            "navigationBarBackgroundColor": "#ffffff",
            "disableScroll": true
        }
    }

2、“礼品卡列表”界面内部较为复杂,可分为“购买礼品卡”功能、“我的礼品卡”功能两个板块进行研发,每个块功能通过swiper、swiper-item进行展示。购买礼品卡为第一个swiper-item,其中包含scroll-view、image、v-for循环列表。
       “礼品卡列表”界面的设计较为复杂,主要包含两个核心功能板块:一是“购买礼品卡”功能,二是“我的礼品卡”管理功能。其中,“购买礼品卡”模块采用swiper组件作为交互式展示框架,具体包括滚动视图(scroll-view)、图片展示(image)和基于v-for循环的数据展示(v-for)。第一个swiper-item中包含滚动视图、图片展示以及基于v-for循环的数据展示。效果如图6.4.3。


6.4.3 礼品卡功能.png

       通过@tap="switchTab(0)"的方式,用户点击卡片即可切换子页面,默认情况下参数为0对应“购买礼品卡”子页面,参数为1则切换至“我的礼品卡”子页面。充分利用组件库和动态class类管理功能,确保界面交互流畅且操作便捷,并通过合理的布局优化和性能调优提升了整体用户体验。

    <view class="tabbar">
        <view class="item" :class="{'active': !currentTab}" @tap="switchTab(0)">购买礼品卡</view>
        <view class="item" :class="{'active': currentTab}" @tap="switchTab(1)">我的礼品卡</view>
    </view>

       “礼品卡列表”选项卡的设计通过调用onShow生命周期阶段(onShow),在该阶段的初始阶段(construct)会启动数据获取流程。具体而言,在选项卡的显示生命周期阶段(onShow)开始时,将执行await this.$api('giftCards')的方法,以获取卡片数据集合(giftCards.category_list)。
       通过v-for循环遍历该数据集,每个卡片信息将被分解展示:卡片名称作为主标题,卡片图片和相关礼品卡活动信息作为副标题。具体来说,图片组件将根据theme.imageUrls展示卡片图片,同时配合theme.activityName字段显示相关信息。这一设计实现了卡片数据的高效获取与动态呈现,最终呈现出的效果如图6.4.4所示。


6.4.4 礼品卡列表效果.png
    <scroll-view scroll-y="true" class="h-100" style="padding-bottom: 80rpx;">
        <image :src="giftCards.img" class="w-100" mode="widthFix"/>
        <view class="pl-20 pr-20 mb-30">
            <view class="category-list mt-40" v-for="(category, index) in giftCards.category_list" :key="index">
                <view class="font-size-lg font-weight-bold">{{ category.name }}</view>
                <view class="themes-list">
                    <view class="theme" v-for="(theme, key) in category.themesList" :key="key">
                        <image :src="theme.imageUrls" class="w-100" mode="widthFix"/>
                        <view class="activity-name">{{ theme.activityName }}</view>
                    </view>
                </view>
            </view>
        </view>
    </scroll-view>

       按照设计图构建“购买礼品卡”子页面的布局方案,并结合UniApp平台的技术特性,实现卡片数据的动态获取与展示功能。
3、在“我的礼品卡”子页面中,嵌套了一个swiper组件,用于实现可使用和不可使用的卡片信息切换。该嵌套组件支持左右滑动操作,并通过@change事件监听功能,根据滑动后的结果动态切换至对应的子页面。
       当用户对嵌套swiper进行滑动操作时,会触发一个名为switchGiftCardType的事件处理函数,该函数将决定显示的是可使用的卡片还是不可使用的卡片信息(效果如图6.4.5所示)。


6.4.5 内嵌swiper.png
    switchGiftCardType(index) {
        if(this.giftCardType == index) return
        this.giftCardType = index
    }

       此外,考虑到可能需要展示多个子页面的情况(如n个子页面),在每个子页面之间嵌套一个swiper-item,从而实现多维度的信息呈现和切换效果(效果如图6.4.6所示)。


6.4.6 swiper-item内布局效果.png
    <swiper-item>
        <template v-if="!myCards.length">
            <view class="d-flex flex-column align-items-center">
                <image src="https://s3.uuu.ovh/imgs/2025/02/08/36ca6ba99601c912.png" style="width: 300rpx;margin-top: 100rpx;" mode="widthFix"/>
                <view class="font-size-sm text-color-assist">您暂时还没有礼品卡哦~</view>
            </view>
        </template>
    </swiper-item>

       按照设计图完成“我的礼品卡”子页面的布局及功能。
4、至此,本工单的相关功能开发工作结束。团队成员应运用SourceTree工具执行版本控制的提交操作,以便为本次工单的开发代码建立历史版本的记录。

giftcard.vue完整代码

<template>
    <!-- 礼品卡列表 -->
    <view class="container">
        <view class="tabbar">
            <view class="item" :class="{'active': !currentTab}" @tap="switchTab(0)">购买礼品卡</view>
            <view class="item" :class="{'active': currentTab}" @tap="switchTab(1)">我的礼品卡</view>
        </view>
        <swiper :duration="400" :disable-touch="true" class="swiper" :current="currentTab">
            <!-- 购买礼品卡 begin -->
            <swiper-item class="swiper-item-1" @touchmove.stop="handleSwiperItemChange">
                <scroll-view scroll-y="true" class="h-100" style="padding-bottom: 80rpx;">
                    <image :src="giftCards.img" class="w-100" mode="widthFix"/>
                    <view class="pl-20 pr-20 mb-30">
                        <view class="category-list mt-40" v-for="(category, index) in giftCards.category_list" :key="index">
                            <view class="font-size-lg font-weight-bold">{{ category.name }}</view>
                            <view class="themes-list">
                                <view class="theme" v-for="(theme, key) in category.themesList" :key="key">
                                    <image :src="theme.imageUrls" class="w-100" mode="widthFix"/>
                                    <view class="activity-name">{{ theme.activityName }}</view>
                                </view>
                            </view>
                        </view>
                    </view>
                </scroll-view>
                <view class="footer">
                    <view>购买须知</view>
                    <view class="divider"></view>
                    <view>礼品卡章程</view>
                </view>
            </swiper-item>
            <!-- 购买礼品卡 end -->
            <!-- 我的礼品卡 begin -->
            <swiper-item class="swiper-item-2" @touchmove.stop="handleSwiperItemChange">
                <view class="header">
                    <view class="gift-card-types">
                        <view class="item" :class="{'active': !giftCardType}" @tap="switchGiftCardType(0)">可使用(0)</view>
                        <view class="item" :class="{'active': giftCardType}" @tap="switchGiftCardType(1)">不可使用(0)</view>
                    </view>
                    <view class="d-flex align-items-center text-color-primary">兑换礼品卡</view>
                </view>
                <swiper :duration="400" class="flex-fill" :current="giftCardType" @change="e => switchGiftCardType(e.detail.current)">
                    <swiper-item>
                        <template v-if="!myCards.length">
                            <view class="d-flex flex-column align-items-center">
                                <image src="https://s3.uuu.ovh/imgs/2025/02/08/36ca6ba99601c912.png" style="width: 300rpx;margin-top: 100rpx;" mode="widthFix"/>
                                <view class="font-size-sm text-color-assist">您暂时还没有礼品卡哦~</view>
                            </view>
                        </template>
                    </swiper-item>
                    <swiper-item>
                        <template v-if="!myCards.length">
                            <view class="d-flex flex-column align-items-center">
                                <image src="https://s3.uuu.ovh/imgs/2025/02/08/36ca6ba99601c912.png" style="width: 300rpx;margin-top: 100rpx;" mode="widthFix"/>
                                <view class="font-size-sm text-color-assist">您暂时还没有礼品卡哦~</view>
                            </view>
                        </template>
                    </swiper-item>
                </swiper>
                <view class="footer">
                    <button type="primary" plain>获取记录</button>
                    <button type="primary">赠送记录</button>
                </view>
            </swiper-item>
            <!-- 我的礼品卡 end -->
        </swiper>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                giftCards: {},
                currentTab: 0,
                giftCardType: 0,
                myCards: []
            }
        },
        async onShow() {
            this.giftCards = await this.$api('giftCards')
        },
        methods: {
            switchTab(index) {
                if(this.currentTab == index) return
                this.currentTab = index
            },
            switchGiftCardType(index) {
                if(this.giftCardType == index) return
                this.giftCardType = index
            },
            handleSwiperItemChange() {
                return true
            }
        }
    }
</script>

<style lang="scss" scoped>
    .container {
        display: flex;
        flex-direction: column;
    }
    
    .tabbar {
        width: 100%;
        height: 100rpx;
        border-top: 1rpx solid $border-color;
        background-color: $text-color-white;
        display: flex;
        align-items: stretch;
        font-size: $font-size-lg;
        
        .item {
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            position: relative;
            
            &.active {
                color: $color-primary;
                &:after {
                    content: '';
                    position: absolute;
                    width: 40rpx;
                    height: 10rpx;
                    background-color: $color-primary;
                    border-radius: 50rem !important;
                    bottom: 0;
                    left: calc((100% - 40rpx) / 2);
                }
            }
        }
    }
    
    .swiper {
        flex: 1;
    }

    .swiper-item-1 {
        height: 100%;
        
        .themes-list {
            display: flex;
            flex-wrap: wrap;
            justify-content: space-between;
            
            .theme {
                width: 340rpx;
                margin-top: 30rpx;
                border-radius: 24rpx;
                background-color: $text-color-white;
                display: flex;
                flex-direction: column;
                box-shadow: $box-shadow;
                
                image {
                    border-radius: 24rpx 24rpx 0 0;
                    max-height: 300rpx;
                }
            }
            
            .activity-name {
                font-size: $font-size-base;
                padding: 20rpx 0;
                text-align: center;
            }
        }
        
        .footer {
            z-index: 10;
            background-color: $bg-color;
            position: fixed;
            bottom: 0;
            width: 100%;
            height: 80rpx;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: $font-size-sm;
            color: $color-primary;
            
            .divider {
                height: 0.6rem;
                width: 4rpx;
                background-color: $color-primary;
                margin: 0 20rpx;
            }
        }
    }
    
    .swiper-item-2 {
        height: 100%;
        display: flex;
        flex-direction: column;
        
        .header {
            height: 110rpx;
            display: flex;
            justify-content: space-between;
            align-items: stretch;
            padding: 30rpx;
            font-size: $font-size-sm;
            
            .gift-card-types {
                display: flex;
                align-items: stretch;
                
                .item {
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    padding: 0 20rpx;
                    
                    &.active {
                        background-color: #FFFFFF;
                        box-shadow: $box-shadow;
                        border-radius: 50rem !important;
                        color: $color-primary;
                    }
                }
            }
        }
        
        .footer {
            z-index: 10;
            background-color: $text-color-white;
            position: fixed;
            bottom: 0;
            width: 100%;
            height: 100rpx;
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10rpx 40rpx;
            box-shadow: 0 -10rpx 10rpx rgba($color: #eee, $alpha: 0.1);
            
            button {
                width: 310rpx;
                height: 100%;
                line-height: 80rpx;
                border-radius: 50rem !important;
                font-size: $font-size-lg;
            }
        }
    }
</style>

mine.vue 部分跳转代码

          ...
          ...
          ...
                <view class="w-100 d-flex align-items-center just-content-center">
                    <!-- 通过三目运算符判断 -->
                    <view class="user-grid" @tap="coupons">
                        <view class="value font-size-extra-lg font-weight-bold text-color-base">
                            {{ isLogin ? member.couponNum : '***' }}
                        </view>
                        <view class="font-size-sm text-color-assist">云鲤券</view>
                    </view>
                    <view class="user-grid" @tap="integrals">
                        <view class="value font-size-extra-lg font-weight-bold text-color-base">
                            {{ isLogin ? member.pointNum : '***' }}
                        </view>
                        <view class="font-size-sm text-color-assist">积分商城</view>
                    </view>
          ...
          ...
          ...
<script>
    import {
        mapState,
        mapGetters
    } from 'vuex'
    export default {
        data() {
            return {
                // isLogin: false,//false为未登录,true为登录
                newIcon: 'https://s3.uuu.ovh/imgs/2024/12/05/6b3856fe6d711a0a.png'
            }
        },
        computed: {
            ...mapState(['member']),
            ...mapGetters(['isLogin']),
            growthValue() { //计算成长属性
                if (!this.isLogin) return 0
                const {
                    currentValue,
                    needValue
                } = this.member
                return currentValue / (currentValue + needValue) * 100
            }
        },
        methods: {
            login() { //登录
                uni.navigateTo({
                    url: '/pages/login/login'
                })
            },
            ...
            ...
            ...
            services() {//更多服务
                uni.navigateTo({
                    url: '/pages/services/services'
                })
            },
            coupons() {//我的卡券
                if(!this.isLogin) {
                    this.login()
                    return
                }
                uni.navigateTo({
                    url: '/pages/coupons/coupons'
                })
            },
            balance() { //进入会员充值界面
                if (!this.isLogin) {
                    this.login()
                    return
                }
                uni.navigateTo({
                    url: '/pages/balance/balance'
                })
            },
            giftCards() { //礼品卡
                if (!this.isLogin) {
                    this.login()
                    return
                }
                uni.navigateTo({
                    url: '/pages/giftcard/giftcard'
                })
            }
        }
    }
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 229,237评论 6 537
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,957评论 3 423
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 177,248评论 0 382
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,356评论 1 316
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,081评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,485评论 1 324
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,534评论 3 444
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,720评论 0 289
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,263评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,025评论 3 356
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,204评论 1 371
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,787评论 5 362
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,461评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,874评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,105评论 1 289
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,945评论 3 395
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,205评论 2 375