mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-22 23:08:25 +00:00
feat(payment): UI improvements for admin payment pages
- Plan table: group column shows GroupBadge with platform color instead of raw ID, column header changed to "分组" - Plan dialog: group info preview uses GroupBadge, removes redundant platform/rate fields - Action buttons: all payment admin tables (plans, orders, providers) now use vertically stacked icon+text buttons matching the app style - Added i18n keys: payment.admin.group, common.view
This commit is contained in:
@@ -83,37 +83,37 @@
|
||||
</template>
|
||||
|
||||
<template #cell-actions="{ row }">
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
@click="emit('detail', row)"
|
||||
class="btn-icon text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
:title="t('common.view')"
|
||||
class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-gray-50 hover:text-gray-700 dark:hover:bg-gray-800/50 dark:hover:text-gray-300"
|
||||
>
|
||||
<Icon name="eye" size="sm" />
|
||||
<span class="text-xs">{{ t('common.view') }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="row.status === 'PENDING'"
|
||||
@click="emit('cancel', row)"
|
||||
class="btn-icon text-yellow-500 hover:text-yellow-700"
|
||||
:title="t('payment.orders.cancel')"
|
||||
class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-yellow-50 hover:text-yellow-600 dark:hover:bg-yellow-900/20 dark:hover:text-yellow-400"
|
||||
>
|
||||
<Icon name="x" size="sm" />
|
||||
<span class="text-xs">{{ t('payment.orders.cancel') }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="row.status === 'FAILED'"
|
||||
@click="emit('retry', row)"
|
||||
class="btn-icon text-blue-500 hover:text-blue-700"
|
||||
:title="t('payment.admin.retry')"
|
||||
class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-blue-50 hover:text-blue-600 dark:hover:bg-blue-900/20 dark:hover:text-blue-400"
|
||||
>
|
||||
<Icon name="refresh" size="sm" />
|
||||
<span class="text-xs">{{ t('payment.admin.retry') }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="canRefund(row)"
|
||||
@click="emit('refund', row)"
|
||||
class="btn-icon text-red-500 hover:text-red-700"
|
||||
:title="t('payment.admin.refund')"
|
||||
class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20 dark:hover:text-red-400"
|
||||
>
|
||||
<Icon name="dollar" size="sm" />
|
||||
<span class="text-xs">{{ t('payment.admin.refund') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -46,12 +46,14 @@
|
||||
<div class="flex items-center gap-4">
|
||||
<ToggleSwitch :label="t('common.enabled')" :checked="provider.enabled" @toggle="emit('toggleField', 'enabled')" />
|
||||
<ToggleSwitch :label="t('admin.settings.payment.refundEnabled')" :checked="provider.refund_enabled" @toggle="emit('toggleField', 'refund_enabled')" />
|
||||
<div class="flex items-center gap-1 border-l border-gray-200 pl-3 dark:border-dark-600">
|
||||
<button @click="emit('edit')" class="btn-icon text-blue-500 hover:text-blue-700" :title="t('common.edit')">
|
||||
<div class="flex items-center gap-2 border-l border-gray-200 pl-3 dark:border-dark-600">
|
||||
<button @click="emit('edit')" class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-blue-50 hover:text-blue-600 dark:hover:bg-blue-900/20 dark:hover:text-blue-400">
|
||||
<Icon name="edit" size="sm" />
|
||||
<span class="text-xs">{{ t('common.edit') }}</span>
|
||||
</button>
|
||||
<button @click="emit('delete')" class="btn-icon text-red-500 hover:text-red-700" :title="t('common.delete')">
|
||||
<button @click="emit('delete')" class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20 dark:hover:text-red-400">
|
||||
<Icon name="trash" size="sm" />
|
||||
<span class="text-xs">{{ t('common.delete') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -306,6 +306,7 @@ export default {
|
||||
saving: 'Saving...',
|
||||
selectedCount: '({count} selected)',
|
||||
refresh: 'Refresh',
|
||||
view: 'View',
|
||||
settings: 'Settings',
|
||||
chooseFile: 'Choose File',
|
||||
notAvailable: 'N/A',
|
||||
@@ -5199,6 +5200,7 @@ export default {
|
||||
forSale: 'For Sale',
|
||||
onSale: 'On Sale',
|
||||
offSale: 'Off Sale',
|
||||
group: 'Group',
|
||||
groupId: 'Group ID',
|
||||
features: 'Features',
|
||||
featuresHint: 'One feature per line',
|
||||
|
||||
@@ -306,6 +306,7 @@ export default {
|
||||
saving: '保存中...',
|
||||
selectedCount: '(已选 {count} 个)',
|
||||
refresh: '刷新',
|
||||
view: '查看',
|
||||
settings: '设置',
|
||||
chooseFile: '选择文件',
|
||||
notAvailable: '不可用',
|
||||
@@ -5396,6 +5397,7 @@ export default {
|
||||
forSale: '上架状态',
|
||||
onSale: '上架',
|
||||
offSale: '下架',
|
||||
group: '分组',
|
||||
groupId: '分组 ID',
|
||||
features: '功能特性',
|
||||
featuresHint: '每行一个特性',
|
||||
|
||||
@@ -38,11 +38,23 @@
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{ formatDateTime(value) }}</span>
|
||||
</template>
|
||||
<template #cell-actions="{ row }">
|
||||
<div class="flex items-center gap-1">
|
||||
<button @click="showOrderDetail(row)" class="btn-icon text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200" :title="t('common.view')"><Icon name="eye" size="sm" /></button>
|
||||
<button v-if="row.status === 'PENDING'" @click="handleCancelOrder(row)" class="btn-icon text-yellow-500 hover:text-yellow-700" :title="t('payment.orders.cancel')"><Icon name="x" size="sm" /></button>
|
||||
<button v-if="row.status === 'FAILED'" @click="handleRetryOrder(row)" class="btn-icon text-blue-500 hover:text-blue-700" :title="t('payment.admin.retry')"><Icon name="refresh" size="sm" /></button>
|
||||
<button v-if="canRefund(row)" @click="openRefundDialog(row)" class="btn-icon text-red-500 hover:text-red-700" :title="t('payment.admin.refund')"><Icon name="dollar" size="sm" /></button>
|
||||
<div class="flex items-center gap-2">
|
||||
<button @click="showOrderDetail(row)" class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-gray-50 hover:text-gray-700 dark:hover:bg-gray-800/50 dark:hover:text-gray-300">
|
||||
<Icon name="eye" size="sm" />
|
||||
<span class="text-xs">{{ t('common.view') }}</span>
|
||||
</button>
|
||||
<button v-if="row.status === 'PENDING'" @click="handleCancelOrder(row)" class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-yellow-50 hover:text-yellow-600 dark:hover:bg-yellow-900/20 dark:hover:text-yellow-400">
|
||||
<Icon name="x" size="sm" />
|
||||
<span class="text-xs">{{ t('payment.orders.cancel') }}</span>
|
||||
</button>
|
||||
<button v-if="row.status === 'FAILED'" @click="handleRetryOrder(row)" class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-blue-50 hover:text-blue-600 dark:hover:bg-blue-900/20 dark:hover:text-blue-400">
|
||||
<Icon name="refresh" size="sm" />
|
||||
<span class="text-xs">{{ t('payment.admin.retry') }}</span>
|
||||
</button>
|
||||
<button v-if="canRefund(row)" @click="openRefundDialog(row)" class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20 dark:hover:text-red-400">
|
||||
<Icon name="dollar" size="sm" />
|
||||
<span class="text-xs">{{ t('payment.admin.refund') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</DataTable>
|
||||
|
||||
@@ -49,9 +49,15 @@
|
||||
</button>
|
||||
</template>
|
||||
<template #cell-actions="{ row }">
|
||||
<div class="flex items-center gap-1">
|
||||
<button @click="openPlanEdit(row)" class="btn-icon text-blue-500 hover:text-blue-700" :title="t('common.edit')"><Icon name="edit" size="sm" /></button>
|
||||
<button @click="confirmDeletePlan(row)" class="btn-icon text-red-500 hover:text-red-700" :title="t('common.delete')"><Icon name="trash" size="sm" /></button>
|
||||
<div class="flex items-center gap-2">
|
||||
<button @click="openPlanEdit(row)" class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-blue-50 hover:text-blue-600 dark:hover:bg-blue-900/20 dark:hover:text-blue-400">
|
||||
<Icon name="edit" size="sm" />
|
||||
<span class="text-xs">{{ t('common.edit') }}</span>
|
||||
</button>
|
||||
<button @click="confirmDeletePlan(row)" class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20 dark:hover:text-red-400">
|
||||
<Icon name="trash" size="sm" />
|
||||
<span class="text-xs">{{ t('common.delete') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</DataTable>
|
||||
@@ -66,17 +72,17 @@
|
||||
<input v-model="planForm.name" type="text" class="input" required />
|
||||
</div>
|
||||
<div>
|
||||
<label class="input-label">{{ t('payment.admin.groupId') }}</label>
|
||||
<label class="input-label">{{ t('payment.admin.group') }}</label>
|
||||
<Select v-model="planForm.group_id" :options="groupOptions" class="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Group Info Preview -->
|
||||
<div v-if="selectedGroupInfo" class="rounded-lg border border-gray-200 bg-gray-50 p-3 dark:border-dark-600 dark:bg-dark-800">
|
||||
<p class="mb-2 text-xs font-semibold text-gray-500 dark:text-gray-400">{{ t('payment.admin.groupInfo') }}</p>
|
||||
<div class="mb-2 flex items-center gap-2">
|
||||
<GroupBadge :name="selectedGroupInfo.name" :platform="selectedGroupInfo.platform" :rate-multiplier="selectedGroupInfo.rate_multiplier" />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2 text-xs">
|
||||
<div><span class="text-gray-500">{{ t('payment.admin.platform') }}:</span> <span class="ml-1 font-medium text-gray-700 dark:text-gray-300">{{ selectedGroupInfo.platform }}</span></div>
|
||||
<div><span class="text-gray-500">{{ t('payment.admin.rateMultiplierLabel') }}:</span> <span class="ml-1 font-medium text-gray-700 dark:text-gray-300">{{ selectedGroupInfo.rate_multiplier }}x</span></div>
|
||||
<div><span class="text-gray-500">{{ t('payment.admin.dailyLimit') }}:</span> <span class="ml-1 font-medium text-gray-700 dark:text-gray-300">{{ selectedGroupInfo.daily_limit_usd != null ? '$' + selectedGroupInfo.daily_limit_usd : t('payment.admin.unlimited') }}</span></div>
|
||||
<div><span class="text-gray-500">{{ t('payment.admin.weeklyLimit') }}:</span> <span class="ml-1 font-medium text-gray-700 dark:text-gray-300">{{ selectedGroupInfo.weekly_limit_usd != null ? '$' + selectedGroupInfo.weekly_limit_usd : t('payment.admin.unlimited') }}</span></div>
|
||||
<div><span class="text-gray-500">{{ t('payment.admin.monthlyLimit') }}:</span> <span class="ml-1 font-medium text-gray-700 dark:text-gray-300">{{ selectedGroupInfo.monthly_limit_usd != null ? '$' + selectedGroupInfo.monthly_limit_usd : t('payment.admin.unlimited') }}</span></div>
|
||||
@@ -200,7 +206,7 @@ const validityUnitOptions = computed(() => [
|
||||
const planColumns = computed((): Column[] => [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'name', label: t('payment.admin.planName') },
|
||||
{ key: 'group_id', label: t('payment.admin.groupId') },
|
||||
{ key: 'group_id', label: t('payment.admin.group') },
|
||||
{ key: 'price', label: t('payment.admin.price') },
|
||||
{ key: 'validity_days', label: t('payment.admin.validityDays') },
|
||||
{ key: 'for_sale', label: t('payment.admin.forSale') },
|
||||
|
||||
Reference in New Issue
Block a user