feat: 添加查看授权和编辑授权功能, 代理充值使用 NumberField 组件
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { CheckCircle, Key, MoreHorizontal, Plus, Share2, Trash2, XCircle } from 'lucide-vue-next'
|
||||
import { CheckCircle, Eye, Key, MoreHorizontal, Pencil, Plus, Share2, Trash2, XCircle } from 'lucide-vue-next'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { toast } from 'vue-sonner'
|
||||
@@ -17,6 +17,17 @@ const agentApps = ref<AgentApp[]>([])
|
||||
const showRemoveDialog = ref(false)
|
||||
const removingItem = ref<AgentApp | null>(null)
|
||||
|
||||
const viewDialogOpen = ref(false)
|
||||
const viewItem = ref<AgentApp | null>(null)
|
||||
|
||||
const editDialogOpen = ref(false)
|
||||
const editItem = ref<AgentApp | null>(null)
|
||||
const editSaving = ref(false)
|
||||
const editForm = ref({
|
||||
status: 'active',
|
||||
card_types: [] as Array<{ card_type_id: number, can_generate: boolean, price: number, name: string }>,
|
||||
})
|
||||
|
||||
const activeCount = computed(() => agentApps.value.filter(app => app.status === 'active').length)
|
||||
const inactiveCount = computed(() => agentApps.value.filter(app => app.status === 'inactive').length)
|
||||
const totalBalance = computed(() => agentApps.value.reduce((sum, app) => sum + (app.balance || 0), 0))
|
||||
@@ -59,6 +70,55 @@ function goToCreate() {
|
||||
router.push('/admin/agent-apps/create')
|
||||
}
|
||||
|
||||
function handleView(item: AgentApp) {
|
||||
viewItem.value = item
|
||||
viewDialogOpen.value = true
|
||||
}
|
||||
|
||||
function handleEdit(item: AgentApp) {
|
||||
editItem.value = item
|
||||
editForm.value = {
|
||||
status: item.status || 'active',
|
||||
card_types: (item.card_types || []).map(ct => ({
|
||||
card_type_id: ct.card_type_id || ct.id,
|
||||
can_generate: ct.can_generate,
|
||||
price: ct.price || 0,
|
||||
name: ct.name || '',
|
||||
})),
|
||||
}
|
||||
editDialogOpen.value = true
|
||||
}
|
||||
|
||||
async function handleEditSubmit() {
|
||||
if (!editItem.value) return
|
||||
|
||||
editSaving.value = true
|
||||
try {
|
||||
await api.put(`/dev/agent-apps/${editItem.value.id}`, {
|
||||
status: editForm.value.status,
|
||||
})
|
||||
|
||||
await api.put(`/dev/agent-apps/${editItem.value.id}/card-types`, {
|
||||
card_types: editForm.value.card_types.map(ct => ({
|
||||
card_type_id: ct.card_type_id,
|
||||
can_generate: ct.can_generate,
|
||||
price: ct.price,
|
||||
})),
|
||||
})
|
||||
|
||||
toast.success('保存成功')
|
||||
editDialogOpen.value = false
|
||||
fetchData()
|
||||
}
|
||||
catch (error: any) {
|
||||
console.error('保存失败:', error)
|
||||
toast.error(error.message || '保存失败')
|
||||
}
|
||||
finally {
|
||||
editSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function confirmRemove(item: AgentApp) {
|
||||
removingItem.value = item
|
||||
showRemoveDialog.value = true
|
||||
@@ -147,7 +207,7 @@ onMounted(() => {
|
||||
<UiCardTitle class="text-sm font-medium">
|
||||
代理余额总计
|
||||
</UiCardTitle>
|
||||
<Wallet class="size-4 text-muted-foreground" />
|
||||
<Key class="size-4 text-muted-foreground" />
|
||||
</UiCardHeader>
|
||||
<UiCardContent>
|
||||
<div class="text-2xl font-bold">
|
||||
@@ -256,6 +316,15 @@ onMounted(() => {
|
||||
</UiButton>
|
||||
</UiDropdownMenuTrigger>
|
||||
<UiDropdownMenuContent align="end">
|
||||
<UiDropdownMenuItem @click="handleView(item)">
|
||||
<Eye class="mr-2 h-4 w-4" />
|
||||
查看授权
|
||||
</UiDropdownMenuItem>
|
||||
<UiDropdownMenuItem @click="handleEdit(item)">
|
||||
<Pencil class="mr-2 h-4 w-4" />
|
||||
编辑授权
|
||||
</UiDropdownMenuItem>
|
||||
<UiDropdownMenuSeparator />
|
||||
<UiDropdownMenuItem class="text-destructive" @click="confirmRemove(item)">
|
||||
<Trash2 class="mr-2 h-4 w-4" />
|
||||
移除授权
|
||||
@@ -277,5 +346,157 @@ onMounted(() => {
|
||||
confirm-text="确定移除"
|
||||
@confirm="handleRemove"
|
||||
/>
|
||||
|
||||
<UiDialog v-model:open="viewDialogOpen">
|
||||
<UiDialogContent class="sm:max-w-lg">
|
||||
<UiDialogHeader>
|
||||
<UiDialogTitle>查看授权</UiDialogTitle>
|
||||
<UiDialogDescription>
|
||||
{{ viewItem?.agent_name }} - {{ viewItem?.app_name }}
|
||||
</UiDialogDescription>
|
||||
</UiDialogHeader>
|
||||
<div class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1">
|
||||
<span class="text-sm text-muted-foreground">代理</span>
|
||||
<p class="font-medium">{{ viewItem?.agent_name }}</p>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<span class="text-sm text-muted-foreground">应用</span>
|
||||
<p class="font-medium">{{ viewItem?.app_name }}</p>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<span class="text-sm text-muted-foreground">状态</span>
|
||||
<UiBadge :variant="viewItem?.status === 'active' ? 'default' : 'secondary'">
|
||||
{{ viewItem?.status === 'active' ? '已启用' : '已禁用' }}
|
||||
</UiBadge>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<span class="text-sm text-muted-foreground">余额</span>
|
||||
<p class="font-medium">¥{{ (viewItem?.balance || 0).toFixed(2) }}</p>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<span class="text-sm text-muted-foreground">授权时间</span>
|
||||
<p class="font-medium">{{ viewItem?.created_at }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<span class="text-sm text-muted-foreground">卡密权限</span>
|
||||
<div v-if="viewItem?.card_types && viewItem.card_types.length > 0" class="border rounded-lg">
|
||||
<UiTable>
|
||||
<UiTableHeader>
|
||||
<UiTableRow>
|
||||
<UiTableHead>卡类名称</UiTableHead>
|
||||
<UiTableHead>授权价格</UiTableHead>
|
||||
<UiTableHead>状态</UiTableHead>
|
||||
</UiTableRow>
|
||||
</UiTableHeader>
|
||||
<UiTableBody>
|
||||
<UiTableRow v-for="ct in viewItem.card_types" :key="ct.card_type_id || ct.id">
|
||||
<UiTableCell>{{ ct.name }}</UiTableCell>
|
||||
<UiTableCell>¥{{ ct.price || 0 }}</UiTableCell>
|
||||
<UiTableCell>
|
||||
<UiBadge :variant="ct.can_generate ? 'default' : 'secondary'">
|
||||
{{ ct.can_generate ? '已授权' : '未授权' }}
|
||||
</UiBadge>
|
||||
</UiTableCell>
|
||||
</UiTableRow>
|
||||
</UiTableBody>
|
||||
</UiTable>
|
||||
</div>
|
||||
<p v-else class="text-muted-foreground text-sm">暂无卡密权限</p>
|
||||
</div>
|
||||
</div>
|
||||
<UiDialogFooter>
|
||||
<UiButton variant="outline" @click="viewDialogOpen = false">
|
||||
关闭
|
||||
</UiButton>
|
||||
</UiDialogFooter>
|
||||
</UiDialogContent>
|
||||
</UiDialog>
|
||||
|
||||
<UiDialog v-model:open="editDialogOpen">
|
||||
<UiDialogContent class="sm:max-w-2xl">
|
||||
<UiDialogHeader>
|
||||
<UiDialogTitle>编辑授权</UiDialogTitle>
|
||||
<UiDialogDescription>
|
||||
{{ editItem?.agent_name }} - {{ editItem?.app_name }}
|
||||
</UiDialogDescription>
|
||||
</UiDialogHeader>
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<UiLabel>状态</UiLabel>
|
||||
<UiSelect v-model="editForm.status">
|
||||
<UiSelectTrigger>
|
||||
<UiSelectValue placeholder="请选择状态" />
|
||||
</UiSelectTrigger>
|
||||
<UiSelectContent>
|
||||
<UiSelectItem value="active">
|
||||
已启用
|
||||
</UiSelectItem>
|
||||
<UiSelectItem value="inactive">
|
||||
已禁用
|
||||
</UiSelectItem>
|
||||
</UiSelectContent>
|
||||
</UiSelect>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<UiLabel>卡密权限</UiLabel>
|
||||
<div v-if="editForm.card_types.length > 0" class="border rounded-lg">
|
||||
<UiTable>
|
||||
<UiTableHeader>
|
||||
<UiTableRow>
|
||||
<UiTableHead class="w-12" />
|
||||
<UiTableHead>卡类名称</UiTableHead>
|
||||
<UiTableHead>授权价格</UiTableHead>
|
||||
</UiTableRow>
|
||||
</UiTableHeader>
|
||||
<UiTableBody>
|
||||
<UiTableRow
|
||||
v-for="ct in editForm.card_types"
|
||||
:key="ct.card_type_id"
|
||||
:class="ct.can_generate ? 'bg-primary/10' : ''"
|
||||
class="cursor-pointer"
|
||||
@click="ct.can_generate = !ct.can_generate"
|
||||
>
|
||||
<UiTableCell>
|
||||
<UiCheckbox
|
||||
:checked="ct.can_generate"
|
||||
@update:checked="ct.can_generate = $event"
|
||||
@click.stop
|
||||
/>
|
||||
</UiTableCell>
|
||||
<UiTableCell class="font-medium">
|
||||
{{ ct.name }}
|
||||
</UiTableCell>
|
||||
<UiTableCell @click.stop>
|
||||
<UiNumberField v-model="ct.price" :min="0" class="w-28">
|
||||
<UiNumberFieldContent>
|
||||
<UiNumberFieldDecrement />
|
||||
<UiNumberFieldInput />
|
||||
<UiNumberFieldIncrement />
|
||||
</UiNumberFieldContent>
|
||||
</UiNumberField>
|
||||
</UiTableCell>
|
||||
</UiTableRow>
|
||||
</UiTableBody>
|
||||
</UiTable>
|
||||
</div>
|
||||
<p v-else class="text-muted-foreground text-sm">暂无卡密权限</p>
|
||||
</div>
|
||||
</div>
|
||||
<UiDialogFooter>
|
||||
<UiButton variant="outline" @click="editDialogOpen = false">
|
||||
取消
|
||||
</UiButton>
|
||||
<UiButton :disabled="editSaving" @click="handleEditSubmit">
|
||||
<span v-if="editSaving" class="i-lucide-loader-2 mr-2 h-4 w-4 animate-spin" />
|
||||
保存
|
||||
</UiButton>
|
||||
</UiDialogFooter>
|
||||
</UiDialogContent>
|
||||
</UiDialog>
|
||||
</BasicPage>
|
||||
</template>
|
||||
|
||||
@@ -408,13 +408,13 @@ onMounted(() => {
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<UiLabel>充值金额</UiLabel>
|
||||
<UiInput
|
||||
v-model.number="rechargeAmount"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.01"
|
||||
placeholder="请输入充值金额"
|
||||
/>
|
||||
<UiNumberField v-model="rechargeAmount" :min="0" :step="0.01">
|
||||
<UiNumberFieldContent>
|
||||
<UiNumberFieldDecrement />
|
||||
<UiNumberFieldInput />
|
||||
<UiNumberFieldIncrement />
|
||||
</UiNumberFieldContent>
|
||||
</UiNumberField>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<UiButton
|
||||
|
||||
Reference in New Issue
Block a user