任务6-2操作步骤:我的卡券功能

任务六 工单2

效果图:


6.2.1 我的卡券效果图.png

6.2.2 卡券模态框效果图.png

1、在“我的管理”界面中,通过图6.2.3所示的@tap="coupons"交互,可以进入“我的卡券”界面。因此,在pages文件夹下创建一个新的页面,命名为coupons.vue,并根据既定要求对其进行样式设计。


6.2.3 我的卡券交互处.png
    {
        "path" : "pages/coupons/coupons",
        "style" : {
            "navigationBarTitleText": "我的卡券",
            "navigationBarTextStyle": "black",
            "navigationBarBackgroundColor": "#ffffff"
        }
    }

2、“我的卡券”界面的研发布局参照图6.2.4进行拆分。第一区域的布局通过input和button元素实现,具体代码如下:

    <view class="exchange-box">
        <view class="input-box">
            <input type="text" placeholder="请输入兑换码" placeholder-class="text-color-assist font-size-base" />
            <button type="primary">兑换</button>
        </view>
        <view class="font-size-sm text-color-primary line-height-2">查看兑换规则</view>
    </view>
6.2.4 我的卡券拆分布局.png

对于第二区域,必须引入tabs参数,该参数用于定义卡券的种类。参数的具体样式请参照以下示例:

    tabs: [
        {title: '全部', value: 'all'},
        {title: '茶饮券', value: '1'},
        {title: '酒屋券', value: '2'}
    ]

通过设定activeTabIndex参数,用以追踪当前选中的卡券类别。该参数旨在从下方列表中筛选出相应的卡券类别。若当前类别为全部卡券,则activeTabIndex的值设为0;若为茶饮券,则设为1;若为酒屋券,则设为2。通过调用handleTab(index)方法,可以实现activeTabIndex值的变更。页面中的相关代码如下:

    <view class="tabbar">
        <view class="tab" :class="{active: activeTabIndex == index}" 
            v-for="(item, index) in tabs" :key="index" @tap="handleTab(index)">
            <view class="title">{{ item.title }}</view>
        </view>
    </view>

通过调用await this.$api('customerCoupons')方法,可以获取到所有的coupons卡券数据。利用onShow()方法以及watch对activeTabIndex属性变化的监听,结合getCoupons()方法,实现列表的切换功能。
具体代码实现如下:

...
...
    onShow() {
        this.activeTabIndex = 0
    },
    watch: {
        activeTabIndex: async function(val) {
            const type = this.tabs[val].value
            await this.getCoupons(type)
        }
    },
    methods: {
        handleTab(index) {
            this.activeTabIndex = index
        },
        async getCoupons(type) {
            const coupons = await this.$api('customerCoupons')
            if(type == 'all') {
                this.coupons = coupons
            } else {
                this.coupons = coupons.filter(item => item.couponType == type)
            }
        },
...
...
...

onShow()方法用于监听页面的显示事件,每当页面呈现于屏幕时即被触发。这包括从子页面通过返回操作而显示当前页面的情况(例如,从二级页面返回至本页面时,onLoad不会被重新执行,但onShow会被再次调用)。因此,每次访问本页面时,应将activeTabIndex的值设定为0。

watch属性的作用在于侦测数据的变动,并在数据发生改变时执行既定的操作。上述代码展示了如何对activeTabIndex进行监控,一旦检测到变化,便将tabs中的value值赋予type变量,并调用getCoupons函数将type作为参数传递,以实现数据的筛选。当type的值为'all'时,将筛选出所有卡券数据;若type为其他值,则通过filter函数进行数据筛选并赋值。通过这三个函数的协同工作,实现了点击交互,并能够切换页面上的数据展示,具体效果可参见图6.2.5。


6.2.5 三类卡券效果图.png

在获取到精确分类的数据之后,必须通过使用v-for循环来完成列表的展示,将优惠券逐一呈现。

4、引入了modal和jyfParser两个控件,以开展第三区域的研发工作。通过与每个列表中的item进行交互,触发弹出的modal模态框以展示当前item的卡券详情(具体代码如下所示)。

    <modal custom :show="detailModalVisible" @cancel="closeDetailModal" width="90%">
        <view class="modal-content">
            <view class="d-flex font-size-extra-lg text-color-base just-content-center mb-20">{{ coupon.title }}</view>
            <view class="d-flex font-size-sm text-color-base mb-20">
                有效期:{{ coupon.beginAt }}至{{ coupon.endAt }}
            </view>
            <pre class="pre-line font-size-sm text-color-assist mb-30">
                <jyf-parser ref="couponExplain"></jyf-parser>
            </pre>
            <view class="d-flex align-items-center just-content-center">
                <button type="primary" @tap="useCoupon" class="use-coupon-btn">立即使用</button>
            </view>
        </view>
    </modal>

定义一个布尔型参数detailModalVisible,通过调用openDetailModal()和closeDetailModal()方法实现模态框的显示与隐藏,以完成相应的交互操作。
若需使用卡券,请点击“立即使用”按钮以完成卡券使用流程,具体操作请参见图6.2.6,相关代码如下:


6.2.6 卡券使用交互.png

5、至此,本工单的相关功能开发工作结束。团队成员应运用SourceTree工具执行版本控制的提交操作,以便为本次工单的开发代码建立历史版本的记录。

coupons.vue完整代码

<template>
    <!-- 我的卡券 -->
    <view class="container position-relative w-100 h-100 overflow-hidden">
        <view class="exchange-box">
            <view class="input-box">
                <input type="text" placeholder="请输入兑换码" placeholder-class="text-color-assist font-size-base" />
                <button type="primary">兑换</button>
            </view>
            <view class="font-size-sm text-color-primary line-height-2">查看兑换规则</view>
        </view>
        <view class="tabbar">
            <view class="tab" :class="{active: activeTabIndex == index}" 
                v-for="(item, index) in tabs" :key="index" @tap="handleTab(index)">
                <view class="title">{{ item.title }}</view>
            </view>
        </view>
        <view class="flex-fill">
            <scroll-view scroll-y class="coupon-list">
                <view class="wrapper">
                    <view class="coupon" v-for="(item, index) in coupons" :key="index" @tap="openDetailModal(item)">
                        <view class="d-flex align-items-center detail">
                            <image :src="item.imageUrl" class="coupon-img"></image>
                            <view class="flex-fill d-flex flex-column just-content-center overflow-hidden">
                                <view class="font-size-lg text-color-base text-truncate mb-10">{{ item.title }}</view>
                                <view class="font-size-sm text-color-base">有效期至{{ item.endAt }}</view>
                            </view>
                        </view>
                        <view class="d-flex align-items-center justify-content-end" style="height: 80rpx;">
                            <view class="font-size-sm text-color-primary">查看详情</view>
                        </view>
                    </view>
                </view>
            </scroll-view>
        </view>
        <view class="bottom-box d-flex align-items-center just-content-center font-size-sm text-color-primary">
            <view class="item">历史卡券</view>
            <view class="item" @tap="showTip1">赠送记录</view>
            <view class="item" @tap="showTip2">第三方权益</view>
        </view>
        <modal custom :show="detailModalVisible" @cancel="closeDetailModal" width="90%">
            <view class="modal-content">
                <view class="d-flex font-size-extra-lg text-color-base just-content-center mb-20">{{ coupon.title }}</view>
                <view class="d-flex font-size-sm text-color-base mb-20">
                    有效期:{{ coupon.beginAt }}至{{ coupon.endAt }}
                </view>
                <pre class="pre-line font-size-sm text-color-assist mb-30">
                    <jyf-parser ref="couponExplain"></jyf-parser>
                </pre>
                <view class="d-flex align-items-center just-content-center">
                    <button type="primary" @tap="useCoupon" class="use-coupon-btn">立即使用</button>
                </view>
            </view>
        </modal>
    </view>
</template>

<script>
import modal from '@/components/modal/modal'
import jyfParser from "@/components/jyf-parser/jyf-parser"

export default {
    components: {
        modal,
        jyfParser
    },
    data() {
        return {
        tabs: [
            {title: '全部', value: 'all'},
            {title: '茶饮券', value: '1'},
            {title: '酒屋券', value: '2'}
        ],
            activeTabIndex: '',//控制tabs的index
            coupons: [],//卡券数据
            detailModalVisible: false,//模态框显示参数
            coupon: {}//单个卡券数据
        }
    },
    onShow() {
        this.activeTabIndex = 0
    },
    watch: {
        activeTabIndex: async function(val) {
            const type = this.tabs[val].value //定义类型传入方法
            await this.getCoupons(type)
        }
    },
    methods: {
        handleTab(index) {  //点击类型
            this.activeTabIndex = index
        },
        async getCoupons(type) {  //获取卡券类型数据
            const coupons = await this.$api('customerCoupons')
            if(type == 'all') {
                this.coupons = coupons
            } else {
                this.coupons = coupons.filter(item => item.couponType == type)  //通过筛选类型数据
            }
        },
        openDetailModal(coupon) { //打开模态框
            this.coupon = coupon
            this.$refs['couponExplain'].setContent(this.coupon.couponExplain || '')
            this.detailModalVisible = true
        },
        closeDetailModal() {  //关闭模态框
            this.detailModalVisible = false
            this.coupon = {}
        },
        useCoupon() {  //使用卡券
            uni.switchTab({
                url: '/pages/menu/menu'
            })
        },
        showTip1() {
            uni.showToast({
                title: '您暂时还没有赠送中卡券哦~',
                icon: 'none'
            })
        },
        showTip2() {
            uni.showToast({
                title: '您暂时还没有券码哦~',
                icon: 'none'
            })
        }
    }
}
</script>

<style lang="scss" scoped>
/* #ifdef H5 */
page {
    height: 100%;
}
/* #endif */

.container {
    display: flex;
    flex-direction: column;
}

.exchange-box {
    flex-shrink: 0;
    height: 200rpx;
    background-color: #ffffff;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;

    .input-box {
        display: flex;
        align-items: stretch;
        width: 70%;
        flex-shrink: 0;
        input {
            flex: 1;
            height: 80rpx;
            border: 1rpx solid #eee;
            border-right: 0;
            border-radius: 8rpx 0 0 8rpx;
            padding: 20rpx;
            font-size: $font-size-base;
            color: $text-color-base;
        }
        button {
            border-radius: 0 8rpx 8rpx 0;
            display: flex;
            align-items: center;
        }
    }
}

.tabbar {
    flex-shrink: 0;
    width: 100%;
    height: 120rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    
    .tab {
        flex: 1;
        height: 100%;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        font-size: $font-size-base;
        color: $text-color-base;
        position: relative;
        
        .title {
            padding: 15rpx 0;
        }
        
        &.active {
            color: $color-primary;
            
            .title {
                border-bottom: 5rpx solid $color-primary;
            }
        }
    }
}

.bottom-box {
    height: 80rpx;
    flex-shrink: 0;
    .item {
        padding: 0 20rpx;
        position: relative;
        
        &::after {
            content: " ";
            border-right: 1rpx solid $text-color-assist;
            height: 100%;
            position: absolute;
            right: 0;
            top: 0;
            transform: scaleX(0.5) scaleY(0.8);
        }
        
        &:nth-last-child(1)::after {
            border-right: 0;
        }
    }
}

.coupon-list {
    height: calc(100vh - 80rpx - 120rpx - 200rpx);
    /* #ifdef H5 */
    height: calc(100vh - 80rpx - 120rpx - 200rpx - 44px);
    /* #endif */
}

.wrapper {
    padding: 0 40rpx;
    display: flex;
    flex-direction: column;
    
    .coupon {
        display: flex;
        flex-direction: column;
        background-color: #FFFFFF;
        margin-bottom: 30rpx;
        padding: 0 30rpx;
        border-radius: 6rpx;
        box-shadow: 0 10rpx 10rpx -10rpx rgba(15, 15, 15, 0.1);
        position: relative;
        
        &::before {
            content: "";
            position: absolute;
            background-color: $bg-color;
            width: 30rpx;
            height: 30rpx;
            bottom: 65rpx;
            left: -15rpx;
            border-radius: 100%;
        }
        
        &::after {
            content: "";
            position: absolute;
            background-color: $bg-color;
            width: 30rpx;
            height: 30rpx;
            bottom: 65rpx;
            right: -15rpx;
            border-radius: 100%;
        }
        
        .detail {
            padding: 20rpx 0;
            position: relative;

            &::after {
                content: '';
                position: absolute;
                left: 0;
                right: 0;
                bottom: 0;
                border-bottom: 1rpx dashed #c6c6c6;
                transform: scaleY(0.5);
            }
            
            .coupon-img {
                width: 150rpx;
                height: 150rpx;
                margin-right: 40rpx;
            }
        }
    }
}

.use-coupon-btn {
    width: 95%;
    border-radius: 50rem !important;
}
</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'
                })
            },
            ...
            ...
            ...
            userinfo() {//用户信息
                if(!this.isLogin) {
                    this.login()
                    return
                }
                uni.navigateTo({
                    url: '/pages/mine/userinfo'
                })
            },
            services() {//更多服务
                uni.navigateTo({
                    url: '/pages/services/services'
                })
            },
            coupons() {//我的卡券
                if(!this.isLogin) {
                    this.login()
                    return
                }
                uni.navigateTo({
                    url: '/pages/coupons/coupons'
                })
            },
        }
    }
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 229,362评论 6 537
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,013评论 3 423
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 177,346评论 0 382
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,421评论 1 316
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,146评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,534评论 1 325
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,585评论 3 444
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,767评论 0 289
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,318评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,074评论 3 356
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,258评论 1 371
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,828评论 5 362
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,486评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,916评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,156评论 1 290
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,993评论 3 395
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,234评论 2 375

推荐阅读更多精彩内容