Files
verify/frontend/src/pages/agent/cards/create.vue
T
admin c653ed071d refactor: 将 @iconify/vue 图标替换为 lucide-vue-next 本地组件
- 所有 lucide: 前缀的图标字符串替换为 lucide-vue-next 组件直接引用
- 动态图标绑定改为 <component :is> 渲染
- h(Icon, { icon: 'lucide:xxx' }) 改为 h(Xxx, { class: ... })
- app-card.vue 的 icon_url 改为 <img> 标签(实际是图片路径)
- 修复 Webhook 标识符冲突(重命名为 WebhookIcon)
- payment-channels 保留 @iconify/vue 用于品牌图标(支付宝、微信、Stripe、PayPal、USDT)
2026-05-03 04:25:34 +08:00

176 lines
4.9 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 { useRoute, useRouter } from 'vue-router'
import { toast } from 'vue-sonner'
import api from '@/services/api'
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 _selectedApp = computed(() => {
return apps.value.find(a => a.id === Number(form.value.app_id))
})
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('获取数据失败:', 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('生成卡密失败:', error)
toast.error(error.message || '生成失败')
}
finally {
loading.value = false
}
}
</script>
<template>
<div class="space-y-6">
<div>
<h1 class="text-2xl font-bold">
生成卡密
</h1>
<p class="text-muted-foreground">
为授权应用生成卡密
</p>
</div>
<UiCard class="max-w-xl">
<UiCardHeader>
<UiCardTitle>生成设置</UiCardTitle>
<UiCardDescription>
选择应用和卡类设置生成数量
</UiCardDescription>
</UiCardHeader>
<UiCardContent class="space-y-6">
<div class="space-y-2">
<UiLabel>选择应用</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>选择卡类</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>生成数量</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>
</div>
</template>