合并
This commit is contained in:
commit
e49b6a78d4
@ -1,4 +1,4 @@
|
||||
import { request } from '@/api/http.js'
|
||||
import { request } from '@/api-collect-code/http.js'
|
||||
|
||||
export async function checkPhone(data) {
|
||||
return await request({
|
||||
@ -9,7 +9,7 @@ export async function checkPhone(data) {
|
||||
}
|
||||
export async function userSend(data) {
|
||||
return await request( {
|
||||
url:'/api/v1/m/user/send',
|
||||
url:'/api/v1/m/user/mobile/send',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { request } from '@/api/http.js'
|
||||
import { request } from '@/api-collect-code/http.js'
|
||||
|
||||
export async function offlineQrcodeList(data) {
|
||||
return await request( {
|
||||
@ -56,7 +56,7 @@ export async function offlineQrcode(data) {
|
||||
export async function createOrder(data) {
|
||||
|
||||
return await request( {
|
||||
url:'/api/v1/offlineQrcode/createOrder',
|
||||
url:'/api/v1/offlineQrcode/createOrder/V2',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
|
@ -9,9 +9,9 @@ let http
|
||||
// HTTP 状态码映射 - 使用i18n国际化
|
||||
export function setupHttp() {
|
||||
if (http) return http
|
||||
const {token}= codeAuthStore()
|
||||
const {codeToken}= codeAuthStore()
|
||||
const config = useRuntimeConfig()
|
||||
const baseURL = config.public.NUXT_PUBLIC_API_COLLECT_CODE
|
||||
const baseURL = config.public.NUXT_PUBLIC_API_BASE
|
||||
const router = useRouter()
|
||||
const i18n = useNuxtApp().$i18n
|
||||
|
||||
@ -43,7 +43,7 @@ export function setupHttp() {
|
||||
// 添加 token
|
||||
options.headers = {
|
||||
...options.headers,
|
||||
Authorization: token.value,
|
||||
Authorization: codeToken.value,
|
||||
'accept-language': i18n.locale.value
|
||||
}
|
||||
|
||||
@ -64,11 +64,11 @@ export function setupHttp() {
|
||||
if (data.status === 1) {
|
||||
message.error(data.msg || i18n.t('http.error.operationFailed'))
|
||||
}
|
||||
|
||||
console.log('拦截响应',data)
|
||||
// 处理登录失效
|
||||
if (data.status === 401) {
|
||||
message.error(i18n.t('http.error.loginExpired'))
|
||||
token.value = '' // 清除 token
|
||||
codeToken.value = '' // 清除 token
|
||||
router.replace('/collectCode/login')
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ const subTitle = computed(() => {
|
||||
return route.meta.subTitle ? t(route.meta.subTitle) : ''
|
||||
})
|
||||
const showLeftArrow = computed(() => route.name && routeWhiteList.includes(route.name))
|
||||
console.log('route.name',route.name)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
276
app/components/StripeCheckout.vue
Normal file
276
app/components/StripeCheckout.vue
Normal file
@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<div class="stripe-container">
|
||||
<form id="payment-form" @submit.prevent="handleSubmit">
|
||||
<div id="payment-element">
|
||||
<!--Stripe.js injects the Payment Element-->
|
||||
</div>
|
||||
<button id="submit">
|
||||
<div class="spinner hidden" id="spinner"></div>
|
||||
<span id="button-text">Pay now</span>
|
||||
</button>
|
||||
<div id="payment-message" class="hidden"></div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
// The items the customer wants to buy
|
||||
const items = [{ id: "xl-tshirt", amount: 1000 }]
|
||||
|
||||
const stripe = ref(null)
|
||||
const elements = ref(null)
|
||||
const isLoading = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 初始化 Stripe
|
||||
stripe.value = window.Stripe("pk_test_51QfbSAAB1Vm8VfJq3AWsR4k2mZjnlF7XFrmlbc6XVXrtwXquAUfwzZmOFDbxMIAwqJBgqao8KLt2wmPc4vNOCTeo00WB78KtfV")
|
||||
await initialize()
|
||||
} catch (error) {
|
||||
showMessage("Failed to initialize payment system")
|
||||
}
|
||||
})
|
||||
|
||||
// Fetches a payment intent and captures the client secret
|
||||
const initialize = async () => {
|
||||
try {
|
||||
const response = await fetch("/create-payment-intent", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ items }),
|
||||
})
|
||||
const { clientSecret } = await response.json()
|
||||
|
||||
const appearance = {
|
||||
theme: 'stripe',
|
||||
}
|
||||
|
||||
// 创建 elements 实例
|
||||
elements.value = stripe.value.elements({
|
||||
appearance,
|
||||
clientSecret,
|
||||
})
|
||||
|
||||
// 创建并挂载 Payment Element
|
||||
const paymentElement = elements.value.create("payment", {
|
||||
layout: "accordion",
|
||||
})
|
||||
|
||||
// 确保挂载到正确的 DOM 元素
|
||||
const mountElement = document.getElementById("payment-element")
|
||||
if (mountElement) {
|
||||
paymentElement.mount(mountElement)
|
||||
} else {
|
||||
throw new Error("Payment element mount point not found")
|
||||
}
|
||||
} catch (error) {
|
||||
showMessage("Failed to load payment form")
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!stripe.value || !elements.value) {
|
||||
showMessage("Payment system not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
const { error } = await stripe.value.confirmPayment({
|
||||
elements: elements.value,
|
||||
confirmParams: {
|
||||
return_url: window.location.origin + "/complete.html",
|
||||
},
|
||||
})
|
||||
|
||||
if (error) {
|
||||
if (error.type === "card_error" || error.type === "validation_error") {
|
||||
showMessage(error.message)
|
||||
} else {
|
||||
showMessage("An unexpected error occurred.")
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
showMessage("Payment processing failed")
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const showMessage = (messageText) => {
|
||||
const messageContainer = document.querySelector("#payment-message")
|
||||
|
||||
messageContainer.classList.remove("hidden")
|
||||
messageContainer.textContent = messageText
|
||||
|
||||
setTimeout(() => {
|
||||
messageContainer.classList.add("hidden")
|
||||
messageContainer.textContent = ""
|
||||
}, 4000)
|
||||
}
|
||||
|
||||
// Show a spinner on payment submission
|
||||
const setLoading = (isLoading) => {
|
||||
if (isLoading) {
|
||||
// Disable the button and show a spinner
|
||||
document.querySelector("#submit").disabled = true
|
||||
document.querySelector("#spinner").classList.remove("hidden")
|
||||
document.querySelector("#button-text").classList.add("hidden")
|
||||
} else {
|
||||
document.querySelector("#submit").disabled = false
|
||||
document.querySelector("#spinner").classList.add("hidden")
|
||||
document.querySelector("#button-text").classList.remove("hidden")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.stripe-container {
|
||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
font-size: 16px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 30vw;
|
||||
min-width: 500px;
|
||||
align-self: center;
|
||||
box-shadow: 0px 0px 0px 0.5px rgba(50, 50, 93, 0.1),
|
||||
0px 2px 5px 0px rgba(50, 50, 93, 0.1), 0px 1px 1.5px 0px rgba(0, 0, 0, 0.07);
|
||||
border-radius: 7px;
|
||||
padding: 40px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#payment-message {
|
||||
color: rgb(105, 115, 134);
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
padding-top: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#payment-element {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #0055DE;
|
||||
font-family: Arial, sans-serif;
|
||||
color: #ffffff;
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
padding: 12px 16px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
filter: contrast(115%);
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.spinner,
|
||||
.spinner:before,
|
||||
.spinner:after {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
color: #ffffff;
|
||||
font-size: 22px;
|
||||
text-indent: -99999px;
|
||||
margin: 0px auto;
|
||||
position: relative;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
box-shadow: inset 0 0 0 2px;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.spinner:before,
|
||||
.spinner:after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.spinner:before {
|
||||
width: 10.4px;
|
||||
height: 20.4px;
|
||||
background: #0055DE;
|
||||
border-radius: 20.4px 0 0 20.4px;
|
||||
top: -0.2px;
|
||||
left: -0.2px;
|
||||
-webkit-transform-origin: 10.4px 10.2px;
|
||||
transform-origin: 10.4px 10.2px;
|
||||
-webkit-animation: loading 2s infinite ease 1.5s;
|
||||
animation: loading 2s infinite ease 1.5s;
|
||||
}
|
||||
|
||||
.spinner:after {
|
||||
width: 10.4px;
|
||||
height: 10.2px;
|
||||
background: #0055DE;
|
||||
border-radius: 0 10.2px 10.2px 0;
|
||||
top: -0.1px;
|
||||
left: 10.2px;
|
||||
-webkit-transform-origin: 0px 10.2px;
|
||||
transform-origin: 0px 10.2px;
|
||||
-webkit-animation: loading 2s infinite ease;
|
||||
animation: loading 2s infinite ease;
|
||||
}
|
||||
|
||||
@-webkit-keyframes loading {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
form {
|
||||
width: 80vw;
|
||||
min-width: initial;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -18,7 +18,6 @@ let container = null // DOM容器元素
|
||||
*/
|
||||
export const showMinWindow1 = (props = {}) => {
|
||||
// 服务端渲染时直接返回
|
||||
console.log('!process.client',!process.client)
|
||||
if (!process.client) return null
|
||||
|
||||
// 如果实例已存在,避免重复创建
|
||||
@ -60,7 +59,6 @@ export const showMinWindow1 = (props = {}) => {
|
||||
|
||||
return minWindowInstance
|
||||
} catch (error) {
|
||||
console.error('创建浮动气泡时发生错误:', error)
|
||||
// 发生错误时确保清理资源
|
||||
hideMinWindow1()
|
||||
return null
|
||||
@ -72,7 +70,7 @@ export const showMinWindow1 = (props = {}) => {
|
||||
* 清理所有相关资源和DOM元素
|
||||
*/
|
||||
export const hideMinWindow1 = () => {
|
||||
console.log('!minWindowApp && !container', !minWindowApp && !container);
|
||||
|
||||
|
||||
if (!minWindowApp && !container) return
|
||||
|
||||
@ -93,8 +91,7 @@ export const hideMinWindow1 = () => {
|
||||
document.body.removeChild(existingContainer)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('清理浮动气泡时发生错误:', error)
|
||||
} finally {
|
||||
} finally {
|
||||
// 重置所有状态
|
||||
minWindowApp = null
|
||||
minWindowInstance = null
|
||||
|
@ -28,7 +28,6 @@ export const showMinWindow = (snapshot, props = {}) => {
|
||||
})
|
||||
|
||||
app.config.errorHandler = (err) => {
|
||||
console.error('MinWindow Error:', err)
|
||||
hideMinWindow()
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,7 @@ 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})
|
||||
@ -59,8 +58,7 @@ const goPay=()=>{
|
||||
}else if (detail.value.status===4){
|
||||
router.push('/payment')
|
||||
}
|
||||
console.log('detail',detail.value)
|
||||
//router.push('/payment')
|
||||
//router.push('/payment')
|
||||
}
|
||||
onMounted(() => {
|
||||
document.addEventListener('mousemove', onDrag)
|
||||
|
@ -1,26 +1,72 @@
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import {authStore} from "~/stores/auth/index.js";
|
||||
const {checkoutSessionUrl,payUid}= authStore()
|
||||
import {orderQuery} from "~/api/goods/index.js";
|
||||
import { WebSocketClient } from '@/utils/websocket'
|
||||
const config = useRuntimeConfig()
|
||||
definePageMeta({
|
||||
layout: 'default',
|
||||
title: 'Stripe支付'
|
||||
})
|
||||
console.log('config.public.NUXT_PUBLIC_PKEY',config.public.NUXT_PUBLIC_PKEY);
|
||||
|
||||
const stripe = Stripe(config.public.NUXT_PUBLIC_PKEY)
|
||||
|
||||
|
||||
const route = useRoute()
|
||||
const baseURL = config.public.NUXT_PUBLIC_API_BASE
|
||||
const items = [{ id: "xl-tshirt", amount: 1000 }]
|
||||
const elements = ref(null)
|
||||
const paymentMessage = ref('')
|
||||
const isLoading = ref(false)
|
||||
const showSpinner = ref(false)
|
||||
let pollTimer = null
|
||||
let timeoutTimer = null
|
||||
const router = useRouter()
|
||||
const startPolling = () => {
|
||||
pollTimer = setInterval(async () => {
|
||||
const res = await orderQuery({
|
||||
orderNo: route.query.payUid
|
||||
})
|
||||
if (res.status === 0) {
|
||||
if (res.data.status !== 3) {
|
||||
clearInterval(pollTimer)
|
||||
clearTimeout(timeoutTimer)
|
||||
router.replace({
|
||||
path: route.query.returnUrl,
|
||||
query: {
|
||||
orderNo: route.query.payUid
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
/* timeoutTimer = setTimeout(() => {
|
||||
clearInterval(pollTimer)
|
||||
setLoading(false)
|
||||
}, 180000)*/
|
||||
}
|
||||
let wsClient=null
|
||||
const watchWebSocket = () => {
|
||||
wsClient = new WebSocketClient(
|
||||
config.public.NUXT_PUBLIC_SOCKET_URL
|
||||
)
|
||||
const ws = wsClient.connect('/api/v1/order/ws/v2', {
|
||||
PayUid: route.query.payUid,
|
||||
})
|
||||
ws.onOpen(() => {
|
||||
})
|
||||
ws.onMessage((event) => {
|
||||
router.replace({
|
||||
path: route.query.returnUrl,
|
||||
query: {
|
||||
orderNo: route.query.payUid
|
||||
}
|
||||
})
|
||||
})
|
||||
ws.onClose(() => {
|
||||
})
|
||||
}
|
||||
async function initialize() {
|
||||
const clientSecret = checkoutSessionUrl.value
|
||||
console.log('clientSecret',clientSecret);
|
||||
|
||||
const clientSecret = route.query.stripeKey
|
||||
const appearance = {
|
||||
theme: 'stripe',
|
||||
}
|
||||
@ -38,20 +84,25 @@ async function handleSubmit(e) {
|
||||
e.preventDefault()
|
||||
setLoading(true)
|
||||
|
||||
|
||||
|
||||
const { error } = await stripe.confirmPayment({
|
||||
elements: elements.value,
|
||||
confirmParams: {
|
||||
return_url: "http://192.168.88.68:3000/payment/result?orderNo="+payUid.value,
|
||||
return_url: `${baseURL}${route.query.returnUrl}?orderNo=${route.query.payUid}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (error.type === "card_error" || error.type === "validation_error") {
|
||||
showMessage(error.message)
|
||||
} else {
|
||||
showMessage("An unexpected error occurred.")
|
||||
if (error) {
|
||||
/* clearInterval(pollTimer)
|
||||
clearTimeout(timeoutTimer)*/
|
||||
if (error.type === "card_error" || error.type === "validation_error") {
|
||||
showMessage(error.message)
|
||||
} else {
|
||||
showMessage("An unexpected error occurred.")
|
||||
}
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
function showMessage(messageText) {
|
||||
@ -66,8 +117,17 @@ function setLoading(loading) {
|
||||
showSpinner.value = loading
|
||||
}
|
||||
|
||||
|
||||
onUnmounted(()=>{
|
||||
wsClient.disconnect()
|
||||
clearTimeout(timeoutTimer)
|
||||
clearInterval(pollTimer)
|
||||
|
||||
})
|
||||
onMounted(() => {
|
||||
watchWebSocket()
|
||||
initialize()
|
||||
startPolling()
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -339,4 +399,4 @@ form {
|
||||
}
|
||||
|
||||
/* 其他样式保持不变... */
|
||||
</style>
|
||||
</style>
|
@ -8,7 +8,7 @@ import {message} from '@/components/x-message/useMessage.js'
|
||||
import FingerprintJS from '@fingerprintjs/fingerprintjs'
|
||||
import {checkPhone, mobileLogin, userSend} from "@/api-collect-code/auth/index.js";
|
||||
|
||||
const {userInfo, token, fingerprint} = codeAuthStore()
|
||||
const {userInfo, codeToken, fingerprint} = codeAuthStore()
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const {locale} = useI18n()
|
||||
@ -36,7 +36,6 @@ const countdown = ref(0);
|
||||
const phoneNum = ref('')
|
||||
const code = ref('')
|
||||
const pane = ref(0)
|
||||
const showKeyboard = ref(false);
|
||||
const getFingerprint = async () => {
|
||||
const fp = await FingerprintJS.load()
|
||||
const result = await fp.get()
|
||||
@ -50,36 +49,29 @@ const checkFingerprint = async () => {
|
||||
await router.push('/collectCode/mine')
|
||||
}
|
||||
}
|
||||
const codeInput = ref(null)
|
||||
const isFocused = ref(false)
|
||||
checkFingerprint()
|
||||
const vanSwipeRef = ref(null)
|
||||
const getCode = async () => {
|
||||
loadingRef.value.loading1 = true
|
||||
const res = await checkPhone({
|
||||
tel: phoneNum.value,
|
||||
})
|
||||
loadingRef.value.loading1 = false
|
||||
if (res.status === 0) {
|
||||
const res = await userSend({telNum: phoneNum.value, zone: '+86'})
|
||||
try {
|
||||
const res = await checkPhone({
|
||||
tel: phoneNum.value,
|
||||
})
|
||||
if (res.status === 0) {
|
||||
pane.value = 1
|
||||
vanSwipeRef.value?.swipeTo(pane.value)
|
||||
showKeyboard.value = true
|
||||
const sendRes = await userSend({telNum: phoneNum.value, zone: '+86'})
|
||||
startCountdown()
|
||||
pane.value = 1
|
||||
await nextTick()
|
||||
vanSwipeRef.value?.swipeTo(pane.value)
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取验证码失败:', error)
|
||||
} finally {
|
||||
loadingRef.value.loading1 = false
|
||||
}
|
||||
/* loadingRef.value.loading1 = false
|
||||
if (res.status === 0) {
|
||||
|
||||
|
||||
}
|
||||
pane.value = 1
|
||||
vanSwipeRef.value?.swipeTo(pane.value)
|
||||
showKeyboard.value = true
|
||||
startCountdown();*/
|
||||
/* pane.value = 1
|
||||
vanSwipeRef.value?.swipeTo(pane.value)
|
||||
showKeyboard.value=true
|
||||
startCountdown();*/
|
||||
|
||||
}
|
||||
const changeToPwd = async () => {
|
||||
loginType.value = loginType.value === 0 ? 1 : 0
|
||||
@ -98,7 +90,7 @@ const goLogin = async () => {
|
||||
})
|
||||
if (res.status === 0) {
|
||||
userInfo.value = res.data.accountInfo
|
||||
token.value = res.data.token
|
||||
codeToken.value = res.data.token
|
||||
fingerprint.value = await getFingerprint()
|
||||
|
||||
await router.push('/collectCode/mine');
|
||||
@ -114,98 +106,124 @@ const togglePasswordVisibility = () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-[100vh] w-[100vw] bg-[url('@/static/images/asdfsdd.png')] bg-cover px-[31px] pt-[86px]">
|
||||
<div class="grow-1 w-[100vw] bg-[url('@/static/images/asdfsdd.png')] bg-bottom bg-cover px-[31px] pt-[86px]">
|
||||
<div class="w-full flex justify-center mb-[100px] flex-col items-center">
|
||||
<img class="h-[105px] w-[189px]" src="@/static/images/ghfggff.png" alt="">
|
||||
<img class="h-[29px] w-[108px]" src="@/static/images/qrcodetext.png" alt="">
|
||||
</div>
|
||||
<van-swipe ref="vanSwipeRef" :show-indicators="false" :touchable="false" :lazy-render="true" :loop="false">
|
||||
<van-swipe-item>
|
||||
<div v-show="pane === 0">
|
||||
<div class="">
|
||||
<div class="border-b-[1.7px] mt-[8px]">
|
||||
<van-field v-model="phoneNum" clearable :placeholder="$t('collectCode.login.phoneNumberPlaceholder')">
|
||||
<template #label>
|
||||
<div class="text-[16px] text-[#1A1A1A] flex align-center justify-start">
|
||||
{{ $t('collectCode.login.phoneNumber') }}
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
</div>
|
||||
<div class="border-b-[1.7px] mt-[8px]" v-show="loginType === 1">
|
||||
<van-field
|
||||
v-model="password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
clearable
|
||||
:placeholder="$t('collectCode.login.passwordPlaceholder')"
|
||||
>
|
||||
<template #label>
|
||||
<div class="text-[16px] text-[#1A1A1A] flex align-center justify-start">
|
||||
{{ $t('collectCode.login.password') }}
|
||||
</div>
|
||||
</template>
|
||||
<template #button>
|
||||
<div class="flex justify-center items-center">
|
||||
<van-icon
|
||||
size="20"
|
||||
:name="showPassword ? 'eye-o' : 'closed-eye'"
|
||||
@click="togglePasswordVisibility"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
</div>
|
||||
<div class="flex justify-end mt-[10px]">
|
||||
<div class="text-[14px] text-[#2B53AC]" @click="changeToPwd">
|
||||
{{ loginType === 0 ? $t('collectCode.login.passwordLogin') : $t('collectCode.login.codeLogin') }}
|
||||
<div v-if="pane === 0">
|
||||
<div>
|
||||
<div class="">
|
||||
<div class="border-b-[1.7px] mt-[8px]">
|
||||
<van-field v-model="phoneNum" clearable :placeholder="$t('collectCode.login.phoneNumberPlaceholder')">
|
||||
<template #label>
|
||||
<div class="text-[16px] text-[#1A1A1A] flex align-center justify-start">
|
||||
{{ $t('collectCode.login.phoneNumber') }}
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
</div>
|
||||
<div class="border-b-[1.7px] mt-[8px]" v-if="loginType === 1">
|
||||
<van-field
|
||||
v-model="password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
clearable
|
||||
:placeholder="$t('collectCode.login.passwordPlaceholder')"
|
||||
>
|
||||
<template #label>
|
||||
<div class="text-[16px] text-[#1A1A1A] flex align-center justify-start">
|
||||
{{ $t('collectCode.login.password') }}
|
||||
</div>
|
||||
</template>
|
||||
<template #button>
|
||||
<div class="flex justify-center items-center">
|
||||
<van-icon
|
||||
size="20"
|
||||
:name="showPassword ? 'eye-o' : 'closed-eye'"
|
||||
@click="togglePasswordVisibility"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
</div>
|
||||
<div class="flex justify-end mt-[10px]">
|
||||
<div class="text-[14px] text-[#2B53AC]" @click="changeToPwd">
|
||||
{{ loginType === 0 ? $t('collectCode.login.passwordLogin') : $t('collectCode.login.codeLogin') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div/>
|
||||
</div>
|
||||
<div class="mt-[55px]">
|
||||
<div v-if="loginType === 0">
|
||||
<van-button :loading="loadingRef.loading1" v-if="phoneNum" :loading-text="$t('collectCode.login.getCode')"
|
||||
type="primary" block style="height: 48px" @click="getCode">{{ $t('collectCode.login.getCode') }}
|
||||
</van-button>
|
||||
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">{{ $t('collectCode.login.getCode') }}</van-button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<van-button type="primary" v-if="password" block :loading="loadingRef.loading2" :loading-text="$t('collectCode.login.login')"
|
||||
style="height: 48px;margin-top:10px" @click="goLogin">{{ $t('collectCode.login.login') }}
|
||||
</van-button>
|
||||
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">{{ $t('collectCode.login.login') }}</van-button>
|
||||
<div class="mt-[55px]">
|
||||
<div v-if="loginType === 0">
|
||||
<van-button :loading="loadingRef.loading1" v-if="phoneNum" :loading-text="$t('collectCode.login.getCode')"
|
||||
type="primary" block style="height: 48px" @click="getCode">{{
|
||||
$t('collectCode.login.getCode')
|
||||
}}
|
||||
</van-button>
|
||||
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">
|
||||
{{ $t('collectCode.login.getCode') }}
|
||||
</van-button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<van-button type="primary" v-if="password" block :loading="loadingRef.loading2"
|
||||
:loading-text="$t('collectCode.login.login')"
|
||||
style="height: 48px;margin-top:10px" @click="goLogin">{{ $t('collectCode.login.login') }}
|
||||
</van-button>
|
||||
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">
|
||||
{{ $t('collectCode.login.login') }}
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-swipe-item>
|
||||
<van-swipe-item>
|
||||
<div v-show="pane === 1">
|
||||
<div class="flex mb-[16px]">
|
||||
<div class="text-[16px] text-[#BDBDBD] mr-[10px]">{{ $t('collectCode.login.hasSendTo') }}</div>
|
||||
<div class="text-[16px] text-[#000]">+86 {{ phoneNum }}</div>
|
||||
</div>
|
||||
<van-password-input :value="code" :gutter="10" :mask="false" focused @focus="showKeyboard = true"/>
|
||||
<div class="flex justify-between">
|
||||
<div :class="`${countdown>0?'text-#BDBDBD':'text-#2B53AC'} text-14px`">
|
||||
{{ $t('collectCode.login.reSend') }}<span v-if="countdown>0">({{ countdown }})</span>
|
||||
<div v-if="pane == 1">
|
||||
<div>
|
||||
<div class="flex mb-[16px]">
|
||||
<div class="text-[16px] text-[#BDBDBD] mr-[10px]">{{ $t('collectCode.login.hasSendTo') }}</div>
|
||||
<div class="text-[16px] text-[#000]">+86 {{ phoneNum }}</div>
|
||||
</div>
|
||||
<div @click="goBack" class="text-#2B53AC text-14px">
|
||||
{{ $t('collectCode.login.back') }}
|
||||
<div class="relative">
|
||||
<van-password-input
|
||||
:value="code"
|
||||
:gutter="10"
|
||||
:mask="false"
|
||||
:focused="isFocused"
|
||||
/>
|
||||
<input
|
||||
v-model="code"
|
||||
type="tel"
|
||||
maxlength="6"
|
||||
ref="codeInput"
|
||||
class="opacity-0 absolute top-0 left-0 h-full w-full z-999"
|
||||
@input="code = $event.target.value.replace(/\D/g, '').slice(0, 6)"
|
||||
@focus="isFocused = true"
|
||||
@blur="isFocused = false"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<div :class="`${countdown>0?'text-#BDBDBD':'text-#2B53AC'} text-14px`">
|
||||
{{ $t('collectCode.login.reSend') }}<span v-if="countdown>0">({{ countdown }})</span>
|
||||
</div>
|
||||
<div @click="goBack" class="text-#2B53AC text-14px">
|
||||
{{ $t('collectCode.login.back') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[17px]">
|
||||
<van-button v-if="code.length === 6" type="primary" block :loading="loadingRef.loading2"
|
||||
:loading-text="$t('collectCode.login.login')" style="height: 48px" @click="goLogin">
|
||||
{{ $t('collectCode.login.login') }}
|
||||
</van-button>
|
||||
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">
|
||||
{{ $t('collectCode.login.login') }}
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[17px]">
|
||||
<van-button v-if="code.length === 6" type="primary" block :loading="loadingRef.loading2"
|
||||
:loading-text="$t('collectCode.login.login')" style="height: 48px" @click="goLogin">
|
||||
{{ $t('collectCode.login.login') }}
|
||||
</van-button>
|
||||
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">
|
||||
{{ $t('collectCode.login.login') }}
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
<van-number-keyboard v-model="code" :show="showKeyboard" @blur="showKeyboard = false"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -33,7 +33,6 @@ const getQRBase64 = async () => {
|
||||
errorCorrectionLevel: 'H'
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('生成二维码失败:', err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
@ -45,7 +44,26 @@ const openQrCode=async ()=>{
|
||||
QRUrl.value=base64
|
||||
show.value=true
|
||||
}
|
||||
/**
|
||||
* 将数字格式化为"250XX"格式,其中XX是两位数
|
||||
* @param {number} num - 要格式化的数字
|
||||
* @return {string} - 格式化后的字符串
|
||||
*/
|
||||
function formatNumber(num) {
|
||||
// 确保输入是有效数字
|
||||
if (typeof num !== 'number' && isNaN(Number(num))) {
|
||||
throw new Error('输入必须是有效数字');
|
||||
}
|
||||
|
||||
// 转换为数字类型(以防输入是字符串数字)
|
||||
const number = Number(num);
|
||||
|
||||
// 数字部分格式化为两位数,不足补0
|
||||
const formattedNum = number.toString().padStart(2, '0');
|
||||
|
||||
// 添加前缀"250"并返回结果
|
||||
return `250${formattedNum}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -59,7 +77,7 @@ const openQrCode=async ()=>{
|
||||
<XImage class="w-57px h-56px rounded-4px" :src="data.hdPic"></XImage>
|
||||
</div>
|
||||
<div class="text-12px text-#1E1E1E">
|
||||
<div>{{ $t('collectCode.qrcode.card.lotNo') }}{{ data.lotNo }}</div>
|
||||
<div>{{ $t('collectCode.qrcode.card.lotNo') }}{{ formatNumber(data.lotNo) }}</div>
|
||||
<div>{{ $t('collectCode.qrcode.card.creator') }}{{ data.userName }}</div>
|
||||
<div>{{ $t('collectCode.qrcode.card.createTime') }}{{data.createdAt}}</div>
|
||||
</div>
|
||||
|
@ -30,7 +30,6 @@ const initData = async () => {
|
||||
}
|
||||
const show=ref(false)
|
||||
const close=()=>{
|
||||
console.log('show',show.value)
|
||||
show.value=false
|
||||
}
|
||||
const logOut=()=>{
|
||||
@ -81,8 +80,11 @@ const loadMore = async () => {
|
||||
const abnormal=ref(false)
|
||||
const abnormalRow=ref({})
|
||||
const inputLotNo=async (data)=>{
|
||||
if (createForm.value.lotNo<=25000){
|
||||
return
|
||||
}
|
||||
const res=await offlineQrcodeList({
|
||||
lotNo:createForm.value.lotNo
|
||||
lotNo:createForm.value.lotNo-25000
|
||||
})
|
||||
if (res.status===0){
|
||||
if (res.data.Data?.length>0){
|
||||
|
@ -10,7 +10,7 @@ import {codeAuthStore} from "~/stores-collect-code/auth/index.js";
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
const {t} = useI18n();
|
||||
const {checkoutSessionUrl,qrUid,qrData} = codeAuthStore()
|
||||
const {checkoutSessionUrl,qrUid,qrData,codePKey,codePayUid} = codeAuthStore()
|
||||
const payStatus = ref(0)
|
||||
definePageMeta({
|
||||
i18n: 'payment.title'
|
||||
@ -19,6 +19,7 @@ const changePayStatus = () => {
|
||||
payStatus.value = payStatus.value === 0 ? 1 : 0
|
||||
}
|
||||
const amount = ref('')
|
||||
const router = useRouter()
|
||||
const confirmPay = async () => {
|
||||
if (payStatus.value === 1 && !amount.value) {
|
||||
message.warning(t('collectCode.payment.enterAmount'))
|
||||
@ -40,7 +41,18 @@ const confirmPay = async () => {
|
||||
testReturnEndPoint: '/collectCode/payment/result'
|
||||
})
|
||||
if (res.status === 0) {
|
||||
window.location.href = res.data.checkoutSessionUrl
|
||||
codePKey.value=res.data.checkoutSessionUrl
|
||||
codePayUid.value=res.data.payUid
|
||||
router.push({
|
||||
path:'/checkoutPage',
|
||||
query:{
|
||||
payUid:res.data.payUid,
|
||||
returnUrl:'/collectCode/payment/result',
|
||||
stripeKey:res.data.checkoutSessionUrl
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,8 +87,8 @@ const handleInput = (e) => {
|
||||
{{ qrData?.leftPrice }}
|
||||
</div>
|
||||
<div class="mb-12px" v-else>
|
||||
<input v-model="amount" class="w-272px h-48px bg-#F3F3F3 px-11px text-16px" type="text"
|
||||
:placeholder="$t('collectCode.payment.maxAmount', { currency: qrData.currency, price: qrData?.leftPrice })" @input="handleInput">
|
||||
<input v-model="amount" class="w-272px h-48px bg-#F3F3F3 px-11px text-16px" type="text"
|
||||
:placeholder="`${$t('collectCode.payment.maxAmount')} ${qrData.currency} ${qrData?.leftPrice}`" @input="handleInput">
|
||||
</div>
|
||||
<div class="text-#2B53AC text-14px" @click="changePayStatus">{{ payStatus === 1 ? $t('collectCode.payment.fullPayment') : $t('collectCode.payment.partialPayment') }}</div>
|
||||
<div class="w-full mt-auto mb-40px">
|
||||
|
@ -39,8 +39,7 @@ const fetchPmblPdf = async () => {
|
||||
})
|
||||
pmblUrl.value = res.data?.viewUrl // 假设接口返回的PDF URL在data字段中
|
||||
} catch (error) {
|
||||
console.error('获取拍卖笔录失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听折叠面板变化
|
||||
|
@ -8,7 +8,6 @@ definePageMeta({
|
||||
i18n: 'countryRegion.title',
|
||||
})
|
||||
const router = useRouter()
|
||||
console.log('router',router)
|
||||
const { t, locale } = useI18n()
|
||||
const value = ref('');
|
||||
const alphabet = computed(() => {
|
||||
@ -144,7 +143,6 @@ const handleCountrySelect = (country) => {
|
||||
}
|
||||
|
||||
initData()
|
||||
console.log('searchCountry',searchCountry.value)
|
||||
// 监听语言变化,重新初始化数据
|
||||
watch(locale, () => {
|
||||
initData()
|
||||
|
@ -40,6 +40,26 @@ const openShow = async (item) => {
|
||||
localState.value.showDetail = true
|
||||
currentItem.value = item
|
||||
}
|
||||
/**
|
||||
* 将数字格式化为"250XX"格式,其中XX是两位数
|
||||
* @param {number} num - 要格式化的数字
|
||||
* @return {string} - 格式化后的字符串
|
||||
*/
|
||||
function formatNumber(num) {
|
||||
// 确保输入是有效数字
|
||||
if (typeof num !== 'number' && isNaN(Number(num))) {
|
||||
throw new Error('输入必须是有效数字');
|
||||
}
|
||||
|
||||
// 转换为数字类型(以防输入是字符串数字)
|
||||
const number = Number(num);
|
||||
|
||||
// 数字部分格式化为两位数,不足补0
|
||||
const formattedNum = number.toString().padStart(2, '0');
|
||||
|
||||
// 添加前缀"250"并返回结果
|
||||
return `250${formattedNum}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -73,7 +93,7 @@ const openShow = async (item) => {
|
||||
<div
|
||||
class="absolute rounded-2px overflow-hidden line-height-12px left-[8px] top-[8px] h-[17px] w-[55px] flex items-center justify-center bg-[#2b53ac] text-[12px] text-[#fff]"
|
||||
>
|
||||
Lot{{ item.index }}
|
||||
Lot{{ formatNumber(item.index) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-[8px]">
|
||||
|
@ -30,7 +30,6 @@ const captureVideoFrame = () => {
|
||||
try {
|
||||
const video = document.querySelector('#J_prismPlayer video')
|
||||
if (!video) {
|
||||
console.error('未找到视频元素')
|
||||
return null
|
||||
}
|
||||
|
||||
@ -42,7 +41,6 @@ const captureVideoFrame = () => {
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
|
||||
return canvas.toDataURL('image/jpeg', 0.9)
|
||||
} catch (error) {
|
||||
console.error('获取视频截图失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +118,6 @@ const captureVideoFrame = () => {
|
||||
try {
|
||||
const video = document.querySelector('#J_prismPlayer video')
|
||||
if (!video) {
|
||||
console.error('未找到视频元素')
|
||||
return null
|
||||
}
|
||||
|
||||
@ -130,7 +129,6 @@ const captureVideoFrame = () => {
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
|
||||
return canvas.toDataURL('image/jpeg', 0.9)
|
||||
} catch (error) {
|
||||
console.error('获取视频截图失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
@ -144,8 +142,7 @@ const handleCapture = () => {
|
||||
onClick:()=>{
|
||||
router.replace('/')
|
||||
fullLive.value=true
|
||||
console.log('执行')
|
||||
}
|
||||
}
|
||||
})*!/
|
||||
}*/
|
||||
showMinWindow1({
|
||||
|
@ -66,8 +66,7 @@ const loadMore = async () => {
|
||||
watch(()=>{
|
||||
return auctionData.value?.artwork?.index
|
||||
},(newValue)=>{
|
||||
console.log('newValue',newValue)
|
||||
})
|
||||
})
|
||||
watch(()=>props.show,(newValue)=>{
|
||||
if (newValue){
|
||||
nextTick(()=>{
|
||||
|
@ -19,7 +19,6 @@ const player = ref(null)
|
||||
const {quoteStatus, show, playerId, show1, auctionData, getSocketData, getLiveLink, fullLive} = liveStore()
|
||||
const pullLink = ref('')
|
||||
const handlePlayerError = (error) => {
|
||||
console.error('播放器错误:', error)
|
||||
showConfirmDialog({
|
||||
message: t('live_room.error_mess'),
|
||||
showCancelButton: true
|
||||
@ -81,8 +80,7 @@ const initializePlayer = async () => {
|
||||
|
||||
})
|
||||
player.value.on('loading', () => {
|
||||
console.log('loading')
|
||||
})
|
||||
})
|
||||
player.value.on('error', handlePlayerError)
|
||||
} catch (error) {
|
||||
showConfirmDialog({
|
||||
@ -92,8 +90,7 @@ const initializePlayer = async () => {
|
||||
initializePlayer()
|
||||
}).catch(() => {
|
||||
})
|
||||
console.error('播放器初始化失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@ const loadingRef=ref({
|
||||
})
|
||||
const isExist=ref(false)//帐号是否存在 true存在
|
||||
const isReal=ref(false) //isReal 是否实名过
|
||||
const codeInput=ref(null)
|
||||
function goToPage() {
|
||||
router.push('/countryRegion');
|
||||
}
|
||||
@ -40,11 +41,9 @@ const countdown = ref(0);
|
||||
const phoneNum = ref('')
|
||||
const code = ref('')
|
||||
const pane = ref(0)
|
||||
const showKeyboard = ref(false);
|
||||
// 根据语言获取默认国家
|
||||
const getDefaultCountry = () => {
|
||||
let defaultCode = 'CN' // 默认中国大陆
|
||||
console.log('locale.value',locale.value)
|
||||
switch (locale.value) {
|
||||
case 'zh-CN':
|
||||
defaultCode = 'CN'
|
||||
@ -74,7 +73,6 @@ const defaultCountry = getDefaultCountry()
|
||||
|
||||
|
||||
const selectedCountry = ref(route.query.countryName || defaultCountry.name)
|
||||
console.log('selectedCountry',selectedCountry.value)
|
||||
onMounted(()=>{
|
||||
selectedZone.value=route.query.zone || defaultCountry.zone
|
||||
})
|
||||
@ -100,13 +98,8 @@ const getCode =async () => {
|
||||
}
|
||||
pane.value = 1
|
||||
vanSwipeRef.value?.swipeTo(pane.value)
|
||||
showKeyboard.value=true
|
||||
|
||||
startCountdown();
|
||||
/* pane.value = 1
|
||||
vanSwipeRef.value?.swipeTo(pane.value)
|
||||
showKeyboard.value=true
|
||||
startCountdown();*/
|
||||
|
||||
}
|
||||
const goBack = () => {
|
||||
code.value = ''
|
||||
@ -136,8 +129,7 @@ const goLogin =async () => {
|
||||
if (res1.status===0){
|
||||
window.location.href=res1.data.h5Url
|
||||
}
|
||||
console.log('123')
|
||||
}else {
|
||||
}else {
|
||||
await router.push('/');
|
||||
}
|
||||
}
|
||||
@ -145,7 +137,7 @@ const goLogin =async () => {
|
||||
}
|
||||
const isKeyboardVisible = ref(false)
|
||||
const windowHeight = ref(window.innerHeight)
|
||||
|
||||
const isFocused = ref(false)
|
||||
onMounted(() => {
|
||||
// 记录初始窗口高度
|
||||
windowHeight.value = window.innerHeight
|
||||
@ -163,7 +155,7 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-screen-nav w-[100vw] bg-[url('@/static/images/asdfsdd.png')] bg-cover px-[31px] pt-[86px]">
|
||||
<div class="w-[100vw] bg-[url('@/static/images/asdfsdd.png')] bg-bottom bg-cover grow-1 px-[31px] pt-[86px]">
|
||||
<div class="w-full flex justify-center mb-[100px]">
|
||||
<img class="h-[105px] w-[189px]" src="@/static/images/ghfggff.png" alt="">
|
||||
</div>
|
||||
@ -186,7 +178,6 @@ onUnmounted(() => {
|
||||
</template>
|
||||
</van-field>
|
||||
</div>
|
||||
<div />
|
||||
</div>
|
||||
<div class="mt-[55px]">
|
||||
<van-button :loading="loadingRef.loading1" v-if="phoneNum" :loading-text="$t('login.getCode')" color="#2B53AC" block style="height: 48px" @click="getCode">{{ $t('login.getCode') }}</van-button>
|
||||
@ -200,7 +191,24 @@ onUnmounted(() => {
|
||||
<div class="text-[16px] text-[#BDBDBD] mr-[10px]">{{ $t('login.hasSendTo') }}</div>
|
||||
<div class="text-[16px] text-[#000]">+{{ selectedZone }} {{ phoneNum }}</div>
|
||||
</div>
|
||||
<van-password-input :value="code" :gutter="10" :mask="false" focused @focus="showKeyboard = true" />
|
||||
<div class="relative">
|
||||
<van-password-input
|
||||
:value="code"
|
||||
:gutter="10"
|
||||
:mask="false"
|
||||
:focused="isFocused"
|
||||
/>
|
||||
<input
|
||||
v-model="code"
|
||||
type="tel"
|
||||
maxlength="6"
|
||||
ref="codeInput"
|
||||
class="opacity-0 absolute top-0 left-0 h-full w-full"
|
||||
@input="code = $event.target.value.replace(/\D/g, '').slice(0, 6)"
|
||||
@focus="isFocused = true"
|
||||
@blur="isFocused = false"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<div :class="`${countdown>0?'text-#BDBDBD':'text-#2B53AC'} text-14px`">
|
||||
{{ $t('login.reSend') }}<span v-if="countdown>0">({{countdown}})</span>
|
||||
@ -216,7 +224,6 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
<van-number-keyboard v-model="code" :show="showKeyboard" @blur="showKeyboard = false" />
|
||||
<div v-if="!isKeyboardVisible" class="text-center text-14px absolute left-1/2 transform translate-x--1/2 bottom-20px">
|
||||
{{ $t('login.agreement') }}<span class="text-#3454AF " @click="$router.push('/privacyPolicy')">{{ $t('login.privacyPolicy') }}</span>
|
||||
</div>
|
||||
|
@ -4,6 +4,7 @@ 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";
|
||||
|
||||
import {message} from "~/components/x-message/useMessage.js";
|
||||
const {checkoutSessionUrl,payment,payUid}= authStore()
|
||||
const payStatus=ref(0)
|
||||
@ -38,14 +39,18 @@ const confirmPay=async ()=>{
|
||||
testReturnHost:window.location.origin,
|
||||
testReturnEndPoint:'/payment/result'
|
||||
})
|
||||
console.log('res',res);
|
||||
|
||||
if (res.status===0){
|
||||
// if (res.status===0){
|
||||
// window.location.href=res.data.checkoutSessionUrl
|
||||
// }
|
||||
checkoutSessionUrl.value=res.data.checkoutSessionUrl
|
||||
payUid.value=res.data.payUid
|
||||
router.push('/payment/checkoutPage')
|
||||
router.push({
|
||||
path:'/checkoutPage',
|
||||
query:{
|
||||
payUid:res.data.payUid,
|
||||
returnUrl:'/payment/result',
|
||||
stripeKey:res.data.checkoutSessionUrl
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const handleInput = (e) => {
|
||||
|
@ -14,21 +14,28 @@ let timer = null
|
||||
let startTime = Date.now()
|
||||
|
||||
const queryOrder = async () => {
|
||||
// 首先检查是否已经超过5秒
|
||||
if (Date.now() - startTime > 5000) {
|
||||
clearInterval(timer)
|
||||
closeToast()
|
||||
return
|
||||
}
|
||||
|
||||
showLoadingToast({
|
||||
message: '加载中...',
|
||||
forbidClick: true,
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
const res = await orderQuery({
|
||||
orderNo: route.query.orderNo
|
||||
})
|
||||
|
||||
|
||||
if (res.status === 0) {
|
||||
resData.value = res.data
|
||||
|
||||
// 如果状态为1或者超过5秒,停止轮询
|
||||
if (resData.value.status === 1 || Date.now() - startTime > 5000) {
|
||||
|
||||
// 只在支付成功时停止轮询
|
||||
if (resData.value.status === 1) {
|
||||
clearInterval(timer)
|
||||
closeToast()
|
||||
}
|
||||
@ -68,11 +75,12 @@ const goHome = () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-[100vw] h-screen-nav bg-[url('@/static/images/3532@2x.png')] bg-cover grow-1 flex flex-col items-center px-30px">
|
||||
<div
|
||||
class="w-[100vw] h-screen-nav bg-[url('@/static/images/3532@2x.png')] bg-cover grow-1 flex flex-col items-center px-30px">
|
||||
<div class="flex flex-col items-center mt-150px">
|
||||
<img class="w-119px h-120px mb-36px" src="@/static/images/5554@2x1.png" alt="">
|
||||
<div class="text-#000 text-16px mb-25px">{{statusLabel[resData.status]}}!</div>
|
||||
<div class="text-#999 text-16px">{{resData.currency}}{{resData.money}}</div>
|
||||
<div class="text-#000 text-16px mb-25px">{{ statusLabel[resData.status] }}!</div>
|
||||
<div class="text-#999 text-16px">{{ resData.currency }}{{ resData.money }}</div>
|
||||
</div>
|
||||
<div class="w-full mt-auto mb-40px">
|
||||
<van-button type="primary" block @click="goHome">
|
||||
|
@ -37,8 +37,7 @@ const fetchData = async () => {
|
||||
showMyList.value = groupByDate(res.data.data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onRefresh = async () => {
|
||||
|
@ -33,8 +33,7 @@ const fetchPmblPdf = async () => {
|
||||
})
|
||||
pmblUrl.value = res.data?.viewUrl // 假设接口返回的PDF URL在data字段中
|
||||
} catch (error) {
|
||||
console.error('获取拍卖笔录失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听折叠面板变化
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { setupHttp } from '@/api/http'
|
||||
|
||||
import { setupHttp as setupHttp1} from '@/api-collect-code/http'
|
||||
export default defineNuxtPlugin(() => {
|
||||
setupHttp()
|
||||
setupHttp1()
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createGlobalState,useLocalStorage } from '@vueuse/core'
|
||||
export const codeAuthStore = createGlobalState(() => {
|
||||
const token=useLocalStorage('token','')
|
||||
const codeToken=useLocalStorage('codeToken','')
|
||||
const RefreshToken=useLocalStorage('RefreshToken','')
|
||||
const userInfo=useLocalStorage('userInfo',{})
|
||||
const fingerprint=useLocalStorage('fingerprint','')
|
||||
@ -26,7 +26,11 @@ export const codeAuthStore = createGlobalState(() => {
|
||||
currency:''
|
||||
})
|
||||
const qrData=useLocalStorage('qrData',{})
|
||||
const codePKey=useLocalStorage('codePKey','')
|
||||
const codePayUid=useLocalStorage('codePayUid','')
|
||||
return{
|
||||
codePKey,
|
||||
codePayUid,
|
||||
qrData,
|
||||
qrUid,
|
||||
cpayment,
|
||||
@ -37,7 +41,7 @@ export const codeAuthStore = createGlobalState(() => {
|
||||
formData,
|
||||
userInfo,
|
||||
RefreshToken,
|
||||
token,
|
||||
codeToken,
|
||||
fingerprint
|
||||
}
|
||||
})
|
@ -1,4 +1,7 @@
|
||||
import { createGlobalState,useLocalStorage } from '@vueuse/core'
|
||||
import { WebSocketClient } from '@/utils/websocket'
|
||||
import {message} from "~/components/x-message/useMessage.js";
|
||||
|
||||
export const authStore = createGlobalState(() => {
|
||||
const token=useLocalStorage('token','')
|
||||
const RefreshToken=useLocalStorage('RefreshToken','')
|
||||
|
@ -34,8 +34,7 @@ export const goodStore = createGlobalState(() => {
|
||||
auctionDetail.value = res.data
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取拍卖详情错误:', err)
|
||||
} finally {
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
@ -69,7 +68,6 @@ export const goodStore = createGlobalState(() => {
|
||||
}
|
||||
return { finished: true, items: [] }
|
||||
} catch (err) {
|
||||
console.error('获取艺术品列表错误:', err)
|
||||
return { finished: true, items: [] }
|
||||
} finally {
|
||||
loading.value = false
|
||||
@ -85,8 +83,7 @@ export const goodStore = createGlobalState(() => {
|
||||
artWorkDetail.value = res.data
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取艺术品详情错误:', err)
|
||||
} finally {
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
@ -197,12 +197,10 @@ export const liveStore = createGlobalState(() => {
|
||||
|
||||
// WebSocket 事件处理
|
||||
ws.onOpen(() => {
|
||||
console.log('WebSocket connected')
|
||||
})
|
||||
})
|
||||
|
||||
ws.onMessage((data) => {
|
||||
auctionData.value = data.data
|
||||
console.log(' auctionData.value', auctionData.value)
|
||||
const { wsType, tip } = data.data || {}
|
||||
|
||||
switch (wsType) {
|
||||
@ -210,13 +208,11 @@ export const liveStore = createGlobalState(() => {
|
||||
handleTipMessage(tip?.tipType)
|
||||
break
|
||||
case WS_TYPES.STOP_ARTWORK:
|
||||
console.log('changeQuote',quoteStatus.value)
|
||||
//quoteStatus.value = false
|
||||
break
|
||||
case WS_TYPES.OVER:
|
||||
|
||||
quoteStatus.value = false
|
||||
console.log('changeQuote',quoteStatus.value)
|
||||
message.success(createMessageConfig(
|
||||
t('live_room.text10'),
|
||||
'#575757',
|
||||
@ -230,16 +226,13 @@ export const liveStore = createGlobalState(() => {
|
||||
break
|
||||
}
|
||||
|
||||
console.log('onmessage', data)
|
||||
})
|
||||
})
|
||||
|
||||
ws.onClose(() => {
|
||||
console.log('WebSocket disconnected')
|
||||
})
|
||||
})
|
||||
|
||||
ws.onError((error) => {
|
||||
console.error('WebSocket error:', error)
|
||||
})
|
||||
})
|
||||
}
|
||||
const changeStatus = () => {
|
||||
if (auctionData.value.artwork?.isSelling&&!auctionData.value.artwork.isSoled){
|
||||
@ -249,8 +242,7 @@ export const liveStore = createGlobalState(() => {
|
||||
quoteStatus.value = false
|
||||
}
|
||||
}
|
||||
console.log('changeQuote',quoteStatus.value)
|
||||
}
|
||||
}
|
||||
return{
|
||||
fullLive,
|
||||
isMinWindow,
|
||||
|
@ -423,7 +423,7 @@
|
||||
"fullPayment": "Pay in Full",
|
||||
"partialPayment": "Partial Payment",
|
||||
"confirmPayment": "Confirm Payment",
|
||||
"maxAmount": "Maximum {currency}{price}",
|
||||
"maxAmount": "most",
|
||||
"enterAmount": "Please enter amount",
|
||||
"exceedTotal": "Cannot exceed total amount"
|
||||
},
|
||||
|
@ -423,7 +423,7 @@
|
||||
"fullPayment": "全額支払い",
|
||||
"partialPayment": "一部支払い",
|
||||
"confirmPayment": "支払い確認",
|
||||
"maxAmount": "最大 {currency}{price}",
|
||||
"maxAmount": "ほとんど",
|
||||
"enterAmount": "金額を入力してください",
|
||||
"exceedTotal": "合計金額を超えることはできません"
|
||||
},
|
||||
|
@ -426,7 +426,8 @@
|
||||
"confirmPayment": "确认支付",
|
||||
"text1": "最多",
|
||||
"enterAmount": "请输入金额",
|
||||
"exceedTotal": "不得高于全部金额"
|
||||
"exceedTotal": "不得高于全部金额",
|
||||
"maxAmount": "最多"
|
||||
},
|
||||
"signature": {
|
||||
"resultText": "领取您的专属号牌",
|
||||
|
@ -423,7 +423,7 @@
|
||||
"fullPayment": "支付全部",
|
||||
"partialPayment": "支付部分",
|
||||
"confirmPayment": "確認支付",
|
||||
"maxAmount": "最多{currency}{price}",
|
||||
"maxAmount": "最多",
|
||||
"enterAmount": "請輸入金額",
|
||||
"exceedTotal": "不得高於全部金額"
|
||||
},
|
||||
|
@ -1,6 +1,8 @@
|
||||
import dotenv from 'dotenv'
|
||||
import process from 'node:process'
|
||||
import { currentLocales } from './i18n/i18n'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
const envFile = process.env.ENV_FILE || '.env.test'
|
||||
dotenv.config({ path: `./env/${envFile}` })
|
||||
const publicConfig = Object.entries(process.env)
|
||||
@ -10,6 +12,19 @@ const publicConfig = Object.entries(process.env)
|
||||
return config
|
||||
}, {})
|
||||
|
||||
let httpsOptions = {}
|
||||
|
||||
try {
|
||||
// 读取文件并转换为字符串
|
||||
const key = fs.readFileSync(path.resolve(__dirname, 'ssl/localhost-key.pem'), 'utf-8')
|
||||
const cert = fs.readFileSync(path.resolve(__dirname, 'ssl/localhost.pem'), 'utf-8')
|
||||
|
||||
httpsOptions = { key, cert }
|
||||
} catch (error) {
|
||||
// 失败时使用HTTP
|
||||
httpsOptions = false
|
||||
}
|
||||
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
'@vant/nuxt',
|
||||
@ -136,7 +151,8 @@ export default defineNuxtConfig({
|
||||
// 指定 Nuxt 应用程序的兼容性日期,确保应用程序在未来的 Nuxt 版本中保持稳定性
|
||||
compatibilityDate: '2025-02-28',
|
||||
devServer: {
|
||||
host: '0.0.0.0', // Set the host to 'localhost'
|
||||
port: 3000, // Set the port to 3000 or any other port you prefer
|
||||
// https: httpsOptions,
|
||||
host: '0.0.0.0',
|
||||
port: 3000,
|
||||
},
|
||||
})
|
25
server/middleware/stripe.ts
Normal file
25
server/middleware/stripe.ts
Normal file
@ -0,0 +1,25 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const url = getRequestURL(event)
|
||||
|
||||
// 只处理 create-payment-intent 请求
|
||||
if (url.pathname === '/create-payment-intent' && event.method === 'POST') {
|
||||
try {
|
||||
const body = await readBody(event)
|
||||
const { items } = body
|
||||
|
||||
// 计算总金额
|
||||
const amount = items.reduce((total: number, item: any) => total + item.amount, 0)
|
||||
|
||||
// 模拟创建支付意向的响应
|
||||
// 注意:clientSecret 格式应该类似于 'pi_xxxxx_secret_xxxxx'
|
||||
return {
|
||||
clientSecret: `pi_${Math.random().toString(36).substring(2)}_secret_${Math.random().toString(36).substring(2)}`
|
||||
}
|
||||
} catch (error) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: '创建支付意向失败'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
28
ssl/localhost-key.pem
Normal file
28
ssl/localhost-key.pem
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFtE2AWs1o0X/A
|
||||
0+506WAG49xbO3KJQTWKwHl5pfeP0GieVjV3qKXkHXl9Fyhg9oJnCAz2nzxzavAP
|
||||
W9NevDbX7KdEiZQjDhpgO+V9IGUqqJKEQnpVk57G3fHBX40nQ4zAQ/S1Vj4Aw9Tk
|
||||
iqNFgLRX3HTAT0NO3eXENJbsDdGxtOuLn5h+DWThrqCaUK2Xnkee3eG/VwgKJKVH
|
||||
LWuC8xyb1NydtchaRFBjoO9oT6Tb/MGeBOrNH9e7Ndh9mCFepXUhzNxrN1fKjn9s
|
||||
107pSEeZb4il2K7GqE0W6r6Y3eeBR2+mEBRqeFnZObT0JkBLI2QIrAwOnFO6ObQC
|
||||
ZaEjwHcZAgMBAAECggEASMZB8Ql7qyXS3OwmTqrJSj/+ESck1hlG2DhZfsn1At84
|
||||
Y3BgZheSWRHwcndfybFz9vEjtHSRD/tBOqYWfDzUA099kuEBwpWiZ+Ika5bNJpK+
|
||||
vCisV2vrelCgeQnvL5DR8sQRA98nG6j6aNYPm7nwqJbh8xg6MoHD3iFtnJ7JnZvQ
|
||||
pSa6Z9qq4Po+cp63/U3yEzFeiVVDTMQJMVClANUCX3jLHs8B85WMbb1eKKFe/xCA
|
||||
n2BWlFVI7Hld+hxhKWkc71+kafC5hUz1w88FcBaN2W/DAtJgKC0dHYATwLCUGC4Z
|
||||
CoCZfB7b3JOzK4mGJ/XxxUcBUk+oweExOrYwCfW4MQKBgQDJbTM4qgW2ca/Xc7cp
|
||||
zKvclgtkJ8rWbVZqMYW6fpXoOdhhxjJSx+LfeGk1whz3Xj04EdSZbRI6zYVaHfHa
|
||||
HFkA2Na/Wi9hboid4WVXUi5RXzthVTOYi1jAJNmK4R25wuSdcGVoYxSrSLyDcHbx
|
||||
MF2cFdQ4A386L+RcoDYzImWCXwKBgQD7RO4DZJgkqDng6YFrYqIdQkJn6keXl4yW
|
||||
Hq1FGaa7XDjlun/X2jT0xJJPFcwLQLbWwrkwmUYN/VQEbYwYlyB0MegF9VflfILl
|
||||
/leCXC8/9WEknQkPqu8N1JiYhajIKLxfQX35nW/oK+S5prJOBxNw+3Of/S43R5Xe
|
||||
60EEI9iphwKBgQCrs/Sn5vd7sKnOpYuLjDcskJMhS3JzGz1AxPpUIbgz/6tenY8k
|
||||
VdQl3wUAmHoMvD6/XyO1re6ORcfZLBGQdf3A5RcagwxEp+65dvvmVd256844iGK1
|
||||
NIPxNvhilMe8JFCxjLBFLcDeyeA4w1QBAdOqTEldfk2kElM+SiwppraVTQKBgCUP
|
||||
O5OgiJgPf8neZsox1/s8xJKTCVAgeAnEKIYijGbh6Tpo0WZCtsDLJVEow9l9B/qQ
|
||||
6cNzN9PkYznr9lfCInVAzxnh377nKF9Hrhx6ADYMuPEvgCChc3S0wHTuccBj0bSy
|
||||
8iOYxuKVZrzDC1Va0dE+JQWZz/EzS7V/OS2lI9WNAoGBAKujoPDn36/hJ/Zr8XAM
|
||||
CEbOi0q0N7I37aRKO8Wm55SCGDYWtBlu+NiIMqk3gzgomtm/cVF+fUNv0BOKc+hx
|
||||
x6PQE98AEn5LdGeqLpDY66vhyR8WGUyCBPB+Dn8OFFT+njL2E8NcQi0kS3t/YlR/
|
||||
oobyxGhm4M1fM8HtGwqQJX60
|
||||
-----END PRIVATE KEY-----
|
26
ssl/localhost.pem
Normal file
26
ssl/localhost.pem
Normal file
@ -0,0 +1,26 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEbjCCAtagAwIBAgIRAJjJaJNy+AO5JPuasTslJMQwDQYJKoZIhvcNAQELBQAw
|
||||
gZ8xHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE6MDgGA1UECwwxREVT
|
||||
S1RPUC1DOTVCMVIzXDM3MzYzQERFU0tUT1AtQzk1QjFSMyAo6YKi6Zuo5p2oKTFB
|
||||
MD8GA1UEAww4bWtjZXJ0IERFU0tUT1AtQzk1QjFSM1wzNzM2M0BERVNLVE9QLUM5
|
||||
NUIxUjMgKOmCoumbqOadqCkwHhcNMjUwMzAyMDIzMzM3WhcNMjcwNjAyMDIzMzM3
|
||||
WjBlMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxOjA4
|
||||
BgNVBAsMMURFU0tUT1AtQzk1QjFSM1wzNzM2M0BERVNLVE9QLUM5NUIxUjMgKOmC
|
||||
oumbqOadqCkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFtE2AWs1o
|
||||
0X/A0+506WAG49xbO3KJQTWKwHl5pfeP0GieVjV3qKXkHXl9Fyhg9oJnCAz2nzxz
|
||||
avAPW9NevDbX7KdEiZQjDhpgO+V9IGUqqJKEQnpVk57G3fHBX40nQ4zAQ/S1Vj4A
|
||||
w9TkiqNFgLRX3HTAT0NO3eXENJbsDdGxtOuLn5h+DWThrqCaUK2Xnkee3eG/VwgK
|
||||
JKVHLWuC8xyb1NydtchaRFBjoO9oT6Tb/MGeBOrNH9e7Ndh9mCFepXUhzNxrN1fK
|
||||
jn9s107pSEeZb4il2K7GqE0W6r6Y3eeBR2+mEBRqeFnZObT0JkBLI2QIrAwOnFO6
|
||||
ObQCZaEjwHcZAgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
|
||||
BgEFBQcDATAfBgNVHSMEGDAWgBQVJpbTujNcXUH/91CnxLerp/gKbDAUBgNVHREE
|
||||
DTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggGBAERAae9YDQgjnVtDQUWL
|
||||
kIbMowvN6BospgO2srV+aXCDLbB22jnq4cGsTpVjxo80Nl6M0iSRz29K+jy4YFsL
|
||||
efTOeks1EpVQB/UnYuo391p5wzevXwa3s7dH7Oc+917y8JDiLNnSVEct+tk4zeOZ
|
||||
QbVzx6Gexiii7k1uSG/G1NYrRiXf3ggM93Fyu5NM+u8CzZvWm46ix9reYimVqfPa
|
||||
VjHsiQnmKbh+CD6iDWm9y1jUxqBay4cAbo2AVxIvBDdsC9KSCTsbP4hBPx9foy1U
|
||||
cLRxUGsWTVPPS2BmP8o6CSa2tNPeVNCWSP89tanY2mzGErfVXLV8t5E4awF0ea+a
|
||||
kbjyG3svVC6/rLo8LpFPonr4mQWfGcFntmGUC314d5z1ZCCS5ENEWAGZ3b3XzPsU
|
||||
Yh2QQnt4gtvWaTRqwqhSL9DLFp106/tok3hq8MyDFcxTxWKyDZsgaieoRGnF11EW
|
||||
tdIqnK9nwVOyAzaO603SuEoMiGBpb9nj/cAFsvm56YUVrg==
|
||||
-----END CERTIFICATE-----
|
Loading…
Reference in New Issue
Block a user