jcloud/dashboard/src/components/billing/PaymentDetails.vue

234 lines
6.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="flex flex-col gap-4 py-10">
<div class="flex flex-col">
<div class="flex items-center justify-between text-base text-gray-900">
<div class="flex flex-col gap-1.5">
<div class="text-lg font-semibold text-gray-900">{{ $t('Account Balance') }}</div>
<div class="text-2xl font-bold text-blue-600 py-6">
{{ availableCredits || currency + ' 0.00' }}
</div>
</div>
<div class="shrink-0">
<Button
:label="$t('Recharge')"
@click="
() => {
showMessage = false;
showAddPrepaidCreditsDialog = true;
}
"
>
<template #prefix>
<FeatherIcon class="h-4" name="plus" />
</template>
</Button>
</div>
</div>
<div class="my-3 h-px bg-gray-100" />
<div class="flex items-center justify-between text-base text-gray-900">
<div class="flex flex-col gap-1.5">
<div class="font-medium">{{ $t('Billing Address') }}</div>
<div v-if="billingDetailsSummary" class="leading-5 text-gray-700">
{{ billingDetailsSummary }}
</div>
<div v-else class="text-gray-700">{{ $t('No address') }}</div>
</div>
<div class="shrink-0">
<Button
:label="billingAddressButtonLabel"
@click="
() => {
showMessage = false;
showBillingDetailsDialog = true;
}
"
>
<template v-if="!billingDetailsSummary" #prefix>
<FeatherIcon class="h-4" name="plus" />
</template>
</Button>
</div>
</div>
</div>
</div>
<BillingDetailsDialog
v-if="showBillingDetailsDialog"
v-model="showBillingDetailsDialog"
:showMessage="showMessage"
@success="billingDetails.reload()"
/>
<AddPrepaidCreditsDialog
v-if="showAddPrepaidCreditsDialog"
v-model="showAddPrepaidCreditsDialog"
:showMessage="showMessage"
@success="upcomingInvoice.reload()"
/>
</template>
<script setup>
import DropdownItem from './DropdownItem.vue';
import BillingDetailsDialog from './BillingDetailsDialog.vue';
import AddPrepaidCreditsDialog from './AddPrepaidCreditsDialog.vue';
import { Dropdown, Button, FeatherIcon, createResource } from 'jingrow-ui';
import {
confirmDialog,
renderDialog,
} from '../../utils/components';
import { computed, ref, inject, h, defineAsyncComponent, onMounted, nextTick, getCurrentInstance } from 'vue';
import router from '../../router';
// 英文国家名到中文的映射
const countryToZh = {
'China': '中国',
'United States': '美国',
// 可以根据需要添加更多国家
};
const instance = getCurrentInstance();
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
const team = inject('team');
const {
availableCredits,
upcomingInvoice,
currentBillingAmount,
unpaidInvoices,
} = inject('billing');
const showBillingDetailsDialog = ref(false);
const showAddPrepaidCreditsDialog = ref(false);
const currency = computed(() => (team.pg.currency == 'CNY' ? '¥' : '$'));
const billingDetails = createResource({
url: 'jcloud.api.account.get_billing_information',
cache: 'billingDetails',
auto: true,
});
const changePaymentMode = createResource({
url: 'jcloud.api.billing.change_payment_mode',
onSuccess: () => setTimeout(() => team.reload(), 1000),
});
const billingDetailsSummary = computed(() => {
let _billingDetails = billingDetails.data;
if (!_billingDetails) return '';
const { address_line1, city, state, country, pincode } =
_billingDetails || {};
// 转换国家名称为中文
const displayCountry = countryToZh[country] || country;
// 调整为中国用户习惯的地址格式
// 中国地址格式:[国家] [省/自治区/直辖市] [市] [区/详细地址] [邮编]
if (country === 'China') {
// 中国地址使用中文格式
return [
displayCountry,
state, // 省/自治区/直辖市
city, // 市
address_line1, // 详细地址
pincode ? `邮编: ${pincode}` : '' // 邮编
]
.filter(Boolean)
.join(' ');
} else {
// 国外地址保持原来的格式
return [
address_line1,
city,
state,
displayCountry,
pincode
]
.filter(Boolean)
.join(', ');
}
});
const paymentModeOptions = [
{
label: $t('Prepaid Credits'),
value: 'Prepaid Credits',
description: $t('Your monthly billing charges will be deducted from your account balance'),
component: () =>
h(DropdownItem, {
label: $t('Prepaid Credits'),
active: team.pg.payment_mode === 'Prepaid Credits',
onClick: () => updatePaymentMode('Prepaid Credits'),
}),
},
{
label: $t('Paid By Partner'),
value: 'Paid By Partner',
condition: () => team.pg.partner_email,
description: $t('Your partner will pay your monthly subscription fees'),
component: () =>
h(DropdownItem, {
label: $t('Paid By Partner'),
active: team.pg.payment_mode === 'Paid by Partner',
onClick: () =>
confirmDialog({
title: $t('Confirm Payment Method'),
message: $t('By changing the payment method to <strong>Paid By Partner</strong>, the following details will be shared with your partner:<br><br><li>Site/Server Name</li> <li>Plan Name</li><li>Site/Server Active Days</li><br>Are you sure you want to continue?'),
primaryAction: {
label: $t('Change Payment Method'),
variant: 'solid',
onClick: ({ hide }) => {
updatePaymentMode('Paid By Partner');
hide();
},
},
}),
}),
},
];
const paymentMode = computed(() => {
return paymentModeOptions.find((o) => o.value === team.pg.payment_mode);
});
const billingAddressButtonLabel = computed(() => {
return billingDetailsSummary.value ? $t('Edit') : $t('Add Billing Address');
});
// 修改onMounted钩子添加自动设置支付方式为余额支付的逻辑
onMounted(() => {
// 如果当前没有设置支付方式,自动设置为余额支付
nextTick(() => {
if (!team.pg.payment_mode) {
updatePaymentMode('Prepaid Credits');
}
});
});
function payUnpaidInvoices() {
let _unpaidInvoices = unpaidInvoices.data;
if (_unpaidInvoices.length > 1) {
showAddPrepaidCreditsDialog.value = true;
} else {
let invoice = _unpaidInvoices[0];
showAddPrepaidCreditsDialog.value = true;
}
}
const showMessage = ref(false);
function updatePaymentMode(mode) {
showMessage.value = false;
if (mode === 'Paid By Partner' && Boolean(unpaidInvoices.data.length > 0)) {
if (unpaidInvoices.data) {
payUnpaidInvoices();
return;
}
if (currentBillingAmount.value) {
const finalizeInvoicesDialog = defineAsyncComponent(
() => import('./FinalizeInvoicesDialog.vue'),
);
renderDialog(h(finalizeInvoicesDialog));
return;
}
}
if (!changePaymentMode.loading) changePaymentMode.submit({ mode });
}
</script>