You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
755 lines
16 KiB
755 lines
16 KiB
2 weeks ago
|
<template>
|
||
|
<Modal
|
||
|
v-model:open="visible"
|
||
|
title="开通会员"
|
||
|
:width="900"
|
||
|
:footer="null"
|
||
|
@cancel="handleCancel"
|
||
|
class="membership-modal"
|
||
|
>
|
||
|
<div class="membership-content">
|
||
|
<!-- 会员权益介绍 -->
|
||
|
<div class="membership-benefits">
|
||
|
<div class="benefits-header">
|
||
|
<CrownOutlined class="crown-icon" />
|
||
|
<h3>会员专享权益</h3>
|
||
|
</div>
|
||
|
<div class="benefits-list">
|
||
|
<div class="benefit-item">
|
||
|
<CheckCircleOutlined class="benefit-icon" />
|
||
|
<span>无限次合同审查</span>
|
||
|
</div>
|
||
|
<div class="benefit-item">
|
||
|
<CheckCircleOutlined class="benefit-icon" />
|
||
|
<span>优先处理队列</span>
|
||
|
</div>
|
||
|
|
||
|
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<!-- 会员套餐选择 -->
|
||
|
<div class="membership-plans">
|
||
|
<h4>选择套餐</h4>
|
||
|
<div class="plans-grid">
|
||
|
<div
|
||
|
v-for="plan in membershipPlans"
|
||
|
:key="plan.id"
|
||
|
:class="['plan-card', { active: selectedPlan?.id === plan.id }]"
|
||
|
@click="selectPlan(plan)"
|
||
|
>
|
||
|
<div class="plan-header">
|
||
|
<div class="plan-duration">{{ plan.duration }}</div>
|
||
|
<div class="plan-price">
|
||
|
<span class="currency">¥</span>
|
||
|
<span class="amount">{{ plan.price }}</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="plan-features">
|
||
|
<div class="feature-item">
|
||
|
<CheckOutlined class="feature-icon" />
|
||
|
<span>{{ plan.duration }}会员服务</span>
|
||
|
</div>
|
||
|
<div class="feature-item">
|
||
|
<CheckOutlined class="feature-icon" />
|
||
|
<span>所有会员权益</span>
|
||
|
</div>
|
||
|
<div v-if="plan.discount" class="feature-item discount">
|
||
|
<Tag color="red">{{ plan.discount }}</Tag>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="plan-action">
|
||
|
<Button
|
||
|
:type="selectedPlan?.id === plan.id ? 'primary' : 'default'"
|
||
|
:ghost="selectedPlan?.id !== plan.id"
|
||
|
size="large"
|
||
|
block
|
||
|
>
|
||
|
{{ selectedPlan?.id === plan.id ? '已选择' : '选择套餐' }}
|
||
|
</Button>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<!-- 支付方式选择 -->
|
||
|
<div class="payment-methods">
|
||
|
<h4>支付方式</h4>
|
||
|
<Radio.Group v-model:value="selectedPayment" class="payment-options">
|
||
|
<Radio value="wechat" class="payment-option">
|
||
|
<div class="payment-item">
|
||
|
<WechatOutlined class="payment-icon wechat" />
|
||
|
<span>微信支付</span>
|
||
|
</div>
|
||
|
</Radio>
|
||
|
<Radio value="alipay" class="payment-option">
|
||
|
<div class="payment-item">
|
||
|
<AlipayCircleOutlined class="payment-icon alipay" />
|
||
|
<span>支付宝</span>
|
||
|
</div>
|
||
|
</Radio>
|
||
|
</Radio.Group>
|
||
|
</div>
|
||
|
|
||
|
<!-- 订单信息 -->
|
||
|
<div v-if="selectedPlan" class="order-summary">
|
||
|
<Divider />
|
||
|
<div class="summary-item">
|
||
|
<span>套餐:</span>
|
||
|
<span>{{ selectedPlan.duration }}</span>
|
||
|
</div>
|
||
|
<div class="summary-item">
|
||
|
<span>价格:</span>
|
||
|
<span class="price">¥{{ selectedPlan.price }}</span>
|
||
|
</div>
|
||
|
<div v-if="selectedPlan.originalPrice" class="summary-item discount">
|
||
|
<span>原价:</span>
|
||
|
<span class="original-price">¥{{ selectedPlan.originalPrice }}</span>
|
||
|
</div>
|
||
|
<div v-if="currentOrderNo" class="summary-item">
|
||
|
<span>订单号:</span>
|
||
|
<span class="order-no">{{ currentOrderNo }}</span>
|
||
|
</div>
|
||
|
<div v-if="paymentStatus" class="summary-item">
|
||
|
<span>支付状态:</span>
|
||
|
<span :class="['status', getStatusClass(paymentStatus)]">{{ getStatusText(paymentStatus) }}</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<!-- 操作按钮 -->
|
||
|
<div class="modal-actions">
|
||
|
<Button @click="handleCancel" size="large">
|
||
|
取消
|
||
|
</Button>
|
||
|
<Button
|
||
|
type="primary"
|
||
|
size="large"
|
||
|
:loading="loading"
|
||
|
:disabled="!selectedPlan || !selectedPayment"
|
||
|
@click="handlePurchase"
|
||
|
>
|
||
|
立即购买 ¥{{ selectedPlan?.price || 0 }}
|
||
|
</Button>
|
||
|
</div>
|
||
|
</div>
|
||
|
</Modal>
|
||
|
|
||
|
<!-- 二维码支付弹窗 -->
|
||
|
<PaymentQRCodeModal
|
||
|
v-model:visible="qrCodeVisible"
|
||
|
:qr-code-url="qrCodeUrl"
|
||
|
:payment-method="selectedPayment"
|
||
|
:order-no="currentOrderNo"
|
||
|
:amount="selectedPlan?.price || 0"
|
||
|
@payment-success="handlePaymentSuccess"
|
||
|
@payment-failed="handlePaymentFailed"
|
||
|
@cancel="handleQrCodeCancel"
|
||
|
/>
|
||
|
</template>
|
||
|
|
||
|
<script setup lang="ts">
|
||
|
import { ref, computed } from 'vue';
|
||
|
import {
|
||
|
Modal,
|
||
|
Button,
|
||
|
Radio,
|
||
|
Tag,
|
||
|
Divider,
|
||
|
message
|
||
|
} from 'ant-design-vue';
|
||
|
import {
|
||
|
CrownOutlined,
|
||
|
CheckCircleOutlined,
|
||
|
CheckOutlined,
|
||
|
WechatOutlined,
|
||
|
AlipayCircleOutlined,
|
||
|
LoadingOutlined
|
||
|
} from '@ant-design/icons-vue';
|
||
|
import { createPayment, queryPaymentStatus } from '@/api/contractReview/Payment';
|
||
|
import { useUserStore } from '@/store/modules/user';
|
||
|
import PaymentQRCodeModal from './PaymentQRCodeModal.vue';
|
||
|
|
||
|
interface MembershipPlan {
|
||
|
id: string;
|
||
|
duration: string;
|
||
|
price: number;
|
||
|
originalPrice?: number;
|
||
|
discount?: string;
|
||
|
}
|
||
|
|
||
|
const props = defineProps<{
|
||
|
visible: boolean;
|
||
|
}>();
|
||
|
|
||
|
const emit = defineEmits<{
|
||
|
'update:visible': [value: boolean];
|
||
|
'purchase-success': [plan: MembershipPlan];
|
||
|
}>();
|
||
|
|
||
|
// 状态管理
|
||
|
const selectedPlan = ref<MembershipPlan | null>(null);
|
||
|
const selectedPayment = ref<string>('');
|
||
|
const loading = ref(false);
|
||
|
const currentOrderNo = ref<string>('');
|
||
|
const paymentStatus = ref<string>('');
|
||
|
const qrCodeVisible = ref(false);
|
||
|
const qrCodeUrl = ref<string>('');
|
||
|
|
||
|
// 用户信息
|
||
|
const userStore = useUserStore();
|
||
|
|
||
|
// 会员套餐数据
|
||
|
const membershipPlans: MembershipPlan[] = [
|
||
|
{
|
||
|
id: '1month',
|
||
|
duration: '1个月',
|
||
|
price: 0.01,
|
||
|
originalPrice: 129,
|
||
|
discount: '限时优惠'
|
||
|
},
|
||
|
{
|
||
|
id: '3months',
|
||
|
duration: '季度',
|
||
|
price: 269,
|
||
|
originalPrice: 387,
|
||
|
discount: '省¥118'
|
||
|
},
|
||
|
{
|
||
|
id: '6months',
|
||
|
duration: '半年',
|
||
|
price: 499,
|
||
|
originalPrice: 774,
|
||
|
discount: '省¥275'
|
||
|
},
|
||
|
{
|
||
|
id: '12months',
|
||
|
duration: '12个月',
|
||
|
price: 899,
|
||
|
originalPrice: 1548,
|
||
|
discount: '省¥649'
|
||
|
}
|
||
|
];
|
||
|
|
||
|
// 计算属性
|
||
|
const visible = computed({
|
||
|
get: () => props.visible,
|
||
|
set: (value) => emit('update:visible', value)
|
||
|
});
|
||
|
|
||
|
// 方法
|
||
|
function selectPlan(plan: MembershipPlan) {
|
||
|
selectedPlan.value = plan;
|
||
|
}
|
||
|
|
||
|
function handleCancel() {
|
||
|
visible.value = false;
|
||
|
selectedPlan.value = null;
|
||
|
selectedPayment.value = '';
|
||
|
currentOrderNo.value = '';
|
||
|
paymentStatus.value = '';
|
||
|
qrCodeVisible.value = false;
|
||
|
qrCodeUrl.value = '';
|
||
|
}
|
||
|
|
||
|
function handleQrCodeCancel() {
|
||
|
qrCodeVisible.value = false;
|
||
|
qrCodeUrl.value = '';
|
||
|
}
|
||
|
|
||
|
function handlePaymentSuccess() {
|
||
|
// 支付成功处理
|
||
|
if (selectedPlan.value) {
|
||
|
emit('purchase-success', selectedPlan.value);
|
||
|
}
|
||
|
qrCodeVisible.value = false;
|
||
|
handleCancel();
|
||
|
}
|
||
|
|
||
|
function handlePaymentFailed() {
|
||
|
// 支付失败处理
|
||
|
qrCodeVisible.value = false;
|
||
|
}
|
||
|
|
||
|
async function handlePurchase() {
|
||
|
if (!selectedPlan.value || !selectedPayment.value) {
|
||
|
message.warning('请选择套餐和支付方式');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
loading.value = true;
|
||
|
|
||
|
try {
|
||
|
// 构建支付请求
|
||
|
const paymentRequest = {
|
||
|
planType: selectedPlan.value.id,
|
||
|
planName: selectedPlan.value.duration,
|
||
|
duration: getDurationInMonths(selectedPlan.value.duration),
|
||
|
amount: selectedPlan.value.price,
|
||
|
paymentMethod: selectedPayment.value,
|
||
|
userId: userStore.getUserInfo?.userId || 1,
|
||
|
clientIp: '',
|
||
|
notifyUrl: '',
|
||
|
returnUrl: ''
|
||
|
};
|
||
|
|
||
|
// 调用后端支付API
|
||
|
const result = await createPayment(paymentRequest);
|
||
|
|
||
|
if (result.success) {
|
||
|
// 保存订单号用于状态查询
|
||
|
currentOrderNo.value = result.orderNo || '';
|
||
|
paymentStatus.value = result.payStatus || 'PENDING';
|
||
|
|
||
|
// 支付创建成功,显示二维码
|
||
|
if (result.qrCodeUrl) {
|
||
|
qrCodeUrl.value = result.qrCodeUrl;
|
||
|
qrCodeVisible.value = true;
|
||
|
}
|
||
|
|
||
|
message.success('支付订单创建成功,请扫码支付');
|
||
|
} else {
|
||
|
message.error(result.errorMsg || '支付创建失败');
|
||
|
}
|
||
|
} catch (error) {
|
||
|
console.error('支付失败:', error);
|
||
|
message.error('购买失败,请重试');
|
||
|
} finally {
|
||
|
loading.value = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 获取套餐时长(月数)
|
||
|
function getDurationInMonths(duration: string): number {
|
||
|
switch (duration) {
|
||
|
case '1个月':
|
||
|
return 1;
|
||
|
case '季度':
|
||
|
return 3;
|
||
|
case '半年':
|
||
|
return 6;
|
||
|
case '12个月':
|
||
|
return 12;
|
||
|
default:
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 轮询逻辑已迁移到 PaymentQRCodeModal 组件中
|
||
|
|
||
|
// 获取状态文本
|
||
|
function getStatusText(status: string): string {
|
||
|
const statusMap: Record<string, string> = {
|
||
|
'INIT': '订单生成',
|
||
|
'ING': '支付中',
|
||
|
'SUCCESS': '支付成功',
|
||
|
'FAIL': '支付失败',
|
||
|
'CANCEL': '已撤销',
|
||
|
'REFUND': '已退款',
|
||
|
'CLOSED': '已关闭',
|
||
|
'PENDING': '待支付' // 保留兼容性
|
||
|
};
|
||
|
return statusMap[status] || '未知状态';
|
||
|
}
|
||
|
|
||
|
// 获取状态样式类
|
||
|
function getStatusClass(status: string): string {
|
||
|
const statusClassMap: Record<string, string> = {
|
||
|
'INIT': 'status-pending', // 订单生成 - 待支付状态
|
||
|
'ING': 'status-pending', // 支付中 - 进行中状态
|
||
|
'SUCCESS': 'status-success', // 支付成功
|
||
|
'FAIL': 'status-failed', // 支付失败
|
||
|
'CANCEL': 'status-failed', // 已撤销 - 失败状态
|
||
|
'REFUND': 'status-closed', // 已退款 - 关闭状态
|
||
|
'CLOSED': 'status-closed', // 已关闭
|
||
|
'PENDING': 'status-pending' // 兼容性保留 - 待支付
|
||
|
};
|
||
|
return statusClassMap[status] || 'status-unknown';
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<style lang="less" scoped>
|
||
|
.membership-modal {
|
||
|
:deep(.ant-modal-content) {
|
||
|
border-radius: 16px;
|
||
|
overflow: hidden;
|
||
|
}
|
||
|
|
||
|
:deep(.ant-modal-header) {
|
||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
|
color: white;
|
||
|
border-bottom: none;
|
||
|
padding: 24px 24px 20px;
|
||
|
|
||
|
.ant-modal-title {
|
||
|
color: white;
|
||
|
font-size: 18px;
|
||
|
font-weight: 600;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
:deep(.ant-modal-close) {
|
||
|
color: white;
|
||
|
|
||
|
&:hover {
|
||
|
color: rgba(255, 255, 255, 0.8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
:deep(.ant-modal-body) {
|
||
|
padding: 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.membership-content {
|
||
|
padding: 24px;
|
||
|
}
|
||
|
|
||
|
.membership-benefits {
|
||
|
margin-bottom: 32px;
|
||
|
padding: 20px;
|
||
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||
|
border-radius: 12px;
|
||
|
|
||
|
.benefits-header {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
margin-bottom: 16px;
|
||
|
|
||
|
.crown-icon {
|
||
|
font-size: 24px;
|
||
|
color: #ffd700;
|
||
|
margin-right: 12px;
|
||
|
}
|
||
|
|
||
|
h3 {
|
||
|
margin: 0;
|
||
|
font-size: 18px;
|
||
|
font-weight: 600;
|
||
|
color: #333;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.benefits-list {
|
||
|
display: grid;
|
||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
|
gap: 12px;
|
||
|
|
||
|
.benefit-item {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
padding: 8px 0;
|
||
|
|
||
|
.benefit-icon {
|
||
|
color: #52c41a;
|
||
|
margin-right: 8px;
|
||
|
font-size: 16px;
|
||
|
}
|
||
|
|
||
|
span {
|
||
|
color: #666;
|
||
|
font-size: 14px;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.membership-plans {
|
||
|
margin-bottom: 32px;
|
||
|
|
||
|
h4 {
|
||
|
margin-bottom: 16px;
|
||
|
font-size: 16px;
|
||
|
font-weight: 600;
|
||
|
color: #333;
|
||
|
}
|
||
|
|
||
|
.plans-grid {
|
||
|
display: grid;
|
||
|
grid-template-columns: repeat(4, 1fr);
|
||
|
gap: 16px;
|
||
|
|
||
|
.plan-card {
|
||
|
border: 2px solid #e8e8e8;
|
||
|
border-radius: 12px;
|
||
|
padding: 12px;
|
||
|
cursor: pointer;
|
||
|
transition: all 0.3s ease;
|
||
|
background: white;
|
||
|
min-width: 0;
|
||
|
|
||
|
&:hover {
|
||
|
border-color: #667eea;
|
||
|
transform: translateY(-2px);
|
||
|
box-shadow: 0 8px 24px rgba(102, 126, 234, 0.15);
|
||
|
}
|
||
|
|
||
|
&.active {
|
||
|
border-color: #1890ff;
|
||
|
background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%);
|
||
|
color: #1890ff;
|
||
|
transform: translateY(-2px);
|
||
|
box-shadow: 0 8px 24px rgba(24, 144, 255, 0.15);
|
||
|
|
||
|
.plan-price {
|
||
|
color: #1890ff;
|
||
|
|
||
|
.currency {
|
||
|
color: #1890ff;
|
||
|
}
|
||
|
|
||
|
.amount {
|
||
|
color: #1890ff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.feature-item {
|
||
|
color: #333;
|
||
|
|
||
|
.feature-icon {
|
||
|
color: #52c41a;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.plan-header {
|
||
|
text-align: center;
|
||
|
margin-bottom: 16px;
|
||
|
|
||
|
.plan-duration {
|
||
|
font-size: 14px;
|
||
|
font-weight: 600;
|
||
|
margin-bottom: 6px;
|
||
|
}
|
||
|
|
||
|
.plan-price {
|
||
|
.currency {
|
||
|
font-size: 12px;
|
||
|
color: #666;
|
||
|
}
|
||
|
|
||
|
.amount {
|
||
|
font-size: 20px;
|
||
|
font-weight: 700;
|
||
|
color: #667eea;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.plan-features {
|
||
|
margin-bottom: 16px;
|
||
|
|
||
|
.feature-item {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
margin-bottom: 6px;
|
||
|
font-size: 11px;
|
||
|
color: #666;
|
||
|
|
||
|
.feature-icon {
|
||
|
margin-right: 4px;
|
||
|
color: #52c41a;
|
||
|
font-size: 10px;
|
||
|
}
|
||
|
|
||
|
&.discount {
|
||
|
justify-content: center;
|
||
|
margin-top: 6px;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.plan-action {
|
||
|
.ant-btn {
|
||
|
border-radius: 8px;
|
||
|
font-weight: 500;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.payment-methods {
|
||
|
margin-bottom: 24px;
|
||
|
|
||
|
h4 {
|
||
|
margin-bottom: 16px;
|
||
|
font-size: 16px;
|
||
|
font-weight: 600;
|
||
|
color: #333;
|
||
|
}
|
||
|
|
||
|
.payment-options {
|
||
|
display: flex;
|
||
|
gap: 16px;
|
||
|
|
||
|
.payment-option {
|
||
|
flex: 1;
|
||
|
|
||
|
.payment-item {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
padding: 12px 16px;
|
||
|
border: 2px solid #e8e8e8;
|
||
|
border-radius: 8px;
|
||
|
cursor: pointer;
|
||
|
transition: all 0.3s ease;
|
||
|
|
||
|
&:hover {
|
||
|
border-color: #667eea;
|
||
|
}
|
||
|
|
||
|
.payment-icon {
|
||
|
font-size: 20px;
|
||
|
margin-right: 8px;
|
||
|
|
||
|
&.wechat {
|
||
|
color: #07c160;
|
||
|
}
|
||
|
|
||
|
&.alipay {
|
||
|
color: #1677ff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
span {
|
||
|
font-size: 14px;
|
||
|
color: #333;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
:deep(.ant-radio-wrapper) {
|
||
|
width: 100%;
|
||
|
margin-right: 0;
|
||
|
|
||
|
.ant-radio {
|
||
|
display: none;
|
||
|
}
|
||
|
|
||
|
&.ant-radio-wrapper-checked .payment-item {
|
||
|
border-color: #667eea;
|
||
|
background: #f0f2ff;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.order-summary {
|
||
|
margin-bottom: 24px;
|
||
|
|
||
|
.summary-item {
|
||
|
display: flex;
|
||
|
justify-content: space-between;
|
||
|
align-items: center;
|
||
|
padding: 8px 0;
|
||
|
font-size: 14px;
|
||
|
|
||
|
.price {
|
||
|
font-weight: 600;
|
||
|
color: #667eea;
|
||
|
font-size: 16px;
|
||
|
}
|
||
|
|
||
|
.order-no {
|
||
|
font-family: monospace;
|
||
|
color: #666;
|
||
|
font-size: 12px;
|
||
|
}
|
||
|
|
||
|
.status {
|
||
|
font-weight: 500;
|
||
|
padding: 2px 8px;
|
||
|
border-radius: 4px;
|
||
|
font-size: 12px;
|
||
|
|
||
|
&.status-pending {
|
||
|
background: #fff7e6;
|
||
|
color: #fa8c16;
|
||
|
}
|
||
|
|
||
|
&.status-success {
|
||
|
background: #f6ffed;
|
||
|
color: #52c41a;
|
||
|
}
|
||
|
|
||
|
&.status-failed {
|
||
|
background: #fff2f0;
|
||
|
color: #ff4d4f;
|
||
|
}
|
||
|
|
||
|
&.status-closed {
|
||
|
background: #f5f5f5;
|
||
|
color: #999;
|
||
|
}
|
||
|
|
||
|
&.status-unknown {
|
||
|
background: #f0f0f0;
|
||
|
color: #666;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
&.discount {
|
||
|
.original-price {
|
||
|
text-decoration: line-through;
|
||
|
color: #999;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.modal-actions {
|
||
|
display: flex;
|
||
|
gap: 12px;
|
||
|
justify-content: flex-end;
|
||
|
|
||
|
.ant-btn {
|
||
|
min-width: 100px;
|
||
|
border-radius: 8px;
|
||
|
font-weight: 500;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 响应式设计
|
||
|
@media (max-width: 1000px) {
|
||
|
.plans-grid {
|
||
|
grid-template-columns: repeat(2, 1fr) !important;
|
||
|
gap: 12px !important;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@media (max-width: 768px) {
|
||
|
.membership-content {
|
||
|
padding: 16px;
|
||
|
}
|
||
|
|
||
|
.plans-grid {
|
||
|
grid-template-columns: repeat(2, 1fr) !important;
|
||
|
gap: 12px !important;
|
||
|
}
|
||
|
|
||
|
.payment-options {
|
||
|
flex-direction: column;
|
||
|
gap: 12px;
|
||
|
}
|
||
|
|
||
|
.modal-actions {
|
||
|
flex-direction: column;
|
||
|
|
||
|
.ant-btn {
|
||
|
width: 100%;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@media (max-width: 480px) {
|
||
|
.plans-grid {
|
||
|
grid-template-columns: 1fr !important;
|
||
|
}
|
||
|
|
||
|
.benefits-list {
|
||
|
grid-template-columns: 1fr !important;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 二维码弹窗样式已迁移到 PaymentQRCodeModal.vue 组件中
|
||
|
</style>
|