jcloud/dashboard/src2/pages/BillingInvoices.vue
2025-06-27 18:50:23 +08:00

249 lines
6.9 KiB
Vue

<template>
<div class="p-5">
<ObjectList :options="options" />
<Dialog
v-model="invoiceDialog"
:options="{ size: '3xl', title: showInvoice?.name }"
>
<template #body-content>
<template v-if="showInvoice">
<div
v-if="showInvoice.status === 'Empty'"
class="text-base text-gray-600"
>
无内容显示
</div>
<InvoiceTable v-else :invoiceId="showInvoice.name" />
</template>
</template>
</Dialog>
<AddPrepaidCreditsDialog
v-if="showBuyPrepaidCreditsDialog"
v-model="showBuyPrepaidCreditsDialog"
:minimumAmount="minimumAmount"
@success="
() => {
showBuyPrepaidCreditsDialog = false;
}
"
/>
</div>
</template>
<script>
import { h } from 'vue';
import { Button } from 'jingrow-ui';
import ObjectList from '../components/ObjectList.vue';
import InvoiceTable from '../components/InvoiceTable.vue';
import { userCurrency, date } from '../utils/format';
import { confirmDialog, icon, renderInDialog } from '../utils/components';
import AddPrepaidCreditsDialog from '../components/billing/AddPrepaidCreditsDialog.vue';
import { dayjsLocal } from '../utils/dayjs';
import router from '../router';
export default {
name: 'BillingInvoices',
props: ['tab'],
components: {
ObjectList,
InvoiceTable,
AddPrepaidCreditsDialog,
},
data() {
return {
invoiceDialog: false,
showInvoice: null,
showBuyPrepaidCreditsDialog: false,
minimumAmount: 0,
};
},
computed: {
options() {
return {
pagetype: 'Invoice',
fields: [
'type',
'invoice_pdf',
'payment_mode',
'stripe_invoice_url',
'due_date',
'period_start',
'period_end',
'mpesa_invoice',
'mpesa_invoice_pdf',
],
filterControls: () => {
return [
{
type: 'select',
label: '类型',
class: !this.$isMobile ? 'w-36' : '',
fieldname: 'type',
options: [
{ label: '', value: '' },
{ label: '订阅', value: 'Subscription' },
{ label: '余额支付', value: 'Prepaid Credits' },
],
},
{
type: 'select',
label: '状态',
class: !this.$isMobile ? 'w-36' : '',
fieldname: 'status',
options: [
{ label: '', value: '' },
{ label: '待开具', value: 'Draft' },
{ label: '已创建', value: 'Invoice Created' },
{ label: '未支付', value: 'Unpaid' },
{ label: '已支付', value: 'Paid' },
{ label: '已退款', value: 'Refunded' },
{ label: '无法收款', value: 'Uncollectible' },
{ label: '已收款', value: 'Collected' },
{ label: '空白', value: 'Empty' },
],
},
];
},
columns: [
{
label: '发票',
fieldname: 'name',
class: 'font-medium',
format(value, row) {
if (row.type == 'Subscription') {
let end = dayjsLocal(row.period_end);
// 修改为 YYYY-MM 格式
return end.format('YYYY-MM');
} else if (row.type == 'Partnership Fees') {
return 'Partnership Fees';
}
return 'Prepaid Credits';
},
width: 0.8,
},
{
label: '状态',
fieldname: 'status',
type: 'Badge',
width: '150px',
},
{
label: '日期',
fieldname: 'due_date',
format(value, row) {
if (row.type == 'Subscription') {
let start = dayjsLocal(row.period_start);
let end = dayjsLocal(row.period_end);
// 确保格式为 YYYY-MM-DD
return `${start.format('YYYY-MM-DD')} - ${end.format('YYYY-MM-DD')}`;
}
// 确保格式为 YYYY-MM-DD
return dayjsLocal(value).format('YYYY-MM-DD');
},
},
{
label: '总计',
fieldname: 'total',
format: this.formatCurrency,
align: 'right',
width: 0.6,
},
{
label: '已支付金额',
fieldname: 'amount_paid',
format: this.formatCurrency,
align: 'right',
width: 0.6,
},
{
label: '应付金额',
fieldname: 'amount_due',
format: this.formatCurrency,
align: 'right',
width: 0.6,
},
{
label: '',
type: 'Button',
align: 'right',
Button: ({ row }) => {
if (row.invoice_pdf || row.mpesa_invoice_pdf) {
return {
label: '下载发票',
slots: {
prefix: icon('download'),
},
onClick: () => {
if (row.mpesa_invoice_pdf) {
window.open(row.mpesa_invoice_pdf);
} else {
window.open(row.invoice_pdf);
}
},
};
}
if (row.status === 'Unpaid' && row.amount_due > 0) {
return {
label: '立即支付',
slots: {
prefix: icon('external-link'),
},
onClick: (e) => {
e.stopPropagation();
if (row.stripe_invoice_url && row.payment_mode == 'Card') {
window.open(
`/api/action/jcloud.api.client.run_pg_method?dt=Invoice&dn=${row.name}&method=stripe_payment_url`,
);
} else {
this.showBuyPrepaidCreditsDialog = true;
this.minimumAmount = row.amount_due;
}
},
};
}
},
prefix(row) {
if (row.stripe_payment_failed && row.status !== 'Paid') {
return h(Button, {
variant: 'ghost',
theme: 'red',
icon: 'alert-circle',
onClick(e) {
e.stopPropagation();
confirmDialog({
title: '支付失败',
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>`,
primaryAction: {
label: '更改支付方式',
variant: 'solid',
onClick: ({ hide }) => {
hide();
router.push({
name: 'BillingPaymentMethods',
});
},
},
});
},
});
}
},
},
],
orderBy: 'due_date desc, creation desc',
onRowClick: (row) => {
this.showInvoice = row;
this.invoiceDialog = true;
},
};
},
},
methods: {
formatCurrency(value) {
if (value === 0) {
return '';
}
return userCurrency(value);
},
},
};
</script>