876 lines
22 KiB
Vue
876 lines
22 KiB
Vue
<template>
|
||
<BaseDialog v-model="visible" width="1000px" class="payment-base-dialog">
|
||
<div class="recharge-dialog">
|
||
<!-- 头部用户信息 -->
|
||
<div class="user-header">
|
||
<el-avatar :size="45"
|
||
:src="userInfo.avater || userInfo.avatar || userInfo.avatar_url || userStore.avatarUrl || undefined">
|
||
{{ (userInfo.name || userInfo.username || userStore.username || 'U').charAt(0).toUpperCase() }}
|
||
</el-avatar>
|
||
<div class="user-meta">
|
||
<span class="username">{{ userInfo.name || userInfo.username || userStore.username || '用户' }}</span>
|
||
<span class="vip-status" :class="{ expired: !isVip }">
|
||
{{ isVip ? `会员 ${userInfo.membership_expire_at}到期` : '未开通会员' }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="dialog-body">
|
||
<!-- 左侧面板 -->
|
||
<div class="left-panel">
|
||
|
||
<div v-if="!loading && productList.length" class="plans-scroll" :class="{ 'is-disabled': paying }"
|
||
ref="scrollContainer" @mousedown="onMouseDown" @mouseleave="onMouseLeave" @mouseup="onMouseUp"
|
||
@mousemove="onMouseMove">
|
||
<div v-for="(item, idx) in productList" :key="item.goods_id" class="plan-card"
|
||
:class="{ active: selectedProductId === item.goods_id }" @click="handleProductChange(item.goods_id)">
|
||
<div class="plan-name">{{ item.goods_name }}</div>
|
||
<div class="plan-original">¥{{ item.origin_price }}</div>
|
||
<div class="plan-price"><span class="currency">¥</span>{{ item.price }}</div>
|
||
</div>
|
||
</div>
|
||
<div v-else-if="loading" class="loading-wrap">
|
||
<el-icon class="is-loading">
|
||
<Loading />
|
||
</el-icon>
|
||
<span>加载商品...</span>
|
||
</div>
|
||
<!-- 手写切换加载蒙层 -->
|
||
<transition name="fade">
|
||
<div v-if="switchingProduct" class="custom-loading-mask">
|
||
<el-icon class="is-loading">
|
||
<Loading />
|
||
</el-icon>
|
||
<span>切换中...</span>
|
||
</div>
|
||
</transition>
|
||
|
||
<!-- 会员权益 -->
|
||
<div class="privilege-section">
|
||
<div class="privilege-title">
|
||
<img style="width: 171px;height: 18px;" src="@/public/rechargeDialog/huiyuanquanyi.png" alt="">
|
||
</div>
|
||
<div class="privilege-grid">
|
||
<div class="scroll-row">
|
||
<div class="scroll-content right-to-left">
|
||
<div class="scroll-group">
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon1.png" alt=""> 无水印高清下载</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon2.png" alt=""> 模拟微信</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon3.png" alt=""> 工资单</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon4.png" alt=""> 证件模拟</div>
|
||
</div>
|
||
<div class="scroll-group">
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon1.png" alt=""> 无水印高清下载</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon2.png" alt=""> 模拟微信</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon3.png" alt=""> 工资单</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon4.png" alt=""> 证件模拟</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="scroll-row">
|
||
<div class="scroll-content left-to-right">
|
||
<div class="scroll-group">
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon5.png" alt=""> 模拟飞机票</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon6.png" alt=""> 模拟高铁票</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon7.png" alt=""> 模拟购物订单</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon8.png" alt=""> 我的零钱</div>
|
||
</div>
|
||
<div class="scroll-group">
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon5.png" alt=""> 模拟飞机票</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon6.png" alt=""> 模拟高铁票</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon7.png" alt=""> 模拟购物订单</div>
|
||
<div class="p-item"><img class="icon" src="@/public/rechargeDialog/icon8.png" alt=""> 我的零钱</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧毛玻璃面板 -->
|
||
<div class="right-panel">
|
||
<div class="price-display">
|
||
<span class="currency">¥</span>
|
||
<span class="price-num">{{ Number(currentPrice).toFixed(2) || '0.00' }}</span>
|
||
</div>
|
||
<div class="discount-tag" v-if="currentOriginalPrice">
|
||
已优惠 ¥{{ (currentOriginalPrice - currentPrice) }}
|
||
</div>
|
||
|
||
<div class="qr-box">
|
||
<img v-if="qrCode" :src="qrCode" alt="支付二维码" class="qr-img" />
|
||
<div v-else-if="paying || loading" class="qr-placeholder loading">
|
||
<el-icon class="is-loading">
|
||
<Loading />
|
||
</el-icon>
|
||
<span>生成支付码...</span>
|
||
</div>
|
||
<div v-else class="qr-placeholder error" @click="handlePay">
|
||
<el-icon>
|
||
<RefreshRight />
|
||
</el-icon>
|
||
<span>获取失败,点击重试</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="pay-methods">
|
||
<div class="method-btn" :class="{ active: payMethod === 'weixin' }"
|
||
@click="handlePayMethodChange('weixin')">
|
||
<img class="icon" src="@/public/rechargeDialog/wxpay.png" alt="微信支付">
|
||
微信支付
|
||
</div>
|
||
<div class="method-btn alipay" :class="{ active: payMethod === 'alipay' }"
|
||
@click="handlePayMethodChange('alipay')">
|
||
<img class="icon" src="@/public/rechargeDialog/alipay.png" alt="支付宝">
|
||
支付宝支付
|
||
</div>
|
||
</div>
|
||
|
||
<div class="footer-tips">
|
||
<p>支付即视为同意《用户协议》</p>
|
||
<p>如遇到支付问题在线反馈</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</BaseDialog>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, watch, onUnmounted } from 'vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import BaseDialog from '@/components/common/BaseDialog.vue'
|
||
import { Loading, RefreshRight, Download, ChatDotRound, Tickets, Postcard, Position, Van, ShoppingBag, Wallet } from '@element-plus/icons-vue'
|
||
import authService from '@/api/authService.js'
|
||
import { useUserStore } from '@/stores/user'
|
||
|
||
const emit = defineEmits(['success'])
|
||
const userStore = useUserStore()
|
||
|
||
const visible = ref(false)
|
||
const selectedProductId = ref(null)
|
||
const paying = ref(false)
|
||
const switchingProduct = ref(false)
|
||
const qrCode = ref('')
|
||
const loading = ref(false)
|
||
const productList = ref([])
|
||
const payMethod = ref('weixin')
|
||
let pollTimer = null
|
||
let orderVersion = 0
|
||
let activeOrderNo = ''
|
||
|
||
function stopPolling() {
|
||
if (pollTimer) {
|
||
clearInterval(pollTimer)
|
||
pollTimer = null
|
||
}
|
||
}
|
||
|
||
// 拖拽滚动相关状态
|
||
const scrollContainer = ref(null)
|
||
const isDragging = ref(false)
|
||
const startX = ref(0)
|
||
const scrollLeft = ref(0)
|
||
let hasDragged = false
|
||
|
||
const userInfo = computed(() => ({
|
||
username: userStore.username,
|
||
avatar_url: userStore.avatarUrl || '',
|
||
membership_expire_at: userStore.membershipExpireAt || null,
|
||
}))
|
||
const isVip = computed(() => userStore.isVip)
|
||
|
||
const currentPrice = computed(() => {
|
||
const p = productList.value.find(i => i.goods_id === selectedProductId.value)
|
||
return p ? p.price : ''
|
||
})
|
||
const currentOriginalPrice = computed(() => {
|
||
const p = productList.value.find(i => i.goods_id === selectedProductId.value)
|
||
return p ? (p.origin_price || '') : ''
|
||
})
|
||
|
||
function open() {
|
||
stopPolling()
|
||
orderVersion++
|
||
activeOrderNo = ''
|
||
visible.value = true
|
||
selectedProductId.value = null
|
||
qrCode.value = ''
|
||
paying.value = false
|
||
switchingProduct.value = false
|
||
loadProducts()
|
||
}
|
||
|
||
async function loadProducts() {
|
||
loading.value = true
|
||
try {
|
||
const res = await authService.getGoods()
|
||
console.log('商品列表', res)
|
||
productList.value = Array.isArray(res.data) ? res.data : []
|
||
|
||
if (productList.value.length > 0) {
|
||
// 默认选中第一个商品
|
||
selectedProductId.value = productList.value[0].goods_id
|
||
} else {
|
||
ElMessage.warning('暂无可售商品')
|
||
}
|
||
} catch (e) {
|
||
ElMessage.error(e?.response?.data?.error || '加载商品失败')
|
||
productList.value = []
|
||
} finally {
|
||
// 优先关闭列表 Loading,让商品卡片先行渲染展示
|
||
loading.value = false
|
||
}
|
||
|
||
// 列表渲染展示后,再触发支付二维码的异步获取
|
||
if (selectedProductId.value) {
|
||
handlePay()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 智能格式化支付二维码图片地址
|
||
* 解决微信支付二维码加载不出来的常见原因:
|
||
* 1. 微信原生支付协议 (weixin://) 无法直接用 <img> 渲染,需要转换为二维码图片 URL;
|
||
* 2. 纯 Base64 字符串缺失 data:image/png;base64, 前缀;
|
||
* 3. 兼容 res.data.qrcodeBase64、res.data.qrcode 等不同字段。
|
||
*/
|
||
function getQrCodeUrl(resData) {
|
||
if (!resData) return ''
|
||
const dataObj = resData.data || resData
|
||
|
||
// 提取可能的字段
|
||
const raw = dataObj.qrcodeBase64 || dataObj.qrcode_base64 || dataObj.qrcode || dataObj.payParam || (typeof dataObj === 'string' ? dataObj : '')
|
||
|
||
if (!raw) return ''
|
||
|
||
// 1. 已经是完整的 Data URI 或 HTTP(S) 图片链接
|
||
if (raw.startsWith('data:image/') || raw.startsWith('http://') || raw.startsWith('https://')) {
|
||
return raw
|
||
}
|
||
|
||
// 2. 纯 Base64 字符串(补齐前缀)
|
||
if (!raw.startsWith('weixin://') && !raw.startsWith('alipays://') && raw.length > 100) {
|
||
return `data:image/png;base64,${raw.replace(/\s/g, '')}`
|
||
}
|
||
|
||
// 3. 微信协议链接 (weixin://wxpay/...) 或网页链接,动态转换为可渲染的二维码图片
|
||
if (raw.startsWith('weixin://') || raw.startsWith('alipays://') || raw.includes('://')) {
|
||
return `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(raw)}`
|
||
}
|
||
|
||
return raw
|
||
}
|
||
|
||
async function handlePay() {
|
||
if (!selectedProductId.value || paying.value) return
|
||
|
||
stopPolling()
|
||
orderVersion++
|
||
const currentVersion = orderVersion
|
||
|
||
paying.value = true
|
||
qrCode.value = ''
|
||
activeOrderNo = ''
|
||
|
||
const data = {
|
||
"goods_id": selectedProductId.value, // 从商品列表获取
|
||
"pay_type": payMethod.value, // 支付方式,weixin/alipay/apple/bank/balance
|
||
"pay_source": "web", // 默认app,支持app、jsapi(小程序)、web、h5
|
||
"source": "web", // 下单商品位置,业务自定义
|
||
"extra": {
|
||
qrCodeImgUrl: "true",
|
||
}
|
||
}
|
||
try {
|
||
const res = await authService.createOrder(data)
|
||
console.log('创建订单响应结果:', res)
|
||
|
||
// 版本锁校验:若当前请求版本已被后续操作覆盖,则丢弃过时的响应
|
||
if (currentVersion !== orderVersion) {
|
||
console.warn('丢弃已失效过时的订单响应')
|
||
return
|
||
}
|
||
|
||
const orderData = res.data || res
|
||
|
||
if (payMethod.value === 'alipay') {
|
||
qrCode.value = orderData.payParam || ''
|
||
} else {
|
||
// 使用智能格式化获取可供 <img> 渲染的二维码图片地址
|
||
qrCode.value = getQrCodeUrl(orderData)
|
||
}
|
||
|
||
const orderNo = res.order_no || orderData.order_no || orderData.orderId || orderData.order_id
|
||
if (orderNo) {
|
||
activeOrderNo = orderNo
|
||
startPolling(orderNo, currentVersion)
|
||
}
|
||
} catch (e) {
|
||
if (currentVersion === orderVersion) {
|
||
qrCode.value = ''
|
||
ElMessage.error(e?.response?.data?.error || '创建订单失败,请点击重试')
|
||
}
|
||
} finally {
|
||
if (currentVersion === orderVersion) {
|
||
paying.value = false
|
||
switchingProduct.value = false
|
||
}
|
||
}
|
||
}
|
||
|
||
function handlePayMethodChange(method) {
|
||
if (paying.value || payMethod.value === method) return
|
||
payMethod.value = method
|
||
handlePay()
|
||
}
|
||
|
||
function handleProductChange(id) {
|
||
if (hasDragged || paying.value || selectedProductId.value === id) return
|
||
selectedProductId.value = id
|
||
switchingProduct.value = true
|
||
handlePay()
|
||
}
|
||
|
||
// 拖拽滚动逻辑
|
||
function onMouseDown(e) {
|
||
if (!scrollContainer.value) return
|
||
isDragging.value = true
|
||
hasDragged = false
|
||
scrollContainer.value.style.cursor = 'grabbing'
|
||
startX.value = e.pageX - scrollContainer.value.offsetLeft
|
||
scrollLeft.value = scrollContainer.value.scrollLeft
|
||
}
|
||
|
||
function onMouseLeave() {
|
||
isDragging.value = false
|
||
if (scrollContainer.value) scrollContainer.value.style.cursor = 'grab'
|
||
}
|
||
|
||
function onMouseUp() {
|
||
isDragging.value = false
|
||
if (scrollContainer.value) scrollContainer.value.style.cursor = 'grab'
|
||
}
|
||
|
||
function onMouseMove(e) {
|
||
if (!isDragging.value || !scrollContainer.value) return
|
||
e.preventDefault()
|
||
const x = e.pageX - scrollContainer.value.offsetLeft
|
||
const walk = (x - startX.value) * 1.5 // 滚动速度倍率
|
||
if (Math.abs(walk) > 5) {
|
||
hasDragged = true
|
||
}
|
||
scrollContainer.value.scrollLeft = scrollLeft.value - walk
|
||
}
|
||
|
||
function startPolling(orderNo, currentVersion) {
|
||
stopPolling()
|
||
let count = 0
|
||
pollTimer = setInterval(async () => {
|
||
// 双重失效保护:若版本变动或关联的订单号不匹配,强行关闭轮询
|
||
if (currentVersion !== orderVersion || orderNo !== activeOrderNo) {
|
||
stopPolling()
|
||
return
|
||
}
|
||
count++
|
||
try {
|
||
const res = await authService.getOrder({ order_id: orderNo })
|
||
console.log('轮询查询订单响应结果:', res, '订单号', orderNo)
|
||
if (res.code == 0 && res?.data?.status ==
|
||
2) {
|
||
stopPolling()
|
||
ElMessage.success('支付成功,会员已开通')
|
||
setTimeout(() => {
|
||
visible.value = false
|
||
emit('success')
|
||
}, 1500)
|
||
}
|
||
} catch (e) {
|
||
console.error('轮询查询订单失败:', e)
|
||
}
|
||
if (count > 120) {
|
||
stopPolling()
|
||
ElMessage.warning('轮询超时,请刷新页面查看')
|
||
}
|
||
}, 3000)
|
||
}
|
||
|
||
watch(visible, (val) => {
|
||
if (!val) {
|
||
stopPolling()
|
||
orderVersion++
|
||
activeOrderNo = ''
|
||
}
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
stopPolling()
|
||
})
|
||
|
||
defineExpose({ open })
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.recharge-dialog {
|
||
width: 1000px;
|
||
height: 704px;
|
||
background-image: url('/src/public/rechargeDialog/bg.png');
|
||
background-size: cover;
|
||
background-position: center;
|
||
background-repeat: no-repeat;
|
||
display: flex;
|
||
flex-direction: column;
|
||
position: relative;
|
||
}
|
||
|
||
.user-header {
|
||
position: absolute;
|
||
top: 124px;
|
||
left: 48px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
color: #fff;
|
||
|
||
.username {
|
||
font-weight: 500;
|
||
font-size: 18px;
|
||
color: #FFFFFF;
|
||
line-height: 18px;
|
||
}
|
||
|
||
.vip-status {
|
||
font-size: 12px;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.user-meta {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
|
||
.vip-status {
|
||
margin-top: 8px;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
color: #FFFFFF;
|
||
line-height: 14px;
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
.dialog-body {
|
||
margin-top: 187px;
|
||
padding: 0 24px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 24px;
|
||
}
|
||
|
||
.left-panel {
|
||
position: relative;
|
||
flex-shrink: 0;
|
||
width: 677px;
|
||
height: 493px;
|
||
padding: 44px 46px;
|
||
box-sizing: border-box;
|
||
|
||
.custom-loading-mask {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(245, 245, 255, 0.75);
|
||
backdrop-filter: blur(2px);
|
||
border-radius: 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
z-index: 10;
|
||
color: #8B70FF;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
pointer-events: auto;
|
||
|
||
.el-icon {
|
||
font-size: 24px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.plans-scroll {
|
||
position: relative;
|
||
display: flex;
|
||
gap: 16px;
|
||
overflow-x: auto;
|
||
padding-bottom: 2px;
|
||
cursor: grab;
|
||
user-select: none;
|
||
border-radius: 16px;
|
||
|
||
&.is-disabled {
|
||
pointer-events: none;
|
||
}
|
||
|
||
|
||
|
||
&::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
.plan-card {
|
||
flex: 0 0 160px;
|
||
height: 210px;
|
||
background: #F5F5FF;
|
||
border-radius: 16px 16px 16px 16px;
|
||
border: 2px solid #BFC8F8;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
padding-top: 30px;
|
||
transition: all 0.3s;
|
||
|
||
.plan-name {
|
||
font-size: 24px;
|
||
color: #26106B;
|
||
line-height: 21px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.plan-original {
|
||
width: 100%;
|
||
padding-left: 33px;
|
||
font-size: 20px;
|
||
color: #26106B;
|
||
line-height: 20px;
|
||
text-decoration: line-through;
|
||
margin-top: 6px;
|
||
margin-bottom: 50px;
|
||
}
|
||
|
||
.plan-price {
|
||
font-size: 55px;
|
||
color: #26106B;
|
||
line-height: 52px;
|
||
font-weight: 500;
|
||
|
||
.currency {
|
||
font-size: 26px;
|
||
color: #26106B;
|
||
line-height: 26px;
|
||
font-weight: 700;
|
||
}
|
||
}
|
||
|
||
&.active {
|
||
background: url('/src/public/rechargeDialog/active-product-bg.png');
|
||
background-size: cover;
|
||
background-position: center;
|
||
background-repeat: no-repeat;
|
||
position: relative;
|
||
background-clip: padding-box;
|
||
border: none;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -2px;
|
||
right: -2px;
|
||
bottom: -2px;
|
||
left: -2px;
|
||
z-index: -1;
|
||
border-radius: 14px;
|
||
background: linear-gradient(135deg, #B17DFF, #50E5FF);
|
||
}
|
||
}
|
||
}
|
||
|
||
.loading-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
height: 220px;
|
||
color: #999;
|
||
}
|
||
|
||
.privilege-section {
|
||
margin-top: 38px;
|
||
|
||
.privilege-title {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16px;
|
||
margin-bottom: 34px;
|
||
|
||
.text {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #8B70FF;
|
||
}
|
||
|
||
.line {
|
||
width: 40px;
|
||
height: 1px;
|
||
background: #E0E0E0;
|
||
}
|
||
}
|
||
|
||
.privilege-grid {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 14px;
|
||
overflow: hidden;
|
||
width: 100%;
|
||
|
||
.scroll-row {
|
||
width: 100%;
|
||
overflow: hidden;
|
||
display: flex;
|
||
}
|
||
|
||
.scroll-content {
|
||
display: flex;
|
||
width: max-content;
|
||
|
||
&.right-to-left {
|
||
animation: scroll-rl 15s linear infinite;
|
||
}
|
||
|
||
&.left-to-right {
|
||
transform: translateX(-50%);
|
||
animation: scroll-lr 15s linear infinite;
|
||
}
|
||
|
||
&:hover {
|
||
animation-play-state: paused;
|
||
}
|
||
}
|
||
|
||
.scroll-group {
|
||
display: flex;
|
||
gap: 10px;
|
||
padding-right: 10px;
|
||
}
|
||
|
||
.p-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
color: #1A1A1A;
|
||
line-height: 14px;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
width: 145px;
|
||
|
||
.icon {
|
||
width: 46px;
|
||
height: 46px;
|
||
}
|
||
|
||
.el-icon {
|
||
font-size: 20px;
|
||
color: #8B70FF;
|
||
}
|
||
}
|
||
}
|
||
|
||
@keyframes scroll-rl {
|
||
0% {
|
||
transform: translateX(0);
|
||
}
|
||
|
||
100% {
|
||
transform: translateX(-50%);
|
||
}
|
||
}
|
||
|
||
@keyframes scroll-lr {
|
||
0% {
|
||
transform: translateX(-50%);
|
||
}
|
||
|
||
100% {
|
||
transform: translateX(0);
|
||
}
|
||
}
|
||
}
|
||
|
||
.right-panel {
|
||
width: 251px;
|
||
height: 493px;
|
||
border-radius: 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 53px 16px 22px;
|
||
box-sizing: border-box;
|
||
|
||
.price-display {
|
||
color: #fff;
|
||
|
||
.currency {
|
||
font-size: 32px;
|
||
color: #FFFFFF;
|
||
line-height: 48px;
|
||
margin-right: 2px;
|
||
}
|
||
|
||
.price-num {
|
||
font-size: 52px;
|
||
color: #FFFFFF;
|
||
line-height: 48px;
|
||
}
|
||
}
|
||
|
||
.discount-tag {
|
||
padding-inline: 20px;
|
||
min-width: 120px;
|
||
height: 44px;
|
||
background: url('/src/public/rechargeDialog/discount-tag-bg.png');
|
||
background-size: 100% 100%;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
color: #fff;
|
||
padding: 0 12px;
|
||
border-radius: 20px;
|
||
margin-bottom: 28px;
|
||
font-weight: 700;
|
||
font-size: 18px;
|
||
line-height: 48px;
|
||
color: #FFFFFF;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.qr-box {
|
||
width: 158px;
|
||
height: 158px;
|
||
background: #fff;
|
||
border-radius: 10px;
|
||
padding: 15px;
|
||
margin-bottom: 20px;
|
||
box-sizing: border-box;
|
||
|
||
.qr-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.qr-placeholder {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
color: #999;
|
||
font-size: 12px;
|
||
background: #f5f5f5;
|
||
border-radius: 10px;
|
||
padding: 8px;
|
||
box-sizing: border-box;
|
||
text-align: center;
|
||
|
||
&.error {
|
||
cursor: pointer;
|
||
color: #ff4d4f;
|
||
transition: background-color 0.2s;
|
||
|
||
&:hover {
|
||
background-color: #ffeef0;
|
||
}
|
||
|
||
.el-icon {
|
||
font-size: 20px;
|
||
}
|
||
}
|
||
|
||
&.loading {
|
||
cursor: wait;
|
||
color: #8B70FF;
|
||
|
||
.el-icon {
|
||
font-size: 22px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.pay-methods {
|
||
display: flex;
|
||
gap: 12px;
|
||
margin-bottom: auto;
|
||
|
||
.method-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-weight: 700;
|
||
font-size: 14px;
|
||
color: #FFFFFF;
|
||
line-height: 14px;
|
||
white-space: nowrap;
|
||
cursor: pointer;
|
||
border: 1px solid transparent;
|
||
|
||
.icon {
|
||
width: 18px;
|
||
height: 18px;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
.active {
|
||
background: #FFFFFF40;
|
||
}
|
||
}
|
||
|
||
.footer-tips {
|
||
text-align: center;
|
||
font-weight: 500;
|
||
font-size: 12px;
|
||
color: #FFFFFFAA;
|
||
line-height: 24px;
|
||
}
|
||
}
|
||
|
||
.payment-base-dialog {
|
||
|
||
|
||
:deep(.el-dialog) {
|
||
padding: 0 !important;
|
||
background-color: transparent;
|
||
box-shadow: 0 0 0 0 #00000000;
|
||
}
|
||
|
||
:deep(.base-dialog .close) {
|
||
top: 80px !important;
|
||
right: -32px !important;
|
||
}
|
||
}
|
||
|
||
.fade-enter-active,
|
||
.fade-leave-active {
|
||
transition: opacity 0.25s ease;
|
||
}
|
||
|
||
.fade-enter-from,
|
||
.fade-leave-to {
|
||
opacity: 0;
|
||
}
|
||
</style>
|