jcloud/dashboard/src2/pages/BillingPaymentMethods.vue
2025-04-12 17:39:38 +08:00

209 lines
4.9 KiB
Vue

<template>
<div class="p-5">
<ObjectList :options="options" />
</div>
</template>
<script>
import { defineAsyncComponent, h } from 'vue';
import ObjectList from '../components/ObjectList.vue';
import { Badge, FeatherIcon, Tooltip } from 'jingrow-ui';
import { toast } from 'vue-sonner';
import { confirmDialog, renderDialog, icon } from '../utils/components';
export default {
name: 'BillingPaymentMethods',
props: ['tab'],
components: {
ObjectList
},
computed: {
options() {
return {
pagetype: 'Stripe Payment Method',
fields: [
'name',
'is_default',
'expiry_month',
'expiry_year',
'brand',
'stripe_mandate_id'
],
emptyStateMessage: '未添加任何卡片',
columns: [
{
label: '卡片姓名',
fieldname: 'name_on_card'
},
{
label: '卡片',
fieldname: 'last_4',
width: 1.5,
format(value) {
return `•••• ${value}`;
},
prefix: row => {
return this.cardBrandIcon(row.brand);
},
suffix(row) {
if (row.is_default) {
return h(
Badge,
{
theme: 'green'
},
() => '默认'
);
}
}
},
{
label: '有效期',
width: 0.5,
format(value, row) {
return `${row.expiry_month}/${row.expiry_year}`;
}
},
{
label: '授权',
type: 'Component',
width: 1,
align: 'center',
component({ row }) {
if (row.stripe_mandate_id) {
return h(FeatherIcon, {
name: 'check-circle',
class: 'h-4 w-4 text-green-600'
});
}
}
},
{
label: '',
type: 'Component',
align: 'right',
component({ row }) {
if (row.is_default && row.stripe_payment_method) {
return h(
Tooltip,
{
text: '此卡上次支付失败。请使用其他卡片。'
},
() =>
h(FeatherIcon, {
name: 'alert-circle',
class: 'h-4 w-4 text-red-600'
})
);
}
}
},
{
label: '',
fieldname: 'creation',
type: 'Timestamp',
align: 'right'
}
],
rowActions: ({ listResource, row }) => {
return [
{
label: '设为默认',
onClick: () => {
toast.promise(
listResource.runDocMethod.submit({
method: 'set_default',
name: row.name
}),
{
loading: '正在设为默认...',
success: '默认卡片已设置',
error: '无法设置默认卡片'
}
);
},
condition: () => !row.is_default
},
{
label: '移除',
onClick: () => {
if (row.is_default && this.$team.pg.payment_mode === 'Card') {
toast.error('无法移除默认卡片');
return;
}
confirmDialog({
title: '移除卡片',
message: '确定要移除此卡片吗?',
onSuccess: ({ hide }) => {
toast.promise(
listResource.delete.submit(row.name, {
onSuccess() {
hide();
}
}),
{
loading: '正在移除卡片...',
success: '卡片已移除',
error: error =>
error.messages?.length
? error.messages.join('\n')
: error.message || '无法移除卡片'
}
);
}
});
}
}
];
},
orderBy: 'creation desc',
primaryAction() {
return {
label: '添加卡片',
slots: {
prefix: icon('plus')
},
onClick: () => {
let StripeCardDialog = defineAsyncComponent(() =>
import('../components/StripeCardDialog.vue')
);
renderDialog(StripeCardDialog);
}
};
}
};
}
},
methods: {
formatCurrency(value) {
if (value === 0) {
return '';
}
return this.$format.userCurrency(value);
},
cardBrandIcon(brand) {
let component = {
'master-card': defineAsyncComponent(() =>
import('@/components/icons/cards/MasterCard.vue')
),
visa: defineAsyncComponent(() =>
import('@/components/icons/cards/Visa.vue')
),
amex: defineAsyncComponent(() =>
import('@/components/icons/cards/Amex.vue')
),
jcb: defineAsyncComponent(() =>
import('@/components/icons/cards/JCB.vue')
),
generic: defineAsyncComponent(() =>
import('@/components/icons/cards/Generic.vue')
),
'union-pay': defineAsyncComponent(() =>
import('@/components/icons/cards/UnionPay.vue')
)
}[brand || 'generic'];
return h(component, { class: 'h-4 w-6' });
}
}
};
</script>