Files
verify/frontend/src/pages/agent/cards/create.vue
T
admin 9d580a6bb7 feat: 优化代理后台页面布局
- 所有页面统一使用 BasicPage 组件

- 添加面包屑导航支持

- 统一页面标题和描述样式

- 移除控制台页面的应用管理快捷链接

- 优化卡密管理、用户管理、财务管理页面布局
2026-05-09 04:53:12 +08:00

170 lines
5.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { Loader2 } from 'lucide-vue-next'
import { computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import { toast } from 'vue-sonner'
import { BasicPage } from '@/components/global-layout'
import api from '@/services/api'
const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const loading = ref(false)
const apps = ref<any[]>([])
const cardTypes = ref<any[]>([])
const form = ref({
app_id: route.query.app_id || '',
card_type_id: route.query.card_type_id || '',
quantity: 1,
})
const availableCardTypes = computed(() => {
return cardTypes.value.filter(ct => ct.app_id === Number(form.value.app_id))
})
const totalPrice = computed(() => {
const ct = cardTypes.value.find(c => c.id === Number(form.value.card_type_id))
return ct ? ct.price * form.value.quantity : 0
})
onMounted(async () => {
try {
const data = await api.get<any[]>('/agent/apps')
apps.value = data || []
if (apps.value.length > 0) {
const typesData = await api.get<any[]>('/agent/card-types')
cardTypes.value = typesData || []
}
}
catch (error) {
console.error('Load data failed:', error)
}
})
async function handleGenerate() {
if (!form.value.app_id) {
toast.error('请选择应用')
return
}
if (!form.value.card_type_id) {
toast.error('请选择卡类')
return
}
if (form.value.quantity < 1 || form.value.quantity > 100) {
toast.error('数量必须在1-100之间')
return
}
loading.value = true
try {
const data = await api.post<{ codes: string[], cards: any[] }>('/agent/cards/generate', {
app_id: Number(form.value.app_id),
card_type_id: Number(form.value.card_type_id),
quantity: form.value.quantity,
})
toast.success(`成功生成 ${data.codes.length} 张卡密`)
const codesText = data.codes.join('\n')
await navigator.clipboard.writeText(codesText)
toast.success('卡密已复制到剪贴板')
router.push('/agent/cards')
}
catch (error: any) {
console.error('Generate cards failed:', error)
toast.error(error.message || '生成卡密失败')
}
finally {
loading.value = false
}
}
</script>
<template>
<BasicPage
title="生成卡密"
description="批量生成卡密"
sticky
>
<UiCard class="max-w-2xl">
<UiCardHeader>
<UiCardTitle>生成设置</UiCardTitle>
<UiCardDescription>
选择应用和卡类设置生成数量
</UiCardDescription>
</UiCardHeader>
<UiCardContent class="space-y-6">
<div class="space-y-2">
<UiLabel>选择应用 <span class="text-destructive">*</span></UiLabel>
<UiSelect v-model="form.app_id">
<UiSelectTrigger>
<UiSelectValue placeholder="请选择应用" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem v-for="app in apps" :key="app.id" :value="String(app.id)">
{{ app.name }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div v-if="form.app_id" class="space-y-2">
<UiLabel>选择卡类 <span class="text-destructive">*</span></UiLabel>
<UiSelect v-model="form.card_type_id">
<UiSelectTrigger>
<UiSelectValue placeholder="请选择卡类" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem v-for="ct in availableCardTypes" :key="ct.id" :value="String(ct.id)">
{{ ct.name }} - {{ ct.duration_days }} - ¥{{ ct.price }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div class="space-y-2">
<UiLabel>生成数量 <span class="text-destructive">*</span></UiLabel>
<UiInput
v-model.number="form.quantity"
type="number"
:min="1"
:max="100"
placeholder="请输入生成数量"
/>
<p class="text-xs text-muted-foreground">
单次最多生成100张卡密
</p>
</div>
<div v-if="totalPrice > 0" class="p-4 rounded-lg bg-muted/50">
<div class="flex items-center justify-between">
<span class="text-muted-foreground">预计费用</span>
<span class="text-xl font-bold">¥{{ totalPrice.toFixed(2) }}</span>
</div>
</div>
<div class="flex gap-3">
<UiButton variant="outline" as-child>
<router-link to="/agent/cards">
取消
</router-link>
</UiButton>
<UiButton
:disabled="loading || !form.app_id || !form.card_type_id"
@click="handleGenerate"
>
<Loader2 v-if="loading" class="mr-2 h-4 w-4 animate-spin" />
{{ loading ? '生成中...' : '生成卡密' }}
</UiButton>
</div>
</UiCardContent>
</UiCard>
</BasicPage>
</template>