部分页面实现中文替换为支持翻译的英文1

This commit is contained in:
jingrow 2025-12-28 23:36:50 +08:00
parent 62922ef899
commit a8e68da076
6 changed files with 73 additions and 71 deletions

View File

@ -11,7 +11,7 @@
:loading="$resources.createAlipayOrder.loading || loading" :loading="$resources.createAlipayOrder.loading || loading"
@click="processAlipayPayment" @click="processAlipayPayment"
> >
使用支付宝支付 {{ $t('Pay with Alipay') }}
</Button> </Button>
</div> </div>
</div> </div>
@ -49,18 +49,18 @@ export default {
}, },
validate() { validate() {
if (this.amount <= 0) { if (this.amount <= 0) {
throw new DashboardError('支付金额必须大于0'); throw new DashboardError(this.$t('Payment amount must be greater than 0'));
} }
if (this.amount < this.minimumAmount) { if (this.amount < this.minimumAmount) {
throw new DashboardError('金额低于最低要求金额'); throw new DashboardError(this.$t('Amount is below the minimum required amount'));
} }
}, },
onSuccess(response) { onSuccess(response) {
window.open(response.payment_url, '_blank'); window.open(response.payment_url, '_blank');
toast.success('支付页面已在新窗口打开'); toast.success(this.$t('Payment page opened in new window'));
// // Optional: check payment status
// this.checkPaymentStatus(response.order_id); // this.checkPaymentStatus(response.order_id);
} }
}; };
@ -71,7 +71,7 @@ export default {
this.$resources.createAlipayOrder.submit(); this.$resources.createAlipayOrder.submit();
}, },
// // Optional: method to check payment status
checkPaymentStatus(orderId) { checkPaymentStatus(orderId) {
const checkInterval = setInterval(() => { const checkInterval = setInterval(() => {
this.$call('jcloud.api.billing.check_payment_status', { this.$call('jcloud.api.billing.check_payment_status', {
@ -80,15 +80,15 @@ export default {
}).then(result => { }).then(result => {
if (result && result.status === 'Success') { if (result && result.status === 'Success') {
clearInterval(checkInterval); clearInterval(checkInterval);
toast.success('支付成功!账户已充值'); toast.success(this.$t('Payment successful! Account has been recharged'));
this.$emit('payment-success'); this.$emit('payment-success');
} }
}).catch(err => { }).catch(err => {
console.error('检查支付状态出错:', err); console.error('Error checking payment status:', err);
}); });
}, 3000); }, 3000);
// 5 // Stop checking after 5 minutes
setTimeout(() => { setTimeout(() => {
clearInterval(checkInterval); clearInterval(checkInterval);
}, 300000); }, 300000);

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<FormControl <FormControl
:label="`支付金额`" :label="$t('Payment Amount')"
class="mb-3" class="mb-3"
v-model.number="creditsToBuy" v-model.number="creditsToBuy"
name="amount" name="amount"
@ -19,7 +19,7 @@
</div> </div>
<div class="mt-4"> <div class="mt-4">
<div class="text-xs text-gray-600">支付方式</div> <div class="text-xs text-gray-600">{{ $t('Payment Method') }}</div>
<div class="mt-1.5 grid grid-cols-1 gap-2 sm:grid-cols-2"> <div class="mt-1.5 grid grid-cols-1 gap-2 sm:grid-cols-2">
<label <label
class="payment-option" class="payment-option"
@ -54,7 +54,7 @@
</div> </div>
<div class="text-xs text-gray-500 mt-2 mb-4" v-if="$team.pg.currency === 'CNY'"> <div class="text-xs text-gray-500 mt-2 mb-4" v-if="$team.pg.currency === 'CNY'">
使用{{ paymentGateway === 'Alipay' ? '支付宝' : '微信' }}支付快速便捷余额将在支付成功后立即到账 {{ $t('Pay with {method}, fast and convenient. Balance will be credited immediately after successful payment.', { method: paymentGateway === 'Alipay' ? $t('Alipay') : $t('WeChat Pay') }) }}
</div> </div>
<BuyPrepaidCreditsAlipay <BuyPrepaidCreditsAlipay
@ -91,7 +91,7 @@ export default {
}, },
data() { data() {
return { return {
paymentGateway: 'Alipay', // 使 paymentGateway: 'Alipay', // Default to Alipay
creditsToBuy: this.minimumAmount || 0, creditsToBuy: this.minimumAmount || 0,
amountError: '' amountError: ''
}; };
@ -208,7 +208,7 @@ export default {
background-color: #22AC38; background-color: #22AC38;
} }
/* 响应式设计 */ /* Responsive design */
@media (max-width: 640px) { @media (max-width: 640px) {
.payment-options { .payment-options {
flex-direction: column; flex-direction: column;

View File

@ -11,11 +11,11 @@
:loading="$resources.createWeChatPayOrder.loading || loading" :loading="$resources.createWeChatPayOrder.loading || loading"
@click="processWeChatPayment" @click="processWeChatPayment"
> >
使用微信支付 {{ $t('Pay with WeChat Pay') }}
</Button> </Button>
</div> </div>
<!-- 使用Teleport将弹窗移到body下 --> <!-- Use Teleport to move modal to body -->
<Teleport to="body"> <Teleport to="body">
<div v-if="showQRCode" class="wechat-qrcode-overlay"> <div v-if="showQRCode" class="wechat-qrcode-overlay">
<div class="wechat-qrcode-modal"> <div class="wechat-qrcode-modal">
@ -31,20 +31,20 @@
</div> </div>
<div class="text-center mb-5"> <div class="text-center mb-5">
<div class="text-sm text-gray-600 mb-2">扫一扫付款</div> <div class="text-sm text-gray-600 mb-2">{{ $t('Scan to Pay (CNY)') }}</div>
<div class="text-3xl font-bold text-[#FF0036]">{{ amount }} </div> <div class="text-3xl font-bold text-[#FF0036]">{{ amount }} {{ $t('CNY') }}</div>
</div> </div>
<div class="flex justify-center"> <div class="flex justify-center">
<div v-if="qrCodeImage" class="qrcode-container"> <div v-if="qrCodeImage" class="qrcode-container">
<img :src="qrCodeImage" alt="微信支付二维码" class="qrcode-image" /> <img :src="qrCodeImage" :alt="$t('WeChat Pay QR Code')" class="qrcode-image" />
</div> </div>
<div v-else class="qrcode-loading"></div> <div v-else class="qrcode-loading"></div>
</div> </div>
<div class="mt-5 text-center text-sm"> <div class="mt-5 text-center text-sm">
<div class="text-gray-600">请使用微信扫描二维码完成支付</div> <div class="text-gray-600">{{ $t('Please scan the QR code with WeChat to complete payment') }}</div>
<div class="text-gray-500 mt-1">二维码有效期 15 分钟</div> <div class="text-gray-500 mt-1">{{ $t('QR code valid for 15 minutes') }}</div>
</div> </div>
</div> </div>
</div> </div>
@ -55,7 +55,7 @@
<script> <script>
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import { DashboardError } from '../utils/error'; import { DashboardError } from '../utils/error';
import { Teleport } from 'vue'; // Teleport import { Teleport } from 'vue'; // Ensure Teleport is imported
import WeChatPayLogo from '../logo/WeChatPayLogo.vue'; import WeChatPayLogo from '../logo/WeChatPayLogo.vue';
export default { export default {
@ -94,14 +94,14 @@ export default {
}, },
validate() { validate() {
if (this.amount <= 0) { if (this.amount <= 0) {
throw new DashboardError('支付金额必须大于0'); throw new DashboardError(this.$t('Payment amount must be greater than 0'));
} }
if (this.amount < this.minimumAmount) { if (this.amount < this.minimumAmount) {
throw new DashboardError('金额低于最低要求金额'); throw new DashboardError(this.$t('Amount is below the minimum required amount'));
} }
}, },
onSuccess(response) { onSuccess(response) {
console.log('微信支付响应:', response); console.log('WeChat Pay response:', response);
this.orderId = response.order_id; this.orderId = response.order_id;
this.qrCodeUrl = response.qr_code_url; this.qrCodeUrl = response.qr_code_url;
this.qrCodeImage = response.qr_code_image; this.qrCodeImage = response.qr_code_image;
@ -118,11 +118,12 @@ export default {
payment_type: 'wechatpay' payment_type: 'wechatpay'
}, },
onSuccess(data) { onSuccess(data) {
// Check for success status (handles both English and Chinese status values from backend)
if (data && (data.status === 'Success' || data.status === '已支付' || data.status === '交易成功')) { if (data && (data.status === 'Success' || data.status === '已支付' || data.status === '交易成功')) {
this.stopPaymentCheck(); this.stopPaymentCheck();
this.closeQRCode(); this.closeQRCode();
this.$emit('payment-success'); this.$emit('payment-success');
toast.success('支付成功'); toast.success(this.$t('Payment successful'));
} }
} }
}; };
@ -181,7 +182,7 @@ export default {
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15); box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
} }
/* 全新的弹窗样式 */ /* Modal overlay styles */
.wechat-qrcode-overlay { .wechat-qrcode-overlay {
position: fixed; position: fixed;
top: 0; top: 0;
@ -192,7 +193,7 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 99999; /* 极高的z-index确保在最上层 */ z-index: 99999; /* Very high z-index to ensure it's on top */
} }
.wechat-qrcode-modal { .wechat-qrcode-modal {

View File

@ -4,9 +4,9 @@
<div class="flex flex-col gap-2.5 px-4 py-3"> <div class="flex flex-col gap-2.5 px-4 py-3">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<div class="text-lg font-semibold">账单费用</div> <div class="text-lg font-semibold">{{ t('Billing Charges') }}</div>
<div class="text-gray-700"> <div class="text-gray-700">
<span>下次扣款日期 </span> <span>{{ t('Next billing date') }} </span>
<span>{{ currentMonthEnd() }}</span> <span>{{ currentMonthEnd() }}</span>
</div> </div>
</div> </div>
@ -15,14 +15,14 @@
<div class="flex gap-2 text-gray-700"> <div class="flex gap-2 text-gray-700">
<i-lucide-credit-card class="h-4 w-4" /> <i-lucide-credit-card class="h-4 w-4" />
<div> <div>
<span>当前账单金额为 </span> <span>{{ t('Current billing amount is') }} </span>
<span class="font-medium text-gray-900"> <span class="font-medium text-gray-900">
{{ currency }} {{ currentBillingAmount?.toFixed(2) || '0.00' }} {{ currency }} {{ currentBillingAmount?.toFixed(2) || '0.00' }}
</span> </span>
</div> </div>
</div> </div>
<div> <div>
<Button label="查看账单明细" @click="showInvoiceDialog = true" /> <Button :label="t('View Invoice Details')" @click="showInvoiceDialog = true" />
</div> </div>
</div> </div>
</div> </div>
@ -33,12 +33,12 @@
<div class="flex h-7 items-center gap-2 text-gray-800"> <div class="flex h-7 items-center gap-2 text-gray-800">
<i-lucide-receipt class="h-4 w-4" /> <i-lucide-receipt class="h-4 w-4" />
<div> <div>
<span>未支付金额为 </span> <span>{{ t('Unpaid amount is') }} </span>
<span>{{ currency }} {{ unpaidAmount.data?.toFixed(2) }}</span> <span>{{ currency }} {{ unpaidAmount.data?.toFixed(2) }}</span>
</div> </div>
</div> </div>
<div> <div>
<Button variant="solid" label="立即支付" @click="payNow" /> <Button variant="solid" :label="t('Pay Now')" @click="payNow" />
</div> </div>
</div> </div>
</div> </div>
@ -56,8 +56,10 @@ import UpcomingInvoiceDialog from './UpcomingInvoiceDialog.vue';
import { Button, createResource } from 'jingrow-ui'; import { Button, createResource } from 'jingrow-ui';
import { ref, computed, inject } from 'vue'; import { ref, computed, inject } from 'vue';
import { confirmDialog } from '../../utils/components'; import { confirmDialog } from '../../utils/components';
import { useI18n } from '../../composables/useI18n';
import router from '../../router'; import router from '../../router';
const { t } = useI18n();
const team = inject('team'); const team = inject('team');
const { currentBillingAmount, upcomingInvoice, unpaidInvoices } = const { currentBillingAmount, upcomingInvoice, unpaidInvoices } =
inject('billing'); inject('billing');
@ -76,7 +78,7 @@ const unpaidAmount = createResource({
const currentMonthEnd = () => { const currentMonthEnd = () => {
const date = new Date(); const date = new Date();
const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0); const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
return lastDay.toLocaleDateString('zh-CN', { return lastDay.toLocaleDateString('en-US', {
year: 'numeric', year: 'numeric',
month: 'long', month: 'long',
day: 'numeric' day: 'numeric'
@ -96,11 +98,10 @@ function payUnpaidInvoices() {
showAddPrepaidCreditsDialog.value = true; showAddPrepaidCreditsDialog.value = true;
} else { } else {
confirmDialog({ confirmDialog({
title: '多张未支付发票', title: t('Multiple Unpaid Invoices'),
message: message: t('You have multiple unpaid invoices. Please pay them from the invoices page.'),
'您有多张未支付发票。请从发票页面支付它们',
primaryAction: { primaryAction: {
label: '前往发票', label: t('Go to Invoices'),
variant: 'solid', variant: 'solid',
onClick: ({ hide }) => { onClick: ({ hide }) => {
router.push({ name: 'BillingInvoices' }); router.push({ name: 'BillingInvoices' });

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<FormControl <FormControl
:label="`支付金额`" :label="$t('Payment Amount')"
class="mb-3" class="mb-3"
v-model.number="creditsToBuy" v-model.number="creditsToBuy"
name="amount" name="amount"
@ -18,7 +18,7 @@
</div> </div>
<div class="mt-4"> <div class="mt-4">
<div class="text-xs text-gray-600">支付方式</div> <div class="text-xs text-gray-600">{{ $t('Payment Method') }}</div>
<div class="mt-1.5 grid grid-cols-1 gap-2 sm:grid-cols-2"> <div class="mt-1.5 grid grid-cols-1 gap-2 sm:grid-cols-2">
<label <label
class="payment-option" class="payment-option"
@ -53,7 +53,7 @@
</div> </div>
<div class="text-xs text-gray-500 mt-2 mb-4" v-if="$team.pg.currency === 'CNY'"> <div class="text-xs text-gray-500 mt-2 mb-4" v-if="$team.pg.currency === 'CNY'">
使用{{ paymentGateway === 'Alipay' ? '支付宝' : '微信' }}支付快速便捷余额将在支付成功后立即到账 {{ $t('Pay with {method}, fast and convenient. Balance will be credited immediately after successful payment.', { method: paymentGateway === 'Alipay' ? $t('Alipay') : $t('WeChat Pay') }) }}
</div> </div>
<BuyPrepaidCreditsAlipay <BuyPrepaidCreditsAlipay
@ -86,7 +86,7 @@ export default {
}, },
data() { data() {
return { return {
paymentGateway: 'Alipay', // 使 paymentGateway: 'Alipay', // Default to Alipay
creditsToBuy: this.minimumAmount creditsToBuy: this.minimumAmount
}; };
}, },
@ -215,7 +215,7 @@ export default {
background-color: #22AC38; background-color: #22AC38;
} }
/* 响应式设计 */ /* Responsive design */
@media (max-width: 640px) { @media (max-width: 640px) {
.payment-options { .payment-options {
flex-direction: column; flex-direction: column;

View File

@ -11,7 +11,7 @@
v-if="showInvoice.status === 'Empty'" v-if="showInvoice.status === 'Empty'"
class="text-base text-gray-600" class="text-base text-gray-600"
> >
无内容显示 {{ $t('No content to display') }}
</div> </div>
<InvoiceTable v-else :invoiceId="showInvoice.name" /> <InvoiceTable v-else :invoiceId="showInvoice.name" />
</template> </template>
@ -75,43 +75,43 @@ export default {
return [ return [
{ {
type: 'select', type: 'select',
label: '类型', label: this.$t('Type'),
class: !this.$isMobile ? 'w-36' : '', class: !this.$isMobile ? 'w-36' : '',
fieldname: 'type', fieldname: 'type',
options: [ options: [
{ label: '', value: '' }, { label: '', value: '' },
{ label: '订阅', value: 'Subscription' }, { label: this.$t('Subscription'), value: 'Subscription' },
{ label: '余额支付', value: 'Prepaid Credits' }, { label: this.$t('Prepaid Credits'), value: 'Prepaid Credits' },
], ],
}, },
{ {
type: 'select', type: 'select',
label: '状态', label: this.$t('Status'),
class: !this.$isMobile ? 'w-36' : '', class: !this.$isMobile ? 'w-36' : '',
fieldname: 'status', fieldname: 'status',
options: [ options: [
{ label: '', value: '' }, { label: '', value: '' },
{ label: '待开具', value: 'Draft' }, { label: this.$t('Draft'), value: 'Draft' },
{ label: '已创建', value: 'Invoice Created' }, { label: this.$t('Invoice Created'), value: 'Invoice Created' },
{ label: '未支付', value: 'Unpaid' }, { label: this.$t('Unpaid'), value: 'Unpaid' },
{ label: '已支付', value: 'Paid' }, { label: this.$t('Paid'), value: 'Paid' },
{ label: '已退款', value: 'Refunded' }, { label: this.$t('Refunded'), value: 'Refunded' },
{ label: '无法收款', value: 'Uncollectible' }, { label: this.$t('Uncollectible'), value: 'Uncollectible' },
{ label: '已收款', value: 'Collected' }, { label: this.$t('Collected'), value: 'Collected' },
{ label: '空白', value: 'Empty' }, { label: this.$t('Empty'), value: 'Empty' },
], ],
}, },
]; ];
}, },
columns: [ columns: [
{ {
label: '发票', label: this.$t('Invoice'),
fieldname: 'name', fieldname: 'name',
class: 'font-medium', class: 'font-medium',
format(value, row) { format(value, row) {
if (row.type == 'Subscription') { if (row.type == 'Subscription') {
let end = dayjsLocal(row.period_end); let end = dayjsLocal(row.period_end);
// YYYY-MM // Format as YYYY-MM
return end.format('YYYY-MM'); return end.format('YYYY-MM');
} else if (row.type == 'Partnership Fees') { } else if (row.type == 'Partnership Fees') {
return 'Partnership Fees'; return 'Partnership Fees';
@ -121,41 +121,41 @@ export default {
width: 0.8, width: 0.8,
}, },
{ {
label: '状态', label: this.$t('Status'),
fieldname: 'status', fieldname: 'status',
type: 'Badge', type: 'Badge',
width: '150px', width: '150px',
}, },
{ {
label: '日期', label: this.$t('Date'),
fieldname: 'due_date', fieldname: 'due_date',
format(value, row) { format(value, row) {
if (row.type == 'Subscription') { if (row.type == 'Subscription') {
let start = dayjsLocal(row.period_start); let start = dayjsLocal(row.period_start);
let end = dayjsLocal(row.period_end); let end = dayjsLocal(row.period_end);
// YYYY-MM-DD // Format as YYYY-MM-DD
return `${start.format('YYYY-MM-DD')} - ${end.format('YYYY-MM-DD')}`; return `${start.format('YYYY-MM-DD')} - ${end.format('YYYY-MM-DD')}`;
} }
// YYYY-MM-DD // Format as YYYY-MM-DD
return dayjsLocal(value).format('YYYY-MM-DD'); return dayjsLocal(value).format('YYYY-MM-DD');
}, },
}, },
{ {
label: '总计', label: this.$t('Total'),
fieldname: 'total', fieldname: 'total',
format: this.formatCurrency, format: this.formatCurrency,
align: 'right', align: 'right',
width: 0.6, width: 0.6,
}, },
{ {
label: '已支付金额', label: this.$t('Amount Paid'),
fieldname: 'amount_paid', fieldname: 'amount_paid',
format: this.formatCurrency, format: this.formatCurrency,
align: 'right', align: 'right',
width: 0.6, width: 0.6,
}, },
{ {
label: '应付金额', label: this.$t('Amount Due'),
fieldname: 'amount_due', fieldname: 'amount_due',
format: this.formatCurrency, format: this.formatCurrency,
align: 'right', align: 'right',
@ -168,7 +168,7 @@ export default {
Button: ({ row }) => { Button: ({ row }) => {
if (row.invoice_pdf || row.mpesa_invoice_pdf) { if (row.invoice_pdf || row.mpesa_invoice_pdf) {
return { return {
label: '下载发票', label: this.$t('Download Invoice'),
slots: { slots: {
prefix: icon('download'), prefix: icon('download'),
}, },
@ -183,7 +183,7 @@ export default {
} }
if (row.status === 'Unpaid' && row.amount_due > 0) { if (row.status === 'Unpaid' && row.amount_due > 0) {
return { return {
label: '立即支付', label: this.$t('Pay Now'),
slots: { slots: {
prefix: icon('external-link'), prefix: icon('external-link'),
}, },
@ -210,10 +210,10 @@ export default {
onClick(e) { onClick(e) {
e.stopPropagation(); e.stopPropagation();
confirmDialog({ confirmDialog({
title: '支付失败', title: this.$t('Payment Failed'),
message: `<div class="space-y-4"><p class="text-base">您使用尾号为 <strong>${row.stripe_payment_failed_card}</strong> 的卡支付此发票失败,原因如下:</p><div class="text-sm font-mono text-gray-600 rounded p-2 bg-gray-100">${row.stripe_payment_error}</div><p class="text-base">请更改您的支付方式以支付此发票。</p></div>`, message: `<div class="space-y-4"><p class="text-base">${this.$t('Payment for this invoice failed using card ending in')} <strong>${row.stripe_payment_failed_card}</strong>. ${this.$t('Reason:')}</p><div class="text-sm font-mono text-gray-600 rounded p-2 bg-gray-100">${row.stripe_payment_error}</div><p class="text-base">${this.$t('Please update your payment method to pay this invoice.')}</p></div>`,
primaryAction: { primaryAction: {
label: '更改支付方式', label: this.$t('Update Payment Method'),
variant: 'solid', variant: 'solid',
onClick: ({ hide }) => { onClick: ({ hide }) => {
hide(); hide();