2
0

feat:多支付以及货币符号显示

This commit is contained in:
zhongzm
2025-04-27 20:17:35 +08:00
parent 621932f514
commit 3cc72b946d
11 changed files with 142 additions and 29 deletions

View File

@@ -1,10 +1,10 @@
<script setup lang="ts">
import { defineProps, defineEmits, computed } from 'vue';
import { defineProps, defineEmits, computed, ref, onMounted, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { AlipayOutlined, WechatOutlined, WalletOutlined } from '@ant-design/icons-vue';
import { AlipayOutlined, WechatOutlined, WalletOutlined,MoneyCollectOutlined } from '@ant-design/icons-vue';
import { Modal } from 'ant-design-vue';
import PaypalButton from '@/components/payment/paypal-button.vue';
import { stripePay } from '@/service/api/payment';
import { stripePay, getPaymentConfig } from '@/service/api/payment';
const { t } = useI18n();
@@ -23,6 +23,27 @@ interface Props {
const props = defineProps<Props>();
const emit = defineEmits(['update:visible', 'confirm', 'cancel']);
const paymentMethods = ref<string[]>([]);
const currencySymbol = ref('¥');
// 获取支付配置
const fetchPaymentConfig = async () => {
try {
const response = await getPaymentConfig();
paymentMethods.value = response.data.paymentMethods || [];
currencySymbol.value = response.data.currencySymbol || '¥';
} catch (error) {
console.error('Failed to fetch payment config:', error);
}
};
// 监听visible属性变化
watch(() => props.visible, (newVisible) => {
if (newVisible) {
fetchPaymentConfig();
}
}, { immediate: true });
const orderTypeMap = {
0: t('page.order.packagePurchase'),
1: t('page.order.balanceRecharge')
@@ -85,7 +106,7 @@ const handleCancel = () => {
</div>
<div class="info-item">
<span class="label">{{ t('page.order.orderAmount') }}</span>
<span class="value highlight">¥{{ orderInfo.orderAmount.toFixed(2) }}</span>
<span class="value highlight">{{ currencySymbol }}{{ orderInfo.orderAmount.toFixed(2) }}</span>
</div>
<div class="info-item">
<span class="label">{{ t('page.order.orderId') }}</span>
@@ -104,20 +125,19 @@ const handleCancel = () => {
}"
@click="!isBalancePayDisabled && handleConfirm('balance')"
>
<div class="debug-info" style="display: none;">
Enable balance pay: {{ enableBalancePay }}
</div>
<WalletOutlined class="payment-icon balance-icon" />
<span>{{ t('page.order.balancePay') }}</span>
<div class="balance-info" v-if="userBalance !== undefined">
{{ t('page.order.availableBalance') }}: ¥{{ userBalance.toFixed(2) }}
{{ t('page.order.availableBalance') }}: {{ currencySymbol }}{{ userBalance.toFixed(2) }}
<template v-if="userBalance < orderInfo.orderAmount">
<br>
<span class="balance-insufficient">{{ t('page.order.insufficientBalance') }}</span>
</template>
</div>
</div>
<div
v-if="paymentMethods.includes('alipay')"
class="method-item"
:class="{ disabled: loading }"
@click="!loading && handleConfirm('alipay')"
@@ -125,7 +145,9 @@ const handleCancel = () => {
<AlipayOutlined class="payment-icon alipay-icon" />
<span>{{ t('page.order.alipay') }}</span>
</div>
<div
v-if="paymentMethods.includes('wxpay')"
class="method-item"
:class="{ disabled: loading }"
@click="!loading && handleConfirm('wxpay')"
@@ -133,16 +155,20 @@ const handleCancel = () => {
<WechatOutlined class="payment-icon wxpay-icon" />
<span>{{ t('page.order.wxpay') }}</span>
</div>
<div
v-if="paymentMethods.includes('stripe')"
class="method-item"
:class="{ disabled: loading }"
@click="!loading && handleConfirm('stripe')"
>
<WechatOutlined class="payment-icon wxpay-icon" />
<MoneyCollectOutlined class="payment-icon wxpay-icon" />
<span>{{ t('page.order.stripe') }}</span>
</div>
<PaypalButton
:order-info="props.orderInfo"
v-if="paymentMethods.includes('paypal')"
:order-info="props.orderInfo"
/>
</div>
</div>
@@ -199,10 +225,13 @@ const handleCancel = () => {
.methods-container {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.method-item {
flex: 1;
min-width: 120px;
max-width: calc(50% - 6px);
display: flex;
flex-direction: column;
align-items: center;

View File

@@ -425,7 +425,7 @@ export const customRoutes: GeneratedRoute[] = [
hideInMenu: true,
order: 10,
},
}
},
]
},
{

View File

@@ -62,3 +62,10 @@ export function stripePay(orderId: number) {
method: 'post'
});
}
/** Get payment configuration */
export function getPaymentConfig() {
return request({
url: '/u/config/pay',
method: 'get'
});
}

View File

@@ -128,6 +128,7 @@ declare global {
const getFixedTabIds: typeof import('../store/modules/tab/shared')['getFixedTabIds']
const getFixedTabs: typeof import('../store/modules/tab/shared')['getFixedTabs']
const getGlobalMenusByAuthRoutes: typeof import('../store/modules/route/shared')['getGlobalMenusByAuthRoutes']
const getPaymentConfig: typeof import('../service/api/payment')['getPaymentConfig']
const getQueryParams: typeof import('../utils/common')['getQueryParams']
const getRouteIcons: typeof import('../store/modules/tab/shared')['getRouteIcons']
const getSelectedMenuKeyPathByKey: typeof import('../store/modules/route/shared')['getSelectedMenuKeyPathByKey']

View File

@@ -80,6 +80,7 @@ declare module 'vue' {
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
OrderConfirmModal: typeof import('./../components/order-confirm/orderConfirmModal.vue')['default']
Pay: typeof import('./../components/pay/pay.vue')['default']
PaypalButton: typeof import('./../components/payment/paypal-button.vue')['default']
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
ReloadButton: typeof import('./../components/common/reload-button.vue')['default']

View File

@@ -7,6 +7,7 @@ import { useRouter } from 'vue-router';
import { fetchPackageHistory } from '@/service/api/auth';
import { useWindowSize } from '@vueuse/core';
import { useTable, useTableOperate } from '@/hooks/common/table';
import { getPaymentConfig } from '@/service/api/payment';
const router = useRouter();
const handleBack = () => {
@@ -14,7 +15,18 @@ const handleBack = () => {
};
const { t } = useI18n();
const currencySymbol = ref('¥');
const fetchCurrencySymbol = async () => {
try {
const response = await getPaymentConfig();
if (response && response.data) {
currencySymbol.value = response.data.currencySymbol || '¥';
}
} catch (error) {
console.error('Failed to fetch currency symbol:', error);
}
};
const packageData = ref<Api.Package.PackageHistoryRecord[]>([]);
const total = ref(0);
@@ -51,6 +63,7 @@ const getPackageData = async () => {
};
onMounted(() => {
fetchCurrencySymbol();
getPackageData();
});
@@ -75,7 +88,7 @@ const showPackageDetail = (record: Api.Package.PackageHistoryRecord) => {
]),
h('div', { class: 'detail-item' }, [
h('span', { class: 'detail-label' }, t('page.packagehistories.price') + ':'),
h('span', { class: 'detail-value' }, `¥${Number(record.upackage?.price || 0).toFixed(2)}`)
h('span', { class: 'detail-value' }, `${currencySymbol.value}${Number(record.upackage?.price || 0).toFixed(2)}`)
]),
h('div', { class: 'detail-item' }, [
h('span', { class: 'detail-label' }, t('page.packagehistories.traffic') + ':'),
@@ -168,7 +181,7 @@ const { columns, data, loading, getData, mobilePagination, searchParams } = useT
key: 'orderAmount',
customRender: ({ text }: { text: string }) => {
const formattedAmount = Number(text).toFixed(2);
return `¥${formattedAmount}`;
return `${currencySymbol.value}${formattedAmount}`;
}
},
{

View File

@@ -6,6 +6,7 @@ import { useI18n } from "vue-i18n";
import { useRouter } from 'vue-router';
import { fetchRechargeHistory } from '@/service/api/auth';
import { useWindowSize } from '@vueuse/core';
import { getPaymentConfig } from '@/service/api/payment';
const router = useRouter();
const handleBack = () => {
@@ -13,7 +14,18 @@ const handleBack = () => {
};
const { t } = useI18n();
const currencySymbol = ref('¥');
const fetchCurrencySymbol = async () => {
try {
const response = await getPaymentConfig();
if (response && response.data) {
currencySymbol.value = response.data.currencySymbol || '¥';
}
} catch (error) {
console.error('Failed to fetch currency symbol:', error);
}
};
const rechargeData = ref<Api.Recharge.RechargeRecord[]>([]);
const total = ref(0);
@@ -56,7 +68,7 @@ const { columns, data, loading, getData, mobilePagination, searchParams } = useT
key: 'orderAmount',
customRender: ({ text }: { text: string }) => {
const formattedAmount = Number(text).toFixed(2);
return `¥${formattedAmount}`;
return `${currencySymbol.value}${formattedAmount}`;
}
},
{
@@ -85,6 +97,9 @@ const { columns, data, loading, getData, mobilePagination, searchParams } = useT
}
]
});
onMounted(()=>{
fetchCurrencySymbol();
})
</script>

View File

@@ -6,9 +6,26 @@ import type { ECOption } from '@/hooks/common/echarts';
import { useI18n } from 'vue-i18n';
import { useAuthStore } from '@/store/modules/auth';
import { clientAuth } from '@/service/ue/client';
import { getPaymentConfig } from '@/service/api/payment';
const { t } = useI18n();
const authStore = useAuthStore();
// 添加货币符号的响应式引用
const currencySymbol = ref('¥');
// 获取货币符号
const fetchCurrencySymbol = async () => {
try {
const response = await getPaymentConfig();
if (response && response.data) {
currencySymbol.value = response.data.currencySymbol || '¥';
}
} catch (error) {
console.error('Failed to fetch currency symbol:', error);
}
};
defineOptions({
name: 'HeaderBanner'
});
@@ -307,7 +324,7 @@ async function mockDataUpdate() {
value: numBalance,
max: Math.max(numBalance, 100),
displayValue:formatBalance(response.balance),
unit: t('page.headerbanner.money'),
unit: currencySymbol.value,
subTitle: t('page.headerbanner.deviceCount') + `: ${
response.packageName ? (
!response.clientNumEnable
@@ -422,6 +439,8 @@ let timer: ReturnType<typeof setInterval> | null = null;
// 初始化
async function init() {
// 立即执行一次数据更新
// 先获取货币符号
await fetchCurrencySymbol();
await mockDataUpdate();
// 设置定期执行的定时器
timer = setInterval(mockDataUpdate, 30000);
@@ -500,7 +519,7 @@ const getDeviceCount = (subTitle?: string, clientNumEnable?: boolean): string =>
</div>
<div class="info-item">
<span class="info-label">{{ t('page.headerbanner.price') }}</span>
<span class="info-value">{{ packageInfo.price === '-' ? '-' : '¥' + packageInfo.price }}</span>
<span class="info-value">{{ packageInfo.price === '-' ? '-' : currencySymbol + packageInfo.price }}</span>
</div>
<div class="info-item">
<span class="info-label">{{ t('page.headerbanner.monthflowr') }}</span>
@@ -531,7 +550,7 @@ const getDeviceCount = (subTitle?: string, clientNumEnable?: boolean): string =>
<div class="info-group">
<div class="info-item">
<span class="info-label">{{ t('page.headerbanner.Remainingcredit') }}</span>
<span class="info-value">{{ baseData[0].displayValue === '-' ? '-' : '¥' + baseData[0].displayValue }}</span>
<span class="info-value">{{ baseData[0].displayValue === '-' ? '-' : currencySymbol + baseData[0].displayValue }}</span>
</div>
<div class="info-item">
<span class="info-label">{{ t('page.headerbanner.client') }}</span>

View File

@@ -8,6 +8,7 @@ import { useRouterPush } from '@/hooks/common/router';
import type { Ref } from 'vue';
import OrderConfirmModal from '@/components/order-confirm/orderConfirmModal.vue';
import { useAppStore } from '@/store/modules/app';
import { getPaymentConfig } from '@/service/api/payment';
defineOptions({
name: 'BalanceRecharge'
@@ -16,7 +17,18 @@ defineOptions({
const { t } = useI18n();
const { routerPushByKey } = useRouterPush();
const appStore = useAppStore();
const currencySymbol = ref('¥');
const fetchCurrencySymbol = async () => {
try {
const response = await getPaymentConfig();
if (response && response.data) {
currencySymbol.value = response.data.currencySymbol || '¥';
}
} catch (error) {
console.error('Failed to fetch currency symbol:', error);
}
};
interface RechargeOption {
amount: number;
displayAmount: string;
@@ -27,13 +39,13 @@ const customAmount = ref<string | number | undefined>(undefined);
const selectedAmount = ref<number | null>(null);
const isCustomMode = ref<boolean>(false);
const rechargeOptions: Ref<RechargeOption[]> = ref([
{ amount: 10, displayAmount: `${t('page.carddata.money')}10`, price: 10.00 },
{ amount: 20, displayAmount: `${t('page.carddata.money')}20`, price: 20.00 },
{ amount: 30, displayAmount: `${t('page.carddata.money')}30`, price: 30.00 },
{ amount: 50, displayAmount: `${t('page.carddata.money')}50`, price: 50.00 },
{ amount: 100, displayAmount: `${t('page.carddata.money')}100`, price: 100.00 },
{ amount: 200, displayAmount: `${t('page.carddata.money')}200`, price: 200.00 },
const rechargeOptions = computed(() => [
{ amount: 10, displayAmount: `${currencySymbol.value}10`, price: 10.00 },
{ amount: 20, displayAmount: `${currencySymbol.value}20`, price: 20.00 },
{ amount: 30, displayAmount: `${currencySymbol.value}30`, price: 30.00 },
{ amount: 50, displayAmount: `${currencySymbol.value}50`, price: 50.00 },
{ amount: 100, displayAmount: `${currencySymbol.value}100`, price: 100.00 },
{ amount: 200, displayAmount: `${currencySymbol.value}200`, price: 200.00 },
]);
const paymentAmount = computed(() => {
@@ -76,10 +88,12 @@ const handleBlur = () => {
};
onMounted(() => {
fetchCurrencySymbol();
document.addEventListener('click', handleClickOutside);
});
onUnmounted(() => {
fetchCurrencySymbol();
document.removeEventListener('click', handleClickOutside);
});
@@ -202,7 +216,7 @@ const handlePaymentConfirm = async (paymentMethod: 'alipay' | 'wxpay') => {
:disabled="!paymentAmount || paymentAmount <= 0"
@click="handleRecharge"
>
<span style="color: var(--text-color, var(--ant-text-color))"> ¥{{ paymentAmount.toFixed(2) }} {{ t('page.carddata.pay') }} </span>
<span style="color: var(--text-color, var(--ant-text-color))"> {{ currencySymbol }}{{ paymentAmount.toFixed(2) }} {{ t('page.carddata.pay') }} </span>
</AButton>
</div>

View File

@@ -8,10 +8,23 @@ import { aliPayPcPay,aliPayWapPay, wxPayScanCode, payBalance } from '@/service/a
import { useRouterPush } from '@/hooks/common/router';
import { useAppStore } from '@/store/modules/app';
import { useAuthStore} from '@/store/modules/auth';
import { getPaymentConfig } from '@/service/api/payment';
defineOptions({
name: 'PackageSubscription'
});
const currencySymbol = ref('¥');
const fetchCurrencySymbol = async () => {
try {
const response = await getPaymentConfig();
if (response && response.data) {
currencySymbol.value = response.data.currencySymbol || '¥';
}
} catch (error) {
console.error('Failed to fetch currency symbol:', error);
}
};
const { t } = useI18n();
const authStore = useAuthStore();
interface RateLimit {
@@ -343,6 +356,7 @@ const isPackageActive = computed(() => {
});
onMounted(async () => {
await fetchCurrencySymbol();
fetchDashboardData();
await fetchPackages();
await fetchUserBalance();
@@ -354,7 +368,7 @@ onMounted(async () => {
<!-- 顶部价格展示 -->
<div class="price-header">
<div class="price">
<span class="currency">¥</span>
<span class="currency">{{ currencySymbol }}</span>
<span class="amount">{{ selectedPackage.price }}</span>
</div>
<div class="subtitle">{{ selectedPackage.packageName }}</div>
@@ -380,7 +394,7 @@ onMounted(async () => {
{{ t('page.setmeal.highlyrecommended') }}
</div>
<div class="package-name">{{ option.packageName }}</div>
<div class="price">¥{{ option.price }}</div>
<div class="price">{{ currencySymbol }}{{ option.price }}</div>
<div class="traffic">{{ option.trafficEnable ? option.trafficDisplay : t('page.setmeal.unlimit') }}</div>
<div class="device-count">
{{ option.clientNumEnable ? `${option.clientNum} ${t('page.setmeal.device')}` : t('page.setmeal.unlimit') }}