任务4-3操作步骤:支付结算界面

任务四 工单3


4.3.2 备注界面效果图.png

1、团队小组成员对购物车管理功能完成后,顾客需支付其商品金额。在page文件夹下新建pay.vue“支付信息”界面,并在pages.json中设置其相应的样式,代码如下:

    {
        "path" : "pages/pay/pay",
        "style" : 
        {
            "navigationBarTitleText": "支付信息",
            "navigationBarTextStyle": "black",
            "navigationBarBackgroundColor": "#ffffff"
        }
    }

通过点击menu.vue“点餐”界面中的“去结算”区域,调用@tap="toPay"方法完成页面跳转至pay.vue页面。跳转前得判断用户是否登录,若已登录,则将购物车列表cart数组通过setStorageSync()方法存入本地数据,用于“支付信息”界面核算金额。

    toPay() {//支付
        if (!this.isLogin) {
            uni.navigateTo({//跳转登录页面
                url: '/pages/login/login'
            })
            return
        }
        uni.showLoading({
            title: '加载中'
        })
        uni.setStorageSync('cart', JSON.parse(JSON.stringify(this.cart)))//将购物车数据存入本地数据
        uni.navigateTo({//跳转支付页面
            url: '/pages/pay/pay'
        })
        uni.hideLoading()
    }

2、在pay.vue页面的onLoad()生命周期中获取本地购物车数据,用于核算金额。从vuex中引入mapState, mapMutations,并在computed计算属性中通过...mapState(['orderType','address','store'])获取商品取餐类型、顾客配送地址、店铺信息。
通过判断'orderType'商品取餐类型决定界面样式,若orderType为takein,效果如图4.3.4,若为takeout,样式如4.3.5。


中间商品列表list-cell为购物车中选购商品,通过v-for循环对cart购物车遍历进行展示。total()、amount()计算属性为总计金额、实付金额,通过
this.cart.reduce()核算,相关代码如下:

    computed: {
        ...mapState(['orderType','address','store']),//获取取餐类型、配送地址、店铺信息
        total() {//总计金额
            return this.cart.reduce((acc, cur) => acc + cur.number * cur.price, 0)
        },
        amount() {//实付金额
            return this.cart.reduce((acc, cur) => acc + cur.number * cur.price, 0)
        }
    },

3、当顾客选择“外卖”配送时,可通过点击第一栏地址信息跳转“我的地址”界面进行切换用户配送地址,选择新的地址后,回退到“支付信息”页面。由于在“首页”界面点击外卖选择完地址后,是跳转到“点餐”界面。因此,需要在chooseAddress()方法中跳转至“我的地址”界面传递一个参数scene=pay进行区分,跳转路劲为url: '/pages/address/address?scene=pay'。
在address.vue的onLoad()方法中获取到scene参数,进行判断。代码如下:

    data() {
        return {
...
...
            scene: 'menu',  //判断哪个页面过来
        }
    },
    async onLoad({scene}) {
        this.scene = scene || 'menu'
    },
...
...
    methods: {
        ...mapMutations(['SET_ADDRESS','SET_ADDRESSES',  'SET_ORDER_TYPE']),
        chooseAddress(address) {
            this.SET_ADDRESS(address) //添加派送的地址
            this.SET_ORDER_TYPE('takeout') //设置状态,自取还是外卖
            if(this.scene == 'menu') {//如果是从地址点过来的选择地址,则跳转menu,选择商品
                uni.switchTab({
                    url: '/pages/menu/menu'
                })
            } else if(this.scene == 'pay') {//如果是支付信息页面跳过来的,重新选择地址后直接返回支付信息页面
                uni.navigateTo({
                    url: '/pages/pay/pay'
                })
            }
        }

4、通过与列表中的云鲤券、备注交互跳转至功能所在处,如图4.4.6为交互处。重要相关代码如下:


4.3.6 其他交互区域.png
    goToPackages() {//跳转云鲤券界面
        uni.navigateTo({
            url: '/pages/packages/packages'
        })
    },
    goToRemark() {//跳转备注页面
        uni.navigateTo({
            url: '/pages/remark/remark?remark=' + this.form.remark
        })
    },

通过goToRemark()方法跳转至“备注”界面。在page文件夹下新建remark.vue“备注”界面,并在pages.json中设置其相应的样式,代码如下:

    {
        "path": "pages/remark/remark",
        "style": {
            "navigationBarTitleText": "备注",
            "navigationBarTextStyle": "black",
            "navigationBarBackgroundColor": "#ffffff"
        }
    }

“备注”界面中需注意,quickInputs为快速备注信息,点击某项信息后通过handleQuickInput(item)方法拼接到this.remark中。若备注中信息超过50字则字体显示为红色,效果如图4.3.7。


4.3.7 超过50字后效果.png

备注信息填写完毕后,通过点击下方“完成”按钮返回上一界面,返回时带入包含备注信息的参数this.remark,此块重要相关代码如下:

    methods: {
        handleQuickInput(item) {
            this.remark = this.remark.concat(" ", item)
        },
        submit() {
            uni.navigateTo({//返回支付页面
                url: "/pages/pay/pay?remark=" + this.remark
            })
        }
    }

回到“支付信息”界面后,在备注栏中将显示备注信息,效果如图4.3.8。


4.3.8 备注信息回调显示.png

5、当顾客所有信息填写完毕后,将对所有商品进行付款操作。若顾客选择的为“外卖”类型,则弹出模态框进行用户地址确认(效果如图4.3.9),确认后清除购物车中商品并跳转至“取餐信息”界面。


通过@tap="submit"进行付款交互,定义布尔参数ensureAddressModalVisible判断是否提示地址模态框,代码如下:

    submit() { //提交付款
        if (this.orderType == 'takeout') { //如果为外卖,提醒地址
            this.ensureAddressModalVisible = true
        } else { //直接走支付
            this.pay()
        }
    }

由于后台开发工程师正在对项目进行研发,前端小组成员使用import orders from '@/api/orders'模拟订单数据。支付后,将订单数据通过SET_ORDER()方法存入,同时调用uni.removeStorageSync('cart')方法将购物车中商品清除,跳转至“取餐信息”界面(take-foods.vue)。

    pay() {
        uni.showLoading({
            title: '加载中'
        })
        //测试订单
        let order = this.orderType == 'takein' ? orders[0] : orders[1]
        order = Object.assign(order, {
            status: 1
        })
        this.SET_ORDER(order)
        uni.removeStorageSync('cart')
        uni.reLaunch({
            url: '/pages/take-foods/take-foods'
        })
        uni.hideLoading()
    }

6、至此,支付结算界面相关功能开发工作已经完成。团队成员应使用SourceTree工具进行版本控制提交,以便为本次工单的开发代码创建历史版本记录。

menu.vue 跳转代码

...
...
...
        <!-- 购物车栏 begin -->
            <view class="cart-box" v-if="cart.length > 0">
                <view class="mark">
                    <image src="/static/images/menu/cart.png" class="cart-img" @tap="openCartPopup"></image>
                    <view class="tag">{{ getCartGoodsNumber }}</view>
                </view>
                <view class="price">¥{{ getCartGoodsPrice }}</view>
                <button type="primary" class="pay-btn" @tap="toPay" :disabled="disabledPay">
                    {{ disabledPay ? `差${spread}元起送` : '去结算' }}
                </button>
            </view>
            <!-- 购物车栏 end -->
...
...
            toPay() {//支付
                if (!this.isLogin) {
                    uni.navigateTo({
                        url: '/pages/login/login'
                    })
                    return
                }
                uni.showLoading({
                    title: '加载中'
                })
                uni.setStorageSync('cart', JSON.parse(JSON.stringify(this.cart)))//将购物车数据存入本地数据
                uni.navigateTo({
                    url: '/pages/pay/pay'
                })
                uni.hideLoading()
            }
...
...

pay.vue 完整代码

<template>
    <!-- 支付页面 -->
    <view class="container position-relative">
        <view style="padding-bottom: 130rpx;">
            <!-- 相关信息(自取、外卖) begin -->
            <view class="section-1">
                <!--第一栏 用户自取  店名-->
                <template v-if="orderType == 'takein'">
                    <list-cell class="location">
                        <view class="flex-fill d-flex justify-content-between align-items-center">
                            <view class="store-name flex-fill">
                                {{ store.name }}
                            </view>
                            <image src="/static/images/navigator-1.png" class="arrow"></image>
                        </view>
                    </list-cell>
                </template>
                <!-- 外卖 选择地址栏 样式 -->
                <template v-else>
                    <list-cell @click="chooseAddress">
                        <view class="w-100 d-flex flex-column">
                            <view class="d-flex align-items-center justify-content-between mb-10">
                                <view class="font-size-extra-lg text-color-base">{{ address.street }}</view>
                                <image src="/static/images/navigator-1.png" class="arrow"></image>
                            </view>
                            <view class="d-flex text-color-assist font-size-sm align-items-center">
                                <view class="mr-10">{{ address.accept_name }}</view>
                                <view class="mr-10">{{ !address.sex ? '先生' : '女士' }}</view>
                                <view>{{ address.mobile }}</view>
                            </view>
                        </view>
                    </list-cell>
                </template>
                <!-- 第二栏 用户自取 样式 -->
                <template v-if="orderType == 'takein'">
                    <list-cell arrow class="meal-time">
                        <view class="flex-fill d-flex justify-content-between align-items-center">
                            <view class="title">取餐时间</view>
                            <view class="time">立即用餐</view>
                        </view>
                    </list-cell>
                    <list-cell class="contact" last :hover="false">
                        <view class="flex-fill d-flex justify-content-between align-items-center">
                            <view class="title" style="flex: 1 0 auto !important;">联系电话</view>
                            <view class="time">
                                <input class="text-right" placeholder="请输入手机号码" value="18666600000" />
                            </view>
                            <view class="contact-tip font-size-sm" style="flex: 1 0 auto !important;">自动填写</view>
                        </view>
                    </list-cell>
                </template>
                <!-- 外卖 样式 -->
                <template v-else>
                    <list-cell>
                        <view class="w-100 d-flex flex-column">
                            <view class="d-flex align-items-center font-size-base text-color-base">
                                <view class="flex-fill">预计送达时间</view>
                                <view class="mr-10">15:18</view>
                                <image src="/static/images/navigator-1.png" class="arrow"></image>
                            </view>
                            <view class="font-size-base text-color-primary">
                                特殊时期减少接触,请修改下方订单备注
                            </view>
                        </view>
                    </list-cell>
                </template>
            </view>
            <!-- 相关信息(自取、外卖) end -->
            <!-- 购物车列表 begin -->
            <view class="section-2">
                <view class="cart d-flex flex-column">
                    <list-cell last v-for="(item, index) in cart" :key="index">
                        <view class="w-100 d-flex flex-column">
                            <view class="d-flex align-items-center mb-10">
                                <view class="name-and-props overflow-hidden">
                                    <view class="text-color-base font-size-lg">
                                        {{ item.name }}
                                    </view>
                                </view>
                                <view
                                    class="d-flex flex-fill justify-content-between align-items-center text-color-base font-size-lg">
                                    <view>x{{ item.number }}</view>
                                    <view>¥{{ item.price }}</view>
                                </view>
                            </view>
                            <view class="text-truncate font-size-base text-color-assist">
                                {{ item.props_text }}
                            </view>
                        </view>
                    </list-cell>
                    <template v-if="orderType == 'takeout'">
                        <list-cell last v-if="store.packing_fee > 0">
                            <view class="w-100 d-flex font-size-base align-items-center justify-content-between">
                                <view>包装费</view>
                                <view>¥{{ parseFloat(store.packing_fee) }}</view>
                            </view>
                        </list-cell>
                        <list-cell last v-if="store.delivery_cost > 0">
                            <view class="w-100 d-flex font-size-base align-items-center justify-content-between">
                                <view>配送费</view>
                                <view>¥{{ parseFloat(store.delivery_cost) }}</view>
                            </view>
                        </list-cell>
                    </template>
                </view>
                <list-cell arrow @click="goToPackages">
                    <view class="flex-fill d-flex justify-content-between align-items-center">
                        <view class="text-color-base">云鲤券</view>
                        <view class="text-color-primary">超值购买优惠券大礼包</view>
                    </view>
                </list-cell>
                <list-cell arrow>
                    <view class="flex-fill d-flex justify-content-between align-items-center">
                        <view class="text-color-base">礼品卡</view>
                        <view class="text-color-primary">请选择</view>
                    </view>
                </list-cell>
                <list-cell last>
                    <view class="flex-fill d-flex justify-content-end align-items-center">
                        <view>总计¥{{ total }},实付</view>
                        <view class="font-size-extra-lg font-weight-bold">¥{{ amount }}</view>
                    </view>
                </list-cell>
            </view>
            <!-- 购物车列表 end -->
            <view class="d-flex align-items-center justify-content-start font-size-sm text-color-warning"
                style="padding: 20rpx 0;">
                <view class="iconfont iconhelp line-height-100"></view>
                <view>优惠券不与满赠、满减活动共享</view>
            </view>
            <!-- 支付方式 begin -->
            <view class="payment">
                <list-cell last :hover="false">
                    <text>支付方式</text>
                </list-cell>
                <list-cell>
                    <view class="d-flex align-items-center justify-content-between w-100 disabled">
                        <view class="iconfont iconbalance line-height-100 payment-icon"></view>
                        <view class="flex-fill">余额支付(余额¥0)</view>
                        <view class="font-size-sm">余额不足</view>
                        <view class="iconfont iconradio-button-off line-height-100 checkbox"></view>
                    </view>
                </list-cell>
                <list-cell last>
                    <view class="d-flex align-items-center justify-content-between w-100">
                        <view class="iconfont iconwxpay line-height-100 payment-icon" style="color: #7EB73A;"></view>
                        <view class="flex-fill">微信支付</view>
                        <view class="iconfont iconradio-button-on line-height-100 checkbox checked"></view>
                    </view>
                </list-cell>
            </view>
            <!-- 支付方式 end -->
            <!-- 备注 begin -->
            <list-cell arrow last @click="goToRemark">
                <view class="d-flex flex-fill align-items-center justify-content-between overflow-hidden">
                    <view class="flex-shrink-0 mr-20">备注</view>
                    <view class="text-color-primary flex-fill text-truncate text-right">{{ form.remark || '点击填写备注' }}
                    </view>
                </view>
            </list-cell>
            <!-- 备注 end -->
        </view>
        <!-- 付款栏 begin -->
        <view
            class="w-100 pay-box position-fixed fixed-bottom d-flex align-items-center justify-content-between bg-white">
            <view class="font-size-sm" style="margin-left: 20rpx;">合计:</view>
            <view class="font-size-lg flex-fill">¥{{ amount }}</view>
            <view class="bg-primary h-100 d-flex align-items-center just-content-center text-color-white font-size-base"
                style="padding: 0 60rpx;" @tap="submit">
                付款
            </view>
        </view>
        <!-- 付款栏 end -->
        <!-- 地址确认模态框 -->
        <modal :show="ensureAddressModalVisible" custom :mask-closable="false"  width="90%">
            <view class="modal-content">
                <view class="d-flex justify-content-end">
                    <image src="/static/images/pay/close.png" style="width: 40rpx; height: 40rpx;"
                        @tap="ensureAddressModalVisible=false"></image>
                </view>
                <view class="d-flex just-content-center align-items-center" style="margin-bottom: 40px;">
                    <view class="font-size-extra-lg text-color-base">请再次确认下单地址</view>
                </view>
                <view
                    class="d-flex font-size-base text-color-base font-weight-bold align-items-center justify-content-between mb-20">
                    <view>{{ address.accept_name }} {{ address.sex ? '女士' : '先生' }}</view>
                    <view>{{ address.mobile }}</view>
                </view>
                <view class="d-flex font-size-sm text-color-assist align-items-center justify-content-between mb-40">
                    <view>{{ address.street + address.door_number }}</view>
                    <button type="primary" size="mini" plain class="change-address-btn"
                        @click="chooseAddress">修改地址</button>
                </view>
                <button type="primary" class="pay_btn" @tap="pay">确认并付款</button>
            </view>
        </modal>
    </view>
</template>

<script>
    import {
        mapState,
        mapMutations
    } from 'vuex'
    import orders from '@/api/orders'
    export default {
        data() {
            return {
                cart: [],
                form: {
                    remark: ''
                },
                ensureAddressModalVisible: false //地址模态框确认栏
            };
        },
        onLoad(option) {
            const {
                remark
            } = option
            this.cart = uni.getStorageSync('cart') //获取本地购物车数据
            remark && this.$set(this.form, 'remark', remark) //确保新增的属性能够实时更新视图
        },
        methods: {
            ...mapMutations(['SET_ORDER']),
            chooseAddress() { //跳转选择地址
                uni.navigateTo({
                    url: '/pages/address/address?scene=pay'
                })
            },
            goToPackages() { //跳转云鲤券界面
                uni.navigateTo({
                    url: '/pages/packages/packages'
                })
            },
            goToRemark() { //跳转备注页面
                uni.navigateTo({
                    url: '/pages/remark/remark?remark=' + this.form.remark
                })
            },
            submit() { //提交付款
                if (this.orderType == 'takeout') { //如果为外卖,提醒地址
                    this.ensureAddressModalVisible = true
                } else { //直接走支付
                    this.pay()
                }
            },
            pay() {
                uni.showLoading({
                    title: '加载中'
                })
                //测试订单
                let order = this.orderType == 'takein' ? orders[0] : orders[1]
                order = Object.assign(order, {
                    status: 1
                })
                this.SET_ORDER(order)
                uni.removeStorageSync('cart')
                uni.reLaunch({
                    url: '/pages/take-foods/take-foods'
                })
                uni.hideLoading()
            }
        },
        computed: {
            ...mapState(['orderType', 'address', 'store']),
            total() { //总计金额
                return this.cart.reduce((acc, cur) => acc + cur.number * cur.price, 0)
            },
            amount() { //实付金额
                return this.cart.reduce((acc, cur) => acc + cur.number * cur.price, 0)
            }
        },
    }
</script>

<style lang="scss">
    .container {
        padding: 30rpx;
    }

    .arrow {
        width: 50rpx;
        height: 50rpx;
        position: relative;
        margin-right: -10rpx;
    }

    .location {
        .store-name {
            font-size: $font-size-lg;
        }

        .iconfont {
            font-size: 50rpx;
            line-height: 100%;
            color: $color-primary;
        }
    }

    .section-1 {
        margin-bottom: 30rpx;

        .contact {
            .contact-tip {
                margin-left: 10rpx;
                border: 2rpx solid $color-primary;
                padding: 6rpx 10rpx;
                color: $color-primary;
            }
        }
    }

    .section-2 {
        .name-and-props {
            width: 65%;
        }
    }

    .payment {
        margin-bottom: 30rpx;

        .disabled {
            color: $text-color-grey;
        }

        .payment-icon {
            font-size: 44rpx;
            margin-right: 10rpx;
        }

        .checkbox {
            font-size: 36rpx;
            margin-left: 10rpx;
        }

        .checked {
            color: $color-primary;
        }
    }

    .pay-box {
        box-shadow: 0 0 20rpx rgba(0, 0, 0, .1);
        height: 100rpx;
    }

    .modal-content {
        .change-address-btn {
            line-height: 2;
            padding: 0 1em;
        }

        .pay_btn {
            width: 100%;
            border-radius: 50rem !important;
            line-height: 3;
        }
    }
</style>

remark.vue 完整代码

<template>
    <!-- 备注页面 -->
    <view class="container w-100 h-100 overflow-hidden">
        <view class="textarea">
            <textarea placeholder-class="text-color-assist font-size-base" v-model="remark"
                class="bg-white w-100 border-box font-size-base remark"
                :class="{'text-color-danger': remarkLength > 50, 'text-color-assist' : remarkLength <=50}"
                placeholder="请填写备注信息" focus/>
            <view class="tips" :class="{'text-color-danger': remarkLength > 50, 'text-color-assist' : remarkLength <=50}">
                {{ remarkLength }}/50
            </view>
        </view>
        <view class="d-flex font-size-base text-color-assist" style="margin: 40rpx 0;">
            快捷输入
        </view>
        <view class="quick-inputs d-flex flex-wrap justify-content-start">
            <view class="quick-input" v-for="(item, index) in quickInputs" :key="index" @tap="handleQuickInput(item)">
                {{ item }}
            </view>
        </view>
        <view class="d-flex just-content-center align-items-center" style="margin-top: 60rpx;">
            <button type="primary" class="submit-btn font-size-base" @tap="submit">完成</button>
        </view>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                remark: '',
                quickInputs: [
                    '请放门把手上', '请放门口', '请放前台桌上', '如地址封闭管理,请电话与我联系'
                ]
            };
        },
        onLoad({remark}) {
            this.remark = remark
        },
        computed: {//计算属性
            remarkLength() {
                return this.remark.length
            },
            isDanger() {
                return this.remark.length > 50
            }
        },
        methods: {
            handleQuickInput(item) {
                this.remark = this.remark.concat(" ", item)
            },
            submit() {
                uni.navigateTo({//返回支付页面
                    url: "/pages/pay/pay?remark=" + this.remark
                })
            }
        }
    }
</script>

<style lang="scss" scoped>
.container {
        padding: 30rpx 40rpx;
        
        .textarea {
            position: relative;
            
            .remark {
                border-radius: 8rpx;
                padding: 30rpx 40rpx;
                height: 320rpx;
                color: $font-size-base;
            }
            
            .tips {
                position: absolute;
                bottom: 30rpx;
                right: 40rpx;
            }
        }
        
        .quick-inputs {
            padding-right: 20rpx;
            
            .quick-input {
                background-color: #FFFFFF;
                border: 2rpx solid $color-primary;
                color: $color-primary;
                font-size: $font-size-base;
                padding: 16rpx 26rpx;
                margin-right: 20rpx;
                margin-bottom: 20rpx;
            }
        }
        
        .submit-btn {
            width: 90%;
            height: 80rpx;
            border-radius: 40rpx;
            line-height: 80rpx;
        }
    }
</style>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容