feat(payment): add complete payment system with multi-provider support
Add a full payment and subscription system supporting EasyPay (Alipay/WeChat), Stripe, and direct Alipay/WeChat Pay providers with multi-instance load balancing.
This commit is contained in:
75
frontend/src/components/admin/payment/PaymentMethodChart.vue
Normal file
75
frontend/src/components/admin/payment/PaymentMethodChart.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="card p-4">
|
||||
<h3 class="mb-4 text-sm font-semibold text-gray-900 dark:text-white">
|
||||
{{ t('payment.admin.paymentDistribution') }}
|
||||
</h3>
|
||||
<div
|
||||
v-if="!methods?.length"
|
||||
class="flex h-32 items-center justify-center text-sm text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ t('payment.admin.noData') }}
|
||||
</div>
|
||||
<div v-else class="space-y-3">
|
||||
<div v-for="method in methods" :key="method.type" class="space-y-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span :class="['inline-block h-3 w-3 rounded-full', colorMap[method.type] || 'bg-gray-400']"></span>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">
|
||||
{{ t('payment.methods.' + method.type, method.type) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
${{ method.amount.toFixed(2) }}
|
||||
</span>
|
||||
<span class="ml-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
({{ method.count }})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-2 w-full overflow-hidden rounded-full bg-gray-100 dark:bg-dark-700">
|
||||
<div
|
||||
:class="['h-full rounded-full transition-all', barColorMap[method.type] || 'bg-gray-400']"
|
||||
:style="{ width: barWidth(method.amount) + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
methods: { type: string; amount: number; count: number }[]
|
||||
}>()
|
||||
|
||||
const colorMap: Record<string, string> = {
|
||||
alipay: 'bg-blue-500',
|
||||
wxpay: 'bg-green-500',
|
||||
alipay_direct: 'bg-blue-400',
|
||||
wxpay_direct: 'bg-green-400',
|
||||
stripe: 'bg-purple-500',
|
||||
}
|
||||
|
||||
const barColorMap: Record<string, string> = {
|
||||
alipay: 'bg-blue-500',
|
||||
wxpay: 'bg-green-500',
|
||||
alipay_direct: 'bg-blue-400',
|
||||
wxpay_direct: 'bg-green-400',
|
||||
stripe: 'bg-purple-500',
|
||||
}
|
||||
|
||||
const maxAmount = computed(() => {
|
||||
if (!props.methods?.length) return 1
|
||||
return Math.max(...props.methods.map(m => m.amount), 1)
|
||||
})
|
||||
|
||||
function barWidth(amount: number): number {
|
||||
return Math.min((amount / maxAmount.value) * 100, 100)
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user