hookehuyr

feat(payCard): 添加支付协议勾选功能及弹窗展示

在支付卡片组件中添加协议勾选区域,用户需同意协议才能进行支付。新增协议弹窗展示详细内容,并实现协议状态检查与更新逻辑。同时调整了相关样式确保UI一致性。
<!--
* @Date: 2023-12-20 14:11:11
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-31 15:30:59
* @FilePath: /jgdl/src/components/payCard.vue
* @LastEditTime: 2025-08-08 10:33:57
* @FilePath: /jgdl/src/components/paycard.vue
* @Description: 文件描述
-->
<template>
......@@ -12,9 +12,56 @@
<view style="font-size: 32rpx;">实付金额</view>
<view style="color: red; margin: 10rpx 0;"><text style="font-size: 50rpx;">¥</text><text style="font-size: 80rpx;">{{ price }}</text></view>
<view style="font-size: 28rpx; margin-bottom: 20rpx;">支付剩余时间 <text style="color: red;">{{ formatTime(remain_time) }}</text></view>
<nut-button block color="#fb923c" @tap="goToPay">立即支付</nut-button>
<!-- 协议勾选区域 -->
<div v-if="!hasAgreed" class="agreement-section">
<nut-checkbox v-model="isChecked" class="agreement-checkbox">
<view class="checkbox-text">
<text>我已阅读并同意</text>
<text class="agreement-link" @tap.stop="showProtocol">
《支付协议》
</text>
</view>
</nut-checkbox>
</div>
<nut-button
block
color="#fb923c"
:disabled="!hasAgreed && !isChecked"
@tap="goToPay"
>
立即支付
</nut-button>
</view>
</nut-action-sheet>
<!-- 支付协议弹框 -->
<nut-popup
v-model:visible="protocolVisible"
position="right"
:close-on-click-overlay="true"
:safe-area-inset-bottom="true"
:style="{ width: '100%', height: '100%' }"
@close="protocolVisible = false"
>
<view class="protocol-container">
<!-- 标题栏 -->
<view class="protocol-header">
<text class="protocol-title">支付协议</text>
<view class="close-btn" @click="protocolVisible = false">
<text class="close-text">×</text>
</view>
</view>
<!-- 内容区域 -->
<scroll-view class="protocol-scroll" :scroll-y="true">
<view class="protocol-body">
<view class="protocol-text">{{ protocolContent }}</view>
</view>
</scroll-view>
</view>
</nut-popup>
</div>
</template>
......@@ -22,7 +69,8 @@
import Taro from '@tarojs/taro'
import { ref, watch, onMounted, onUnmounted } from 'vue'
import { getCurrentPageUrl } from "@/utils/weapp";
import { payAPI, payCheckAPI } from '@/api/index'
import { payAPI, payCheckAPI, getProfileAPI, updateProfileAPI } from '@/api/index'
import { useUserStore } from '@/stores/user'
/**
* 格式化时间
......@@ -53,8 +101,24 @@ const props = defineProps({
const emit = defineEmits(['close', 'paySuccess']);
const userStore = useUserStore()
const visible = ref(false);
// 协议相关状态
const isChecked = ref(false);
const hasAgreed = ref(false);
const protocolVisible = ref(false);
// 支付协议内容
const protocolContent = ref(`
1. 用户在使用捡个电驴支付服务时,需遵守相关法律法规。
2. 平台有权对异常交易进行风险控制。
3. 用户应确保支付信息的真实性和准确性。
4. 平台将按照约定收取相应的服务费用。
5. 如有争议,双方应友好协商解决。
`)
const onClose = () => {
visible.value = false;
}
......@@ -67,12 +131,14 @@ let timeId = null;
watch(
() => props.visible,
(val) => {
async (val) => {
visible.value = val;
if (val) {
id.value = props.data.id;
price.value = props.data.price;
remain_time.value = props.data.remain_time;
// 检查用户协议状态
await checkAgreementStatus();
}
}
)
......@@ -101,7 +167,77 @@ onUnmounted(() => {
timeId && clearInterval(timeId);
})
/**
* 检查用户是否已同意过协议
*/
const checkAgreementStatus = async () => {
try {
// 调用API获取用户信息
const result = await getProfileAPI()
if (result.code && result.data) {
hasAgreed.value = result.data.is_signed || false
// 更新用户store中的状态
if (userStore.userInfo) {
userStore.userInfo.is_signed = result.data.is_signed
}
} else {
// 如果API调用失败,使用store中的数据作为备选
hasAgreed.value = userStore.userInfo?.is_signed || false
}
} catch (error) {
console.error('获取用户协议状态失败:', error)
// 如果API调用失败,使用store中的数据作为备选
hasAgreed.value = userStore.userInfo?.is_signed || false
}
}
/**
* 显示支付协议
*/
const showProtocol = () => {
protocolVisible.value = true
}
/**
* 处理协议同意
*/
const handleAgreeProtocol = async () => {
if (!isChecked.value) return
try {
// 调用API更新用户协议状态
const result = await updateProfileAPI({
is_signed: true
})
if (result.code) {
hasAgreed.value = true
// 更新用户store中的状态
if (userStore.userInfo) {
userStore.userInfo.is_signed = true
}
}
} catch (error) {
console.error('更新协议状态失败:', error)
}
}
const goToPay = async () => {
// 检查协议状态
if (!hasAgreed.value && !isChecked.value) {
Taro.showToast({
title: '请先同意支付协议',
icon: 'none'
})
return
}
// 如果用户勾选了协议但还未提交,先处理协议同意
if (!hasAgreed.value && isChecked.value) {
await handleAgreeProtocol()
}
if (price.value > 0) { // 金额大于0
// 获取支付参数
const { code, data } = await payAPI({ order_id: id.value });
......@@ -158,5 +294,81 @@ const goToPay = async () => {
</script>
<style lang="less">
.pay-card {
.agreement-section {
margin: 20rpx 0;
padding: 0 20rpx;
.agreement-checkbox {
// display: flex;
// align-items: center;
// justify-content: center;
:deep(.nut-checkbox__label) {
margin-left: 8rpx;
}
.checkbox-text {
font-size: 28rpx;
color: #666;
.agreement-link {
color: #fb923c;
text-decoration: underline;
margin-left: 8rpx;
}
}
}
}
}
.protocol-container {
display: flex;
flex-direction: column;
height: 100%;
background-color: #fff;
.protocol-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 30rpx;
border-bottom: 1rpx solid #eee;
.protocol-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.close-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
.close-text {
font-size: 48rpx;
color: #999;
line-height: 1;
}
}
}
.protocol-scroll {
flex: 1;
.protocol-body {
padding: 30rpx;
.protocol-text {
font-size: 28rpx;
line-height: 1.6;
color: #333;
white-space: pre-line;
}
}
}
}
</style>
......