任务3-3操作步骤:会员码功能

任务三 工单3

本工单最终效果图:



1、用户可通过首页会员码进入各自的会员码页面,该页面有用户二维码,出示二维码,商店可扫二维码获得积分以兑换相应礼品。在“首页”界面,点击如图3.3.2处,进入“会员码”界面,按照UI设计师设计效果图进行布局。
3.3.2 会员码按钮.png

在/pages/mine文件夹下新建member-code.vue的“会员码”界面,并在pages.json中配置相应的样式,样式如下:

{
    "path" : "pages/mine/member-code",
    "style" : 
    {
        "navigationBarTitleText": "会员码",
        "navigationBarTextStyle": "black",
        "navigationBarBackgroundColor": "#ffffff"
    }
}

在index.vue中对class为qrcode_section设置点击交互@tap="memberCode",跳转至member-code.vue,代码如下:

    memberCode() {//点击会员码
        if(!this.isLogin) {
            uni.navigateTo({url: '/pages/login/login'})//判断用户是否登录
            return
        }
        uni.navigateTo({
            url: '/pages/mine/member-code'//跳转会员码界面
        })
    },
3.3.3 用户信息展示.png

通过mapState辅助函数获取用户member信息,用于展示“用户头像”“昵称”“云鲤券”、“积分”、“余额”等信息,如图3.3.3。

mapState是一个辅助函数,用于将Vuex store中的状态映射到组件的计算属性中。通过使用mapState,可以简化代码,避免手动编写大量的计算属性。
基本用法
mapState接受一个数组或对象作为参数:
数组形式:数组中的每个字符串都会被映射为组件的计算属性。
对象形式:对象的键名是组件的计算属性名,值是从store中获取的状态。

3.3.4 引入uqrcode类.png

引入二维码工具类uqrcode.js进入common文件夹下,如图3.3.4。通过canvas元素进行绘制,绑定uQRCode类的canvas-id="memberCode"。其中correctLevel为二维码的容错级别,用于控制二维码在损坏或部分遮挡时的恢复能力。容错级别越高,二维码的恢复能力越强,但同时也会增加二维码的尺寸。具体用法如下,可输出二维码如图3.3.5:


3.3.5 canvas画的二维码.png
makeMemberCode(i) {
        uQRCode.make({
            canvasId: 'memberCode',//绑定二维码id
            componentInstance: this,//组件实例
            text: `memberCode${i}`,//二维码扫描的信息,这里做测试循环输出
            size: uni.upx2px(350),//二维码大小
            margin: 20,
            backgroundColor: '#ffffff',//二维码背景色
            foregroundColor: '#000000', //二维码前景色
            fileType: 'jpg',//输出二维码格式
            correctLevel: uQRCode.defaults.correctLevel,//二维码的容错级别
            success: res => {
                // console.log(res)//成功回调函数
            }
        })
    }

为了确保二维码的安全性,团队成员采取一种有效的方法,即通过setInterval()函数来实现每30秒对二维码进行一次刷新操作。方法在一进页面就展示二维码,在uni-app生命周期的onShow()方法调用该方法,代码如下,最终效果图如图3.3.6:
3.3.6 会员码最终效果图.png
    onShow() {
            let i = 1
            this.makeMemberCode(i)
            setInterval(() => {
                i++
                this.makeMemberCode(i)
            }, 30000) //30秒换一次二维码
        },

相关详细代码如下:member-code.vue

<template>
    <!-- 会员码 -->
    <view class="container" style="padding: 20rpx 30rpx;">
        <view class="w-100 d-flex flex-column">
            <!-- 头像 -->
            <view class="d-flex just-content-center align-items-center">
                <view class="avatar-wrapper">
                    <image :src="member.avatar"></image>
                    <view class="tag">
                        <image src="/static/images/mine/level.png" mode="widthFix"></image>
                        <view>{{ member.memberLevel }}</view>
                    </view>
                </view>
            </view>
            <view class="user-box">
                <!-- 昵称 -->
                <view class="d-flex just-content-center text-color-assist font-size-base font-weight-bold mb-30">
                    {{ member.nickname }}
                </view>
                <view class="w-100 d-flex font-size-sm text-color-assist mb-30">
                    <view class="user-grid" @tap="coupons">
                        <view class="value">{{ member.couponNum }}</view>
                        <view>云鲤券</view>
                    </view>
                    <view class="user-grid" @tap="integrals">
                        <view class="value">{{ member.pointNum }}</view>
                        <view>积分</view>
                    </view>
                    <view class="user-grid" @tap="balance">
                        <view class="value">{{ member.balance }}</view>
                        <view>余额</view>
                    </view>
                    <view class="user-grid">
                        <view class="value">{{ member.giftBalance }}</view>
                        <view>礼品卡</view>
                    </view>
                </view>
                <!-- qrcode begin -->
                <view class="d-flex just-content-center align-items-center"><canvas canvas-id="memberCode"
                        style="width: 350rpx; height: 350rpx;"></canvas></view>
                <!-- qrcode end -->
                <view class="d-flex just-content-center align-items-center" style="margin-bottom: 50rpx;">
                    <view class="font-size-sm text-color-assist">支付前出示可累计积分,会员码每30秒更新</view>
                </view>
            </view>
        </view>
        <image src="https://s3.uuu.ovh/imgs/2024/12/14/dc4fa515052c9185.png" class="w-100" mode="widthFix"></image>
    </view>
</template>

<script>
    import {
        mapState
    } from 'vuex'

    import uQRCode from '@/common/uqrcode'
    export default {
        data() {
            return {};
        },
        onShow() {
            let i = 1
            this.makeMemberCode(i)
            setInterval(() => {
                i++
                this.makeMemberCode(i)
            }, 30000) //30秒换一次二维码
        },
        computed: {
            ...mapState(['member'])
        },
        methods: {
            makeMemberCode(i) {
                uQRCode.make({
                    canvasId: 'memberCode', //绑定二维码id
                    componentInstance: this, //组件实例
                    text: `memberCode${i}`, //二维码扫描的信息,这里做测试循环输出
                    size: uni.upx2px(350), //二维码大小
                    margin: 20,
                    backgroundColor: '#ffffff', //二维码背景色
                    foregroundColor: '#000000', //二维码前景色
                    fileType: 'jpg', //输出二维码格式
                    correctLevel: uQRCode.defaults.correctLevel, //二维码的容错级别
                    success: res => {
                        // console.log(res)//成功回调函数
                    }
                })
            }
        }
    }
</script>

<style lang="scss" scoped>
.avatar-wrapper {
        width: 150rpx;
        height: 150rpx;
        border-radius: 100%;
        background-color: #ffffff;
        box-shadow: 0 0 20rpx rgba($color: #000000, $alpha: 0.1);
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
        z-index: 10;

        image {
            width: 130rpx;
            height: 130rpx;
            border-radius: 100%;
        }

        .tag {
            background-color: #ffffff;
            position: absolute;
            right: -30rpx;
            bottom: -6rpx;
            display: flex;
            align-items: center;
            color: $color-warning;
            font-size: 22rpx;
            padding: 6rpx 16rpx;
            border-radius: 50rem !important;
            box-shadow: 2rpx 2rpx 20rpx rgba($color: #000000, $alpha: 0.1);

            image {
                width: 26rpx;
            }
        }
    }

    .user-box {
        width: 100%;
        position: relative;
        border-radius: 8rpx;
        background-color: #ffffff;
        margin-top: -75rpx;
        padding-top: 105rpx;
        padding-bottom: 75rpx;
        margin-bottom: 30rpx;
        box-shadow: 0 0 20rpx rgba($color: #000000, $alpha: 0.1);
    }

    .user-grid {
        flex: 1;
        display: flex;
        flex-direction: column;
        align-items: center;
        position: relative;

        &::after {
            content: ' ';
            position: absolute;
            right: 0;
            top: 0;
            height: 100%;
            border-right: 1rpx solid $text-color-assist;
            transform: scaleX(0.2) scaleY(0.5);
        }

        &:nth-last-child(1)::after {
            border-right: 0;
        }

        .value {
            font-size: $font-size-base;
            font-weight: bold;
            margin-bottom: 10rpx;
        }
    }
</style>

2、会员码界面完成后,在首页几处用户信息需完善。若用户未登录状态情况下,1处提示信息为“您好,游客”,2处提示信息“登录查看积分”,不提示积分;若用户登录状态下,1处提示信息为“您好,用户昵称”,2处积分为用户真实积分。效果对比图如3.3.7和3.3.8。


3.3.7 游客状态对比.png
3.3.8 积分状态对比.png

通过import {mapGetters,mapState} from 'vuex'引入对应isLogin、member对象,获得是否登录状态及用户昵称和用户积分。通过三目运算符完成代码,相关重要代码如下:

    <view class="intro">
        <view class="greet">您好,{{isLogin? member.nickname: '游客'}}</view>
        <view class="note">一杯奶茶,一口软欧包,在云鲤遇见两种美好</view>
    </view>
...
...
        <text class="title">我的积分</text>
        <text :class="{ 'value': isLogin, 'title': isLogin }">{{isLogin? member.pointNum: '登录查看积分'}}</text>
...
...
<script>
    import {mapGetters,mapState} from 'vuex'
    export default {
        computed:{
            ...mapGetters(['isLogin']),
            ...mapState(['member'])
        }
...
...

3、在完成本任务工单的研发工作后,团队成员应使用SourceTree工具执行版本提交,以创建此工单研发代码的历史版本记录。
详细代码如下index.vue

<template>
    <view class="container">
        <!-- 头部布局 -->
        <view class="banner">
            <image src="https://s3.uuu.ovh/imgs/2024/12/01/0793d43c7403e3de.jpg" class="bg"></image>
            <view class="intro">
                <view class="greet">您好,{{isLogin? member.nickname: '游客'}}</view>
                <view class="note">一杯奶茶,一口软欧包,在云鲤遇见两种美好</view>
            </view>
        </view>
        <view class="content">
            <!-- 中间 自取 和 外卖 -->
            <view class="entrance">
                <view class="item" @tap="takein">
                    <image src="/static/images/index/zq.png" class="icon"></image>
                    <view class="title">自取</view>
                </view>
                <view class="item" @tap="takeout">
                    <image src="/static/images/index/wm.png" class="icon"></image>
                    <view class="title">外卖</view>
                </view>
            </view>
            <!-- 我的积分板块 -->
            <view class="info">
                <view class="integral_section"  @tap="integrals">
                    <view class="top">
                        <text class="title">我的积分</text>
                        <text :class="{ 'value': isLogin, 'title': isLogin }">{{isLogin? member.pointNum: '登录查看积分'}}</text>
                    </view>
                    <view class="bottom">
                        进入积分商城兑换云鲤券及周边好礼
                        <view class="iconfont iconarrow-right"></view>
                    </view>
                </view>
                <view class="qrcode_section" @tap="memberCode">
                    <image src="/static/images/index/qrcode.png"></image>
                    <text>会员码</text>
                </view>
            </view>
            <view class="navigators">
                <!-- left 云鲤的茶商城 -->
                <view class="left">
                    <view class="grid flex-column just-content-center">
                        <view class="d-flex align-items-center">
                            <image src="/static/images/index/csc.png" class="mark-img"></image>
                            <view class="font-size-sm text-color-base">云鲤的茶商城</view>
                        </view>
                        <view class="text-color-assist" style="margin-left: 40rpx; font-size: 20rpx;">优质茶礼盒,网红零食</view>
                    </view>
                    <view class="grid justify-content-end align-items-end">
                        <image src="/static/images/index/yzclh.png" class="yzclh-img" mode="heightFix"></image>
                    </view>
                </view>
                <!-- ringt 买茶送包  会员劵包 -->
                <view class="right">
                    <view class="tea-activity">
                        <image src="/static/images/index/mcsb.png" class="mark-img"></image>
                        <view>买茶送包</view>
                        <view class="right-img">
                            <image src="/static/images/index/mcsb_bg.png" mode="widthFix"></image>
                        </view>
                    </view>
                    <view class="member-gifts">
                        <image src="/static/images/index/hyjb.png" class="mark-img"></image>
                        <view>会员劵包</view>
                        <view class="right-img">
                            <image src="/static/images/index/hyjb_bg.png" mode="widthFix"></image>
                        </view>
                    </view>
                </view>
            </view>
            <!-- 会员新鲜事 -->
            <view class="member-news">
                <view class="header">
                    <view class="title">会员新鲜事</view>
                    <view class="iconfont iconRightbutton"></view>
                </view>
                <view class="list">
                    <view class="item">
                        <image src="https://s3.uuu.ovh/imgs/2024/12/01/91c4a34528f7b66e.jpg"></image>
                        <view class="title">"梅"你不行 | 霸气杨梅清爽回归</view>
                    </view>
                </view>
            </view>
        </view>
    </view>
</template>
<script>
    import {mapGetters,mapState} from 'vuex'
    export default {
        data() {
            return {}
        },
        computed:{
            ...mapGetters(['isLogin']),
            ...mapState(['member'])
        },
        methods: {
            takein() { //点击自取
                //通过SET_ORDER_TYPE方法设置orderType 为自取
                this.$store.commit('SET_ORDER_TYPE', 'takein')
                uni.switchTab({
                    url: '/pages/menu/menu'
                })
            },
            takeout() {//点击外卖
                if(!this.isLogin) {//判断是否登录
                    uni.navigateTo({url: '/pages/login/login'})//跳转登录页面
                    return
                }
                uni.navigateTo({
                    url: "/pages/address/address"   //进入我的地址页面
                })
            },
            integrals() {//点击我的积分
                if(!this.isLogin) {//做登录判断
                    uni.navigateTo({url: '/pages/login/login'})
                    return
                }
                uni.navigateTo({
                    url: '/pages/integrals/integrals'
                })
            },
            memberCode() {//点击会员码
                if(!this.isLogin) {
                    uni.navigateTo({url: '/pages/login/login'})
                    return
                }
                uni.navigateTo({
                    url: '/pages/mine/member-code'
                })
            },
        }
    }
</script>
<style lang="scss" scoped>
    /* 这块最后来介绍 */
    /* #ifdef H5 */
    page {
        height: auto;
        min-height: 100%;
    }

    /* #endif */
    .banner {
        position: relative;
        width: 100%;
        height: 600rpx;

        .bg {
            width: 100%;
            height: 600rpx;
        }

        .intro {
            position: absolute;
            top: calc(50rpx + var(--status-bar-height));
            left: 40rpx;
            color: #FFFFFF;
            display: flex;
            flex-direction: column;

            .greet {
                font-size: $font-size-lg;
                margin-bottom: 10rpx;
            }

            .note {
                font-size: $font-size-sm;
            }
        }
    }

    .content {
        padding: 0 30rpx;
    }

    .entrance {
        position: relative;
        margin-top: -80rpx;
        margin-bottom: 30rpx;
        border-radius: 10rpx;
        background-color: #ffffff;
        box-shadow: $box-shadow;
        padding: 30rpx 0;
        display: flex;
        align-items: center;
        justify-content: center;

        .item {
            flex: 1;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            position: relative;

            &:nth-child(1):after {
                content: '';
                position: absolute;
                width: 1rpx;
                background-color: #ddd;
                right: 0;
                height: 100%;
                transform: scaleX(0.5) scaleY(0.8);
            }

            .icon {
                width: 84rpx;
                height: 84rpx;
                margin: 20rpx;
            }

            .title {
                font-size: 30rpx;
                color: $text-color-base;
                font-weight: 600;
            }
        }
    }

    .info {
        position: relative;
        margin-bottom: 30rpx;
        border-radius: 10rpx;
        background-color: #ffffff;
        box-shadow: $box-shadow;
        padding: 30rpx;
        display: flex;
        align-items: center;
        justify-content: center;

        .integral_section {
            flex: 1;
            display: flex;
            flex-direction: column;
            justify-content: center;

            .top {
                display: flex;
                align-items: center;

                .title {
                    color: $text-color-base;
                    font-size: $font-size-base;
                    margin-right: 10rpx;
                }

                .value {
                    font-size: 44rpx;
                    font-weight: bold;
                }
            }

            .bottom {
                font-size: $font-size-sm;
                color: $text-color-assist;
                display: flex;
                align-items: center;
            }
        }

        .qrcode_section {
            color: $color-primary;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            font-size: $font-size-sm;

            image {
                width: 40rpx;
                height: 40rpx;
                margin-bottom: 10rpx;
            }
        }
    }

    .navigators {
        width: 100%;
        margin-bottom: 20rpx;
        border-radius: 10rpx;
        background-color: #ffffff;
        box-shadow: $box-shadow;
        padding: 20rpx;
        display: flex;
        align-items: stretch;

        .left {
            width: 340rpx;
            margin-right: 20rpx;
            display: flex;
            padding: 0 20rpx;
            flex-direction: column;
            font-size: $font-size-sm;
            color: $text-color-base;
            background-color: #F2F2E6;

            .grid {
                height: 50%;
                display: flex;
            }
        }

        .right {
            width: 290rpx;
            display: flex;
            flex-direction: column;

            .tea-activity,
            .member-gifts {
                width: 100%;
                display: flex;
                padding: 20rpx;
                font-size: $font-size-sm;
                color: $text-color-base;
                align-items: center;
                position: relative;
            }

            .tea-activity {
                background-color: #FDF3F2;
                margin-bottom: 20rpx;
            }

            .member-gifts {
                background-color: #FCF6D4;
            }

            .right-img {
                flex: 1;
                position: relative;
                margin-left: 20rpx;
                margin-right: -20rpx;
                margin-bottom: -20rpx;
                display: flex;
                align-items: flex-end;

                image {
                    width: 100%;
                }
            }
        }

        .mark-img {
            width: 30rpx;
            height: 30rpx;
            margin-right: 10rpx;
        }

        .yzclh-img {
            height: 122.96rpx;
            width: 214.86rpx;
        }
    }

    .member-news {
        width: 100%;
        margin-bottom: 30rpx;

        .header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 20rpx 0;

            .title {
                font-size: $font-size-lg;
                font-weight: bold;
            }

            .iconfont {
                font-size: 52rpx;
                color: $text-color-assist;
            }
        }

        .list {
            width: 100%;
            display: flex;
            flex-direction: column;

            .item {
                width: 100%;
                height: 240rpx;
                position: relative;

                image {
                    width: 100%;
                    height: 100%;
                    border-radius: 8rpx;
                }

                .title {
                    position: relative;
                    font-size: 32rpx;
                    font-weight: 500;
                    width: 100%;
                    top: -70rpx;
                    left: 16rpx;
                    color: #ffffff;
                }
            }
        }
    }
</style>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容