feat(payment): 实现支付功能并优化相关页面

- 新增 payment 存储对象,用于保存支付相关信息
- 实现全款和部分款项支付逻辑
- 优化直播室、艺术品详情、个人中心等页面的支付相关展示和功能
- 添加千分位格式化函数,提升用户体验
This commit is contained in:
xingyy 2025-02-19 21:03:54 +08:00
parent 0308ace199
commit f4427bc094
10 changed files with 86 additions and 42 deletions

View File

@ -1,6 +1,7 @@
<script setup>
import itemDetail from '@/components/itemDetail/index.vue'
import {userArtwork} from "~/api/goods/index.js";
import {useRouter} from "#vue-router";
definePageMeta({
i18n: 'detail.text8'
})
@ -12,8 +13,10 @@ const initData = async () => {
const res = await userArtwork({uuid})
if (res.status === 0) {
detail.value = res.data
console.log('detail',detail.value)
}
}
const router = useRouter();
const position = ref({x: window?.innerWidth - 120 || 0, y: 240})
const startPosition = ref({x: 0, y: 0})
const isDragging = ref(false)
@ -44,7 +47,9 @@ const onDrag = (e) => {
const stopDrag = () => {
isDragging.value = false
}
const goPay=()=>{
router.push('/payment')
}
onMounted(() => {
document.addEventListener('mousemove', onDrag)
document.addEventListener('mouseup', stopDrag)
@ -57,6 +62,10 @@ onUnmounted(() => {
document.removeEventListener('touchmove', onDrag)
document.removeEventListener('touchend', stopDrag)
})
function formatThousands(num) {
return Number(num).toLocaleString();
}
initData()
</script>
@ -64,8 +73,8 @@ initData()
<div class="relative h-screen-nav flex flex-col">
<itemDetail class="grow-1" :detail-info="detail.auctionArtworkInfo"/>
<div v-if="[1,3,4].includes(detail.status)" class="h-81px bg-#fff flex justify-center pt-7px">
<van-button class="w-213px !h-38px" type="primary">
<span class="text-#fff text-14px">{{ $t('art_detail_page.button') }} RMB10,000</span>
<van-button class="w-213px !h-38px" type="primary" @click="goPay">
<span class="text-#fff text-14px">{{ $t('art_detail_page.button') }} {{detail.leftCurrency}}{{formatThousands(detail.leftPrice)}}</span>
</van-button>
</div>
<div

View File

@ -22,7 +22,6 @@ const confirm=async ()=>{
}else {
router.push('/collectCode/signature/panel')
}
}
</script>

View File

@ -4,6 +4,10 @@ import {authStore} from "~/stores/auth/index.js";
import {useI18n} from 'vue-i18n'
const {auctionData} = liveStore()
const {userInfo}= authStore()
function formatThousands(num) {
return Number(num).toLocaleString();
}
const headList=[
{
label:useI18n().t('live_room.head'),
@ -40,7 +44,7 @@ const headItem=(statusCode)=>{
<div class="text-start shrink-0 w-60px" :style="`color: ${headItem(item.statusCode).color}`" >{{ headItem(item.statusCode).label }}</div>
<div class="text-start shrink-0 w-80px">{{ item.auctionType==='local'? $t('live_room.spot'):$t('live_room.network') }}</div>
<div class="text-start shrink-0 w-80px">{{ item.createdAt }}</div>
<div class="text-start shrink-0 w-80px">{{item.baseCurrency}}{{ item.baseMoney }}</div>
<div class="text-start shrink-0 w-80px">{{item.baseCurrency}}{{ formatThousands(item.baseMoney) }}</div>
<div class="text-start text-#2B53AC shrink-0 w-20px">{{ item.userId===userInfo.ID?$t('live_room.me'):'' }}</div>
</div>
</template>

View File

@ -11,7 +11,7 @@ import {showMinWindow} from "~/components/liveMinWindow/createMinWindow.js";
const { quoteStatus, changeStatus, show, auctionData, getSocketData ,lastSnapshot,fullLive} = liveStore()
const { pageRef } = goodStore()
const { userInfo } = authStore()
const { userInfo ,payment} = authStore()
const showTang = ref(false)
const router = useRouter()
//
@ -168,7 +168,7 @@ const openOne = () => {
const paySide = computed(() => {
//
if (auctionData.value.artwork?.isSoled && auctionData.value.artwork?.buyInfo.userID === userInfo.value.ID) {
if (auctionData.value.artwork?.isSoled && auctionData.value.artwork?.buyInfo?.userID === userInfo.value.ID) {
return true
} else {
return false
@ -176,6 +176,9 @@ const paySide = computed(() => {
})
const goPay = () => {
payment.value.leftCurrency=auctionData.value?.nowAuctionPrice?.currency
payment.value.leftPrice=auctionData.value?.nowAuctionPrice?.successPrice
payment.value.buyUid=auctionData.value?.nowAuctionPrice?.successBuyUuid
handleCapture()
router.push('/signature/protocol')
}

View File

@ -63,6 +63,11 @@ const loadMore = async () => {
const { finished } = await getArtworkList()
localState.value.finished = finished
}
watch(()=>{
return auctionData.value?.artwork?.index
},(newValue)=>{
console.log('newValue',newValue)
})
watch(()=>props.show,(newValue)=>{
if (newValue){
nextTick(()=>{
@ -113,7 +118,7 @@ watch(()=>props.show,(newValue)=>{
loading="lazy"
/>
<div class="w-45px h-17px bg-#2B53AC text-12px line-height-none flex justify-center items-center absolute top-2px left-2px text-#fff">LOT{{item.index}}</div>
<div v-if="auctionData.artwork.index===item?.index" class="w-80px h-20px bg-#B58047 flex line-height-none justify-center items-center text-#fff text-12px bottom-0 absolute blink">{{ $t('live_room.cast') }}</div>
<div v-show="auctionData.artwork.index===item?.index" class="w-80px h-20px bg-#B58047 flex line-height-none justify-center items-center text-#fff text-12px bottom-0 absolute blink">{{ $t('live_room.cast') }}</div>
</div>
<div>
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">

View File

@ -181,15 +181,15 @@ const tipOpen = () => {
<div class="mr-5px">{{ t('live_room.lower_price') }}{{ auctionData?.nowAuctionPrice?.currency }}</div>
<div ref="nextPriceRef" class="min-w-50px"></div>
</div>
<div v-if="quoteStatus" class="mt-10px mb-10px">
<div v-if="quoteStatus&&auctionData?.nowAuctionPrice?.nowPrice!=='0'" class="mt-10px mb-10px">
<van-button @click="goBuy" color="#FFB25F" class="w-344px !h-[40px]">
<div>{{
`${t('live_room.confirm')} ${auctionData?.nowAuctionPrice?.currency} ${auctionData?.nowAuctionPrice?.nextPrice ?? 0}`
`${t('live_room.confirm')} ${auctionData?.nowAuctionPrice?.currency} ${auctionData?.nowAuctionPrice?.nowPrice ?? 0}`
}}
</div>
</van-button>
</div>
<div v-if="!quoteStatus" class="mt-10px mb-10px">
<div v-else class="mt-10px mb-10px">
<van-button @click="tipOpen" color="#D6D6D8" class="w-344px !h-[40px]" >
<div class="text-#7D7D7F text-14px">{{ t('live_room.button') }}</div>
</van-button>

View File

@ -4,45 +4,60 @@ import {createBuyOrder} from "~/api/goods/index.js";
import {goodStore} from "~/stores/goods/index.js";
import { showLoadingToast ,closeToast} from 'vant';
import {authStore} from "~/stores/auth/index.js";
const {checkoutSessionUrl}= authStore()
import {message} from "~/components/x-message/useMessage.js";
const {checkoutSessionUrl,payment}= authStore()
const payStatus=ref(0)
definePageMeta({
title: '支付'
})
const changePayStatus=()=>{
payStatus.value=payStatus.value===0?1:0
}
const { auctionData} = liveStore()
const validateInput = (e) => {
const value = e.target.value
const char = String.fromCharCode(e.charCode)
if (!/[\d.]/.test(char)) {
e.preventDefault()
return
}
if (char === '.' && (value.includes('.') || !value)) {
e.preventDefault()
return
}
if (value.includes('.') && value.split('.')[1]?.length >= 2) {
e.preventDefault()
return
}
}
const amount=ref('')
const confirmPay=async ()=>{
if (payStatus.value===1&&!amount.value){
message.warning('请输入金额')
return
}
if (Number(payment.value.leftPrice)<Number(amount.value)){
message.warning('不得高于全部金额')
return
}
showLoadingToast({
message: '加载中...',
forbidClick: true,
});
const res=await createBuyOrder({
buyUid:auctionData.value?.nowAuctionPrice?.successBuyUuid,
price:String(auctionData.value?.nowAuctionPrice?.successPrice),
currency:auctionData.value?.nowAuctionPrice?.currency
buyUid:payment.value.buyUid,
price:payStatus.value===0?payment.value.leftPrice:amount.value,
currency:payment.value.leftCurrency,
testReturnHost:'http://192.168.88.50:3000',
testReturnEndPoint:'/payment/result'
})
if (res.status===0){
window.location.href=res.data.checkoutSessionUrl
}
}
const handleInput = (e) => {
//
const value = e.target.value
//
let newValue = value.replace(/[^\d.]/g, '')
//
newValue = newValue.replace(/\.{2,}/g, '.')
//
newValue = newValue.replace(/^(\d*\.\d*)\./, '$1')
//
if (newValue.indexOf('.') > 0) {
newValue = newValue.slice(0, newValue.indexOf('.') + 3)
}
// 0
newValue = newValue.replace(/^0+(\d)/, '$1')
amount.value = newValue
}
</script>
<template>
@ -51,9 +66,9 @@ const confirmPay=async ()=>{
<img class="w-126px h-126px" src="@/static/images/dddf34@2x.png" alt="">
</div>
<div class="text-#1A1A1A text-16px mb-25px font-bold">{{payStatus===0?'支付全部':'支付部分'}}</div>
<div class="text-#999999 text-16px mb-24px font-bold" v-if="payStatus===0">{{auctionData?.nowAuctionPrice?.currency}} {{auctionData?.nowAuctionPrice?.successPrice}}</div>
<div class="text-#999999 text-16px mb-24px font-bold" v-if="payStatus===0">{{payment.leftCurrency}} {{payment?.leftPrice}}</div>
<div class="mb-12px" v-else>
<input class="w-272px h-48px bg-#F3F3F3 px-11px text-16px" type="text" :placeholder="`最多${auctionData?.nowAuctionPrice?.currency}${auctionData?.nowAuctionPrice?.successPrice}`" @keydown="validateInput">
<input v-model="amount" class="w-272px h-48px bg-#F3F3F3 px-11px text-16px" type="text" :placeholder="`最多${payment.leftCurrency}${payment?.leftPrice}`" @input="handleInput">
</div>
<div class="text-#2B53AC text-14px" @click="changePayStatus">{{payStatus===1?'支付全部':'支付部分'}}</div>
<div class="w-full mt-auto mb-40px">

View File

@ -10,7 +10,7 @@ definePageMeta({
})
const {t}=useI18n();
const router = useRouter()
const { userInfo } = authStore()
const { userInfo,payment } = authStore()
const showMyList = ref([])
const localState = ref({
finished: true,
@ -47,10 +47,13 @@ const onRefresh = async () => {
localState.value.refreshing = false
}
const goPay = (status) => {
if (status===1){
const goPay = (item) => {
payment.value.leftPrice=item.leftPrice
payment.value.leftCurrency=item.leftCurrency
payment.value.buyUid=item.uuid
if (item.status===1){
router.push('/signature/protocol')
}else if (status===4){
}else if (item.status===4){
router.push('/payment')
}
}
@ -129,7 +132,7 @@ fetchData()
v-if="[1,3,4].includes(item.status)"
class="w-73px !h-30px"
type="primary"
@click.stop="goPay(item.status)"
@click.stop="goPay(item)"
>
<span class="text-12px">{{ $t('art_detail_page.button') }}</span>
</van-button>

View File

@ -5,7 +5,13 @@ export const authStore = createGlobalState(() => {
const userInfo=useLocalStorage('userInfo',{})
const fingerprint=useLocalStorage('fingerprint','')
const checkoutSessionUrl=useLocalStorage('checkoutSessionUrl','')
const payment=useLocalStorage('payment',{
leftPrice:'',
leftCurrency:'',
buyUid:''
})
return{
payment,
checkoutSessionUrl,
userInfo,
RefreshToken,

View File

@ -112,7 +112,7 @@ export const liveStore = createGlobalState(() => {
// 定义常量
const WS_TYPES = {
TIP: 'tip',
STOP_ARTWORK: 'stopArtwor',
STOP_ARTWORK: 'stopArtwork',
OVER: 'over'
}