根据需求的要求,在生成预付订单之后页面中需要弹出一个弹层,弹层中展示的内容为支付方式(渠道),由用户选择一种支付方式进行支付。
该弹层组件是以扩展组件 uni-popup
为核心的,关于 uni-popup
组件的使用文档请查看这里,这里只介绍我们用到的部分:
type
属性,指定弹层出现的位置
is-mask-click
是否允许点击蒙层关闭弹层
maskClick
点击弹层时触发事件
<!-- pages/test/test.vue -->
<script setup>
import { ref } from 'vue'
// 省略前面小节的代码...
// 弹层的引用
const popupRef = ref()
// 点击蒙层
function onMaskClick() {
console.log('蒙层点击了...')
}
// 打开弹层
function openPopup() {
popupRef.value.open()
}
// 关闭弹层
function closePopup() {
popupRef.value.close()
}
</script>
<template>
<scroll-page
background-color="#f6f6f6"
refresher-enabled
@scrolltolower="test"
@refresherrefresh="test"
>
<view class="content">
<!-- 省略前面小节的代码... -->
<view class="popup-demo">
<button @click="openPopup" class="button" type="primary">
打开弹层
</button>
<button @click="closePopup" class="button" type="primary">
关闭弹层
</button>
</view>
<uni-popup
ref="popupRef"
@maskClick="onMaskClick"
:is-mask-click="false"
type="bottom"
>
<view class="popup-container"></view>
</uni-popup>
</view>
</scroll-page>
</template>
<style lang="scss">
.content {
padding: 30rpx 30rpx 0;
overflow: hidden;
}
// 省略前面小节的代码...
.popup-demo {
display: flex;
justify-content: space-between;
margin: 30rpx 0;
.button {
width: 300rpx;
margin: 0;
}
}
.popup-container {
height: 400rpx;
background-color: #fff;
}
</style>
在掌握了 uni-popup
的基本用法后,我们来封装支付渠道组件,组件要满足以下要求:
- 开放打开(open)和关闭(close)弹层的方法
<!-- components/custom-payment/custom-payment.vue -->
<script setup>
import { ref } from 'vue'
// 在线支付弹层
const paymentPopup = ref()
// 打开弹层
function open() {
paymentPopup.value.open()
}
// 关闭弹层
function close() {
paymentPopup.value.close()
}
// 开放关闭和显示弹层的方法
defineExpose({ open, close })
</script>
<template>
<uni-popup :is-mask-click="false" ref="paymentPopup" type="bottom">
<view class="payment-container">
<view class="payment-header">
<text class="title">选择支付方式</text>
<uni-icons
class="uni-icons-close"
size="18"
color="#333"
type="closeempty"
/>
</view>
<view class="order-amount">¥ 99.9元</view>
<uni-list :border="false">
<uni-list-item
title="支付宝支付"
thumb="/static/images/alipay-icon.png"
>
<template #footer>
<radio color="#16C2A3" />
</template>
</uni-list-item>
<uni-list-item
title="微信支付"
thumb="/static/images/wechatpay-icon.png"
>
<template #footer>
<uni-icons v-if="false" size="26" color="#16C2A3" type="checkbox-filled" />
<uni-icons v-else size="26" color="#d1d1d1" type="circle" />
</template>
</uni-list-item>
</uni-list>
<button class="uni-button">立即支付</button>
</view>
</uni-popup>
</template>
<script>
export default {
options: {
styleIsolation: 'shared',
},
}
</script>
<style lang="scss">
.payment-container {
min-height: 400rpx;
border-radius: 30rpx 30rpx 0 0;
background-color: #fff;
padding: 10rpx 30rpx 40rpx;
.payment-header {
height: 88rpx;
line-height: 88rpx;
text-align: center;
margin-bottom: 20rpx;
font-size: 32rpx;
color: #333;
position: relative;
}
.uni-icons-close {
position: absolute;
top: 2rpx;
right: 0;
}
.order-amount {
padding: 10rpx 0 10rpx;
text-align: center;
font-size: 40rpx;
color: #333;
}
:deep(.uni-list-item__container) {
padding: 40rpx 0 !important;
}
:deep(.uni-list-item--hover) {
background-color: #fff !important;
}
:deep(.uni-list-item__icon) {
margin-right: 0;
}
.uni-button {
margin-top: 40rpx;
}
}
</style>
2.支持两个自定义属性,orderId
、amount
<!-- components/custom-payment/custom-payment.vue -->
<script setup>
import { ref } from 'vue'
// 在线支付弹层
const paymentPopup = ref()
// 接收组件外部传入的数据
const paymentProps = defineProps({
// 待支付订单ID
orderId: String,
// 待支付金额
amount: {
type: [String, Number],
default: 0,
},
})
// 省略前面小节的代码...
</script>
<template>
<uni-popup :is-mask-click="false" ref="paymentPopup" type="bottom">
<view class="payment-container">
<!-- 省略前面小节的代码... -->
<view class="order-amount">¥ {{ paymentProps.amount }}</view>
<!-- 省略前面小节的代码... -->
</view>
</uni-popup>
</template>
3.支持 3 个自定义事件,confirm
、change
和 close
- 在用户切换选择支付方式时触发
change
事件
<!-- components/custom-payment/custom-payment.vue -->
<script setup>
import { ref } from 'vue'
// 在线支付弹层
const paymentPopup = ref()
// 支付渠道的索引
const channelIndex = ref(0)
// 支付渠道(方式)
const paymentChannel = [
{
title: '微信支付',
thumb: '/static/images/wechatpay-icon.png',
},
{
title: '支付宝支付',
thumb: '/static/images/alipay-icon.png',
},
]
// 接收组件外部传入的数据
const paymentProps = defineProps({
// 待支付订单ID
orderId: String,
// 待支付金额
amount: {
type: [String, Number],
default: 0,
},
})
// 自定义事件
const paymentEmits = defineEmits(['confirm', 'change', 'close'])
// 切换支付渠道
function onChannelChange(index) {
// 当前选中渠道索引
channelIndex.value = index
// 触发 change 事件
paymentEmits('change', { index })
}
// 省略前面小节的代码...
</script>
<template>
<uni-popup :is-mask-click="false" ref="paymentPopup" type="bottom">
<view class="payment-container">
<!-- 省略前面小节的代码... -->
<uni-list :border="false">
<uni-list-item
v-for="(channel, index) in paymentChannel"
:key="channel.title"
:title="channel.title"
:thumb="channel.thumb"
clickable
@click="onChannelChange(index)"
>
<template #footer>
<uni-icons
v-if="channelIndex === index"
size="26"
color="#16C2A3"
type="checkbox-filled"
/>
<uni-icons v-else size="26" color="#d1d1d1" type="circle" />
</template>
</uni-list-item>
</uni-list>
<button class="uni-button">立即支付</button>
</view>
</uni-popup>
</template>
- 在用户点击了弹层中的立即购买按钮后触发
confirm
事件
<!-- components/custom-payment/custom-payment.vue -->
<script setup>
// 省略前面小节的代码...
</script>
<template>
<uni-popup :is-mask-click="false" ref="paymentPopup" type="bottom">
<view class="payment-container">
<!-- 省略前面小节的代码... -->
<button
@click="$emit('confirm', { index: channelIndex })"
class="uni-button"
>
立即支付
</button>
</view>
</uni-popup>
</template>
- 在用户点击蒙层或者右上角关闭按钮时触发
close
事件
<!-- components/custom-payment/custom-payment.vue -->
<script setup>
// 省略前面小节的代码...
</script>
<template>
<uni-popup
@maskClick="$emit('close')"
:is-mask-click="false"
ref="paymentPopup"
type="bottom"
>
<view class="payment-container">
<view class="payment-header">
<text class="title">选择支付方式</text>
<uni-icons
class="uni-icons-close"
size="18"
color="#333"
type="closeempty"
@click="$emit('close')"
/>
</view>
<!-- 省略前面小节的代码... -->
</view>
</uni-popup>
</template>
在待支付页面中当用户点击了蒙层或右上角关闭按钮时,调用 uni.showModal
弹出确认框:
<!-- subpkg_consult/payment/index.vue -->
<script setup>
import { ref } from 'vue'
import { useConsultStore } from '@/stores/consult'
import { preOrderApi, createOrderApi } from '@/services/consult'
import { patientDetailApi } from '@/services/patient'
// 省略前面小节的代码...
// 待支付订单ID
const orderId = ref('')
// 支付组件引用
const paymentRef = ref()
// 立即支付
async function onPaymentButtonClick() {
// 生成订单接口
const { code, data, message } = await createOrderApi({
type,
illnessType,
depId,
patientId,
...illnessInfo,
})
// 检测接口是否计用成功
if (code !== 10000) return uni.utils.toast(message)
// 获取待支付订单ID
orderId.value = data.id
// 选择支付渠道
paymentRef.value.open()
}
// 当支付弹层关闭时
async function onPaymentClose() {
const { confirm } = await uni.showModal({
title: '关闭支付',
content: '取消支付将无法获得医生回复,医生接诊名额有限,是否确认关闭?',
cancelText: '仍要关闭',
cancelColor: '#848484',
confirmText: '继续支付',
confirmColor: '#16C2A3',
})
if (!confirm) paymentRef.value.close()
}
// 省略前面小节的代码...
</script>
<template>
<!-- 省略前面小节的代码... -->
<!-- 支付渠道 -->
<custom-payment
@close="onPaymentClose"
:amount="preOrderInfo.actualPayment"
:order-id="orderId"
ref="paymentRef"
/>
</template>
custom-payment文件
<script setup>
import { ref } from 'vue'
// 自定义事件,将组件内的执行结果传递给组件外部,组件会多次使用所以将组件内创建自定义事件,可以让每次事件都有不同的调用结果
const paymentEmits = defineEmits(['close', 'change', 'confirm'])
// 在线支付弹层
const paymentPopup = ref()
// 定义默认支付索引值
const channelIndex = ref(0)
// 支付渠道(方式)
const paymentChannel = [
{
title: '微信支付',
thumb: '/static/images/wechatpay-icon.png',
},
{
title: '支付宝支付',
thumb: '/static/images/alipay-icon.png',
},
]
// 切换支付渠道
function onChannelChange(index) {
channelIndex.value = index
// 将数据传递出去,方便组件外部接收
paymentEmits('change', { index })
}
// 打开弹层
function openPopup() {
paymentPopup.value.open()
}
// 自定义组件封装id以及金额
const paymentProps = defineProps({
// 待支付订单ID
orderId: String,
// 待支付金额
amount: {
type: [String, Number],
default: 0,
},
})
// 关闭弹层
function closePopup() {
paymentPopup.value.close()
}
// 开放关闭和显示弹层的方法
defineExpose({ openPopup, closePopup })
</script>
<template>
<uni-popup :is-mask-click="false" ref="paymentPopup" type="bottom">
<view class="payment-container">
<view class="payment-header">
<text class="title">选择支付方式</text>
<uni-icons
class="uni-icons-close"
size="18"
color="#333"
type="closeempty"
@click="$emit('close')"
/>
</view>
<view class="order-amount">¥ {{ paymentProps.amount }}元</view>
<uni-list :border="false">
<uni-list-item
@click="onChannelChange(index)"
v-for="(item, index) in paymentChannel"
:key="item.title"
:title="item.title"
:thumb="item.thumb"
clickable
>
<template #footer>
<uni-icons
v-if="channelIndex === index"
size="26"
color="#16C2A3"
type="checkbox-filled"
/>
<uni-icons v-else size="26" color="#d1d1d1" type="circle" />
</template>
</uni-list-item>
</uni-list>
<button
class="uni-button"
@click="$emit('confirm', { index: channelIndex })"
>
立即支付
</button>
</view>
</uni-popup>
</template>
<script>
// 样式穿透
export default {
options: {
styleIsolation: 'shared',
},
}
</script>
<style lang="scss">
.payment-container {
min-height: 400rpx;
border-radius: 30rpx 30rpx 0 0;
background-color: #fff;
padding: 10rpx 30rpx 40rpx;
.payment-header {
height: 88rpx;
line-height: 88rpx;
text-align: center;
margin-bottom: 20rpx;
font-size: 32rpx;
color: #333;
position: relative;
}
.uni-icons-close {
position: absolute;
top: 2rpx;
right: 0;
}
.order-amount {
padding: 10rpx 0 10rpx;
text-align: center;
font-size: 40rpx;
color: #333;
}
:deep(.uni-list-item__container) {
padding: 40rpx 0 !important;
}
:deep(.uni-list-item--hover) {
background-color: #fff !important;
}
:deep(.uni-list-item__icon) {
margin-right: 0;
}
.uni-button {
margin-top: 40rpx;
}
}
</style>
支付宝支付跳转: window.location.href = 后端传入的url地址
微信支付跳转:wx.requestPayment({ // 四个参数})