249 lines
6.9 KiB
Vue
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> |