diff --git a/web/tenant/src/router/tenant.ts b/web/tenant/src/router/tenant.ts index 84124d5..3f6a58a 100644 --- a/web/tenant/src/router/tenant.ts +++ b/web/tenant/src/router/tenant.ts @@ -149,7 +149,7 @@ const tenantRoutes: RouteRecordRaw[] = [ path: 'redeem', name: 'TenantRedeem', component: () => import('@/views/finance/RedeemPage.vue'), - meta: { title: '兑换码', icon: 'gift', sort: 43, roles: ['owner', 'admin'] }, + meta: { title: '兑换码', roles: ['owner', 'admin'] }, }, // 沟通 (50-59) { diff --git a/web/tenant/src/views/finance/WalletPage.vue b/web/tenant/src/views/finance/WalletPage.vue index 929be7f..c988dc7 100644 --- a/web/tenant/src/views/finance/WalletPage.vue +++ b/web/tenant/src/views/finance/WalletPage.vue @@ -33,6 +33,16 @@ const frozenItems = ref([]) const frozenLoading = ref(false) let frozenTimer: ReturnType | null = null +// Redeem +const showRedeemModal = ref(false) +const redeemCode = ref('') +const redeemLoading = ref(false) +const redeemResult = ref(null) +const redeemHistory = ref([]) +const redeemHistoryLoading = ref(false) +const redeemTypeLabels: Record = { quota: '额度', plan: '套餐', duration: '时长' } +const redeemTypeBadgeClasses: Record = { quota: 'badge-success', plan: 'badge-primary', duration: 'badge-warning' } + const monthsOptions = [ { label: '1 个月', value: 1 }, { label: '3 个月', value: 3 }, @@ -217,6 +227,49 @@ function closeFrozenModal() { } } +// Redeem +async function openRedeemModal() { + showRedeemModal.value = true + redeemCode.value = '' + redeemResult.value = null + await fetchRedeemHistory() +} + +function closeRedeemModal() { + showRedeemModal.value = false +} + +async function handleRedeem() { + if (!redeemCode.value.trim()) return + redeemLoading.value = true + redeemResult.value = null + try { + const res: any = await request.post('/tenant/redemptions/redeem', { code: redeemCode.value.trim() }) + redeemResult.value = res.data?.data + redeemCode.value = '' + await fetchRedeemHistory() + fetchWallet() + } catch { + // interceptor handles error toast + } finally { + redeemLoading.value = false + } +} + +async function fetchRedeemHistory() { + redeemHistoryLoading.value = true + try { + const res: any = await request.get('/tenant/redemptions/usages', { + params: { page: 1, page_size: 10 } + }) + redeemHistory.value = res.data?.data?.list || [] + } catch { + redeemHistory.value = [] + } finally { + redeemHistoryLoading.value = false + } +} + function formatTime(unix: number): string { if (!unix) return '-' return new Date(unix * 1000).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' }) @@ -255,9 +308,15 @@ onBeforeUnmount(() => {