所谓的 “立即结算”,本质就是跳转到订单结算台,并且跳转的同时,需要携带上对应的订单参数。
而具体需要哪些参数,就需要基于 【订单结算台】 的需求来定。
准备静态页面
<template>
<div class="pay">
<van-nav-bar fixed title="订单结算台" left-arrow @click-left="$router.go(-1)" />
<div class="address">
<div class="left-icon">
<van-icon name="logistics" />
div>
<div class="info" v-if="true">
<div class="info-content">
<span class="name">小红span>
<span class="mobile">13811112222span>
div>
<div class="info-address">
江苏省 无锡市 南长街 110号 504
div>
div>
<div class="info" v-else>
请选择配送地址
div>
<div class="right-icon">
<van-icon name="arrow" />
div>
div>
<div class="pay-list">
<div class="list">
<div class="goods-item">
<div class="left">
<img src="http://cba.itlike.com/public/uploads/10001/20230321/8f505c6c437fc3d4b4310b57b1567544.jpg" alt="" />
div>
<div class="right">
<p class="tit text-ellipsis-2">
三星手机 SAMSUNG Galaxy S23 8GB+256GB 超视觉夜拍系统 超清夜景 悠雾紫 5G手机 游戏拍照旗舰机s23
p>
<p class="info">
<span class="count">x3span>
<span class="price">¥9.99span>
p>
div>
div>
div>
<div class="flow-num-box">
<span>共 12 件商品,合计:span>
<span class="money">¥1219.00span>
div>
<div class="pay-detail">
<div class="pay-cell">
<span>订单总金额:span>
<span class="red">¥1219.00span>
div>
<div class="pay-cell">
<span>优惠券:span>
<span>无优惠券可用span>
div>
<div class="pay-cell">
<span>配送费用:span>
<span v-if="false">请先选择配送地址span>
<span v-else class="red">+¥0.00span>
div>
div>
<div class="pay-way">
<span class="tit">支付方式span>
<div class="pay-cell">
<span><van-icon name="balance-o" />余额支付(可用 ¥ 999919.00 元)span>
<span class="red"><van-icon name="passed" />span>
div>
div>
<div class="buytips">
<textarea placeholder="选填:买家留言(50字内)" name="" id="" cols="30" rows="10">textarea>
div>
div>
<div class="footer-fixed">
<div class="left">实付款:<span>¥999919span>div>
<div class="tipsbtn">提交订单div>
div>
div>
template>
<script>
export default {
name: 'PayIndex',
data () {
return {
}
},
methods: {
}
}
script>
<style lang="less" scoped>
.pay {
padding-top: 46px;
padding-bottom: 46px;
::v-deep {
.van-nav-bar__arrow {
color: #333;
}
}
}
.address {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 20px;
font-size: 14px;
color: #666;
position: relative;
background: url(@/assets/border-line.png) bottom repeat-x;
background-size: 60px auto;
.left-icon {
margin-right: 20px;
}
.right-icon {
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-7px);
}
}
.goods-item {
height: 100px;
margin-bottom: 6px;
padding: 10px;
background-color: #fff;
display: flex;
.left {
width: 100px;
img {
display: block;
width: 80px;
margin: 10px auto;
}
}
.right {
flex: 1;
font-size: 14px;
line-height: 1.3;
padding: 10px;
padding-right: 0px;
display: flex;
flex-direction: column;
justify-content: space-evenly;
color: #333;
.info {
margin-top: 5px;
display: flex;
justify-content: space-between;
.price {
color: #fa2209;
}
}
}
}
.flow-num-box {
display: flex;
justify-content: flex-end;
padding: 10px 10px;
font-size: 14px;
border-bottom: 1px solid #efefef;
.money {
color: #fa2209;
}
}
.pay-cell {
font-size: 14px;
padding: 10px 12px;
color: #333;
display: flex;
justify-content: space-between;
.red {
color: #fa2209;
}
}
.pay-detail {
border-bottom: 1px solid #efefef;
}
.pay-way {
font-size: 14px;
padding: 10px 12px;
border-bottom: 1px solid #efefef;
color: #333;
.tit {
line-height: 30px;
}
.pay-cell {
padding: 10px 0;
}
.van-icon {
font-size: 20px;
margin-right: 5px;
}
}
.buytips {
display: block;
textarea {
display: block;
width: 100%;
border: none;
font-size: 14px;
padding: 12px;
height: 100px;
}
}
.footer-fixed {
position: fixed;
background-color: #fff;
left: 0;
bottom: 0;
width: 100%;
height: 46px;
line-height: 46px;
border-top: 1px solid #efefef;
font-size: 14px;
display: flex;
.left {
flex: 1;
padding-left: 12px;
color: #666;
span {
color:#fa2209;
}
}
.tipsbtn {
width: 121px;
background: linear-gradient(90deg,#f9211c,#ff6335);
color: #fff;
text-align: center;
line-height: 46px;
display: block;
font-size: 14px;
}
}
style>
1 封装获取地址的接口
import request from '@/utils/request'
// 获取地址列表
export const getAddressList = () => {
return request.get('/address/list')
}
2 页面中 - 调用获取地址
data () {
return {
addressList: []
}
},
computed: {
selectAddress () {
// 这里地址管理不是主线业务,直接获取默认第一条地址
return this.addressList[0]
}
},
async created () {
this.getAddressList()
},
methods: {
async getAddressList () {
const { data: { list } } = await getAddressList()
this.addressList = list
}
}
3 页面中 - 进行渲染
computed: {
longAddress () {
const region = this.selectAddress.region
return region.province + region.city + region.region + this.selectAddress.detail
}
},
<div class="info" v-if="selectAddress?.address_id">
<div class="info-content">
<span class="name">{{ selectAddress.name }}</span>
<span class="mobile">{{ selectAddress.phone }}</span>
</div>
<div class="info-address">
{{ longAddress }}
</div>
</div>
思路分析: 这里的订单结算,有两种情况:
购物车结算,需要两个参数
① mode=“cart”
② cartIds=“cartId, cartId”
立即购买结算,需要三个参数
① mode=“buyNow”
② goodsId=“商品id”
③ goodsSkuId=“商品skuId”【商品规格,如大小、颜色】
都需要跳转时将参数传递过来
封装通用 API 接口 api/order
import request from '@/utils/request'
export const checkOrder = (mode, obj) => {
return request.get('/checkout/order', {
params: {
mode,
delivery: 0,
couponId: 0,
isUsePoints: 0,
...obj
}
})
}
1 跳转时,传递查询参数
layout/cart.vue
<div @click="goPay">结算({{ selCount }})</div>
goPay () {
if (this.selCount > 0) {
this.$router.push({
path: '/pay',
query: {
mode: 'cart',
cartIds: this.selCartList.map(item => item.id).join(',')
}
})
}
}
2 页面中接收参数, 调用接口,获取数据
data () {
return {
order: {},
personal: {}
}
},
computed: {
mode () {
return this.$route.query.mode
},
cartIds () {
return this.$route.query.cartIds
}
}
async created () {
this.getOrderList()
},
async getOrderList () {
if (this.mode === 'cart') {
const { data: { order, personal } } = await checkOrder(this.mode, { cartIds: this.cartIds })
this.order = order
this.personal = personal
}
}
3 基于数据进行渲染
<div class="pay-list" v-if="order.goodsList">
<div class="list">
<div class="goods-item" v-for="item in order.goodsList" :key="item.goods_id">
<div class="left">
<img :src="item.goods_image" alt="" />
div>
<div class="right">
<p class="tit text-ellipsis-2">
{{ item.goods_name }}
p>
<p class="info">
<span class="count">x{{ item.total_num }}span>
<span class="price">¥{{ item.total_pay_price }}span>
p>
div>
div>
div>
<div class="flow-num-box">
<span>共 {{ order.orderTotalNum }} 件商品,合计:span>
<span class="money">¥{{ order.orderTotalPrice }}span>
div>
<div class="pay-detail">
<div class="pay-cell">
<span>订单总金额:span>
<span class="red">¥{{ order.orderTotalPrice }}span>
div>
<div class="pay-cell">
<span>优惠券:span>
<span>无优惠券可用span>
div>
<div class="pay-cell">
<span>配送费用:span>
<span v-if="!selectAddress">请先选择配送地址span>
<span v-else class="red">+¥0.00span>
div>
div>
<div class="pay-way">
<span class="tit">支付方式span>
<div class="pay-cell">
<span><van-icon name="balance-o" />余额支付(可用 ¥ {{ personal.balance }} 元)span>
<span class="red"><van-icon name="passed" />span>
div>
div>
<div class="buytips">
<textarea placeholder="选填:买家留言(50字内)" name="" id="" cols="30" rows="10">textarea>
div>
div>
<div class="footer-fixed">
<div class="left">实付款:<span>¥{{ order.orderTotalPrice }}span>div>
<div class="tipsbtn">提交订单div>
div>
1 点击跳转传参
prodetail/index.vue
<div class="btn" v-if="mode === 'buyNow'" @click="goBuyNow">立刻购买</div>
goBuyNow () {
this.$router.push({
path: '/pay',
query: {
mode: 'buyNow',
goodsId: this.goodsId,
goodsSkuId: this.detail.skuList[0].goods_sku_id,
goodsNum: this.addCount
}
})
}
2 计算属性处理参数
computed: {
...
goodsId () {
return this.$route.query.goodsId
},
goodsSkuId () {
return this.$route.query.goodsSkuId
},
goodsNum () {
return this.$route.query.goodsNum
}
}
3 基于请求时携带参数发请求渲染
async getOrderList () {
...
if (this.mode === 'buyNow') {
const { data: { order, personal } } = await checkOrder(this.mode, {
goodsId: this.goodsId,
goodsSkuId: this.goodsSkuId,
goodsNum: this.goodsNum
})
this.order = order
this.personal = personal
}
}
1 新建一个 mixin 文件 mixins/loginConfirm.js
mixin文件中可以编写Vue组件实例的配置项,通过一定语法,可以直接混入到组件内部data、methods、computed和生命周期函数
注意点:如果此处和组件内提供了同名的data或methods,则组件内优先级更高
export default{ data(){ return { title: '标题' } }, methods(){ sayHi(){} } }
当其他组件引入mixin文件时,该组件中的data就会包含一个title属性,methods中会包含一个sayHi方法。
export default {
methods: {
// 是否需要弹登录确认框
// (1) 需要,返回 true,并直接弹出登录确认框
// (2) 不需要,返回 false
loginConfirm () {
if (!this.$store.getters.token) {
this.$dialog.confirm({
title: '温馨提示',
message: '此时需要先登录才能继续操作哦',
confirmButtonText: '去登陆',
cancelButtonText: '再逛逛'
})
.then(() => {
// 如果希望,跳转到登录 => 登录后能回跳回来,需要在跳转去携带参数 (当前的路径地址)
// this.$route.fullPath (会包含查询参数)
this.$router.replace({
path: '/login',
query: {
backUrl: this.$route.fullPath
}
})
})
.catch(() => {})
return true
}
return false
}
}
}
2 页面中导入,混入方法
import loginConfirm from '@/mixins/loginConfirm'
export default {
name: 'ProDetail',
mixins: [loginConfirm],
...
}
3 页面中调用 混入的方法
async addCart () {
if (this.loginConfirm()) {
return
}
const { data } = await addCart(this.goodsId, this.addCount, this.detail.skuList[0].goods_sku_id)
this.cartTotal = data.cartTotal
this.$toast('加入购物车成功')
this.showPannel = false
console.log(this.cartTotal)
},
goBuyNow () {
if (this.loginConfirm()) {
return
}
this.$router.push({
path: '/pay',
query: {
mode: 'buyNow',
goodsId: this.goodsId,
goodsSkuId: this.detail.skuList[0].goods_sku_id,
goodsNum: this.addCount
}
})
}
1 封装 API 通用方法(统一余额支付)
// 提交订单
export const submitOrder = (mode, params) => {
return request.post('/checkout/submit', {
mode,
delivery: 10, // 物流方式 配送方式 (10快递配送 20门店自提)
couponId: 0, // 优惠券 id
payType: 10, // 余额支付
isUsePoints: 0, // 是否使用积分
...params
})
}
2 买家留言绑定
data () {
return {
remark: ''
}
},
3 注册点击事件,提交订单并支付
提交订单
// 提交订单
async submitOrder () {
if (this.mode === 'cart') {
await submitOrder(this.mode, {
remark: this.remark,
cartIds: this.cartIds
})
}
if (this.mode === 'buyNow') {
await submitOrder(this.mode, {
remark: this.remark,
goodsId: this.goodsId,
goodsSkuId: this.goodsSkuId,
goodsNum: this.goodsNum
})
}
this.$toast.success('支付成功')
this.$router.replace('/myorder')
}
1 基础静态结构
2 components/OrderListItem
2023-07-01 12:02:13
待支付
Apple iPhone 14 Pro Max 256G 银色 移动联通电信5G双卡双待手机
¥ 1299.00
x 3
Apple iPhone 14 Pro Max 256G 银色 移动联通电信5G双卡双待手机
¥ 1299.00
x 3
Apple iPhone 14 Pro Max 256G 银色 移动联通电信5G双卡双待手机
¥ 1299.00
x 3
共12件商品,总金额 ¥29888.00
立刻付款
申请取消
确认收货
评价
3 导入注册
import { Tab, Tabs } from 'vant'
Vue.use(Tab)
Vue.use(Tabs)
1 封装获取订单列表的 API 接口
// 订单列表
export const getMyOrderList = (dataType, page) => {
return request.get('/order/list', {
params: {
dataType,
page
}
})
}
2 给 tab 绑定 name 属性
data () {
return {
active: this.$route.query.dataType || 'all',
page: 1,
list: []
}
},
3 封装调用接口获取数据
methods: {
async getOrderList () {
const { data: { list } } = await getMyOrderList(this.active, this.page)
list.data.forEach((item) => {
item.total_num = 0
item.goods.forEach(goods => {
item.total_num += goods.total_num
})
})
this.list = list.data
}
},
watch: {
active: {
immediate: true,
handler () {
this.getOrderList()
}
}
}
4 动态渲染
{{ item.create_time }}
{{ item.state_text }}
{{ goods.goods_name }}
¥ {{ goods.total_pay_price }}
x {{ goods.total_num }}
共 {{ item.total_num }} 件商品,总金额 ¥{{ item.total_price }}
立刻付款
申请取消
确认收货
评价