diff --git a/frontend/src/components/admin-sidebar/index.vue b/frontend/src/components/admin-sidebar/index.vue index 4b5a529..85d980c 100644 --- a/frontend/src/components/admin-sidebar/index.vue +++ b/frontend/src/components/admin-sidebar/index.vue @@ -1,5 +1,5 @@ + + diff --git a/frontend/src/pages/admin/agent-apps/components/columns.ts b/frontend/src/pages/admin/agent-apps/components/columns.ts new file mode 100644 index 0000000..9d382ba --- /dev/null +++ b/frontend/src/pages/admin/agent-apps/components/columns.ts @@ -0,0 +1,117 @@ +import type { ColumnDef } from '@tanstack/vue-table' +import type { Composer } from 'vue-i18n' + +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { Check, Pencil, Trash2, X } from 'lucide-vue-next' +import { h } from 'vue' + +import type { AgentApp } from '../data/schema' + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' +import { MoreHorizontal } from 'lucide-vue-next' + +export function getColumns(actions: { + onEdit: (row: AgentApp) => void + onDelete: (row: AgentApp) => void + onToggleStatus: (row: AgentApp) => void +}, t: Composer['t']): ColumnDef[] { + return [ + { + accessorKey: 'agent_name', + header: () => h('span', {}, '代理'), + cell: ({ row }) => { + const agentName = row.getValue('agent_name') as string + return h('div', { class: 'flex items-center gap-2' }, [ + h('span', { class: 'font-medium' }, agentName || '-'), + ]) + }, + }, + { + accessorKey: 'app_name', + header: () => h('span', {}, '应用'), + cell: ({ row }) => { + const appName = row.getValue('app_name') as string + return h('span', { class: 'font-medium' }, appName || '-') + }, + }, + { + accessorKey: 'card_types', + header: () => h('span', {}, '卡密类型'), + cell: ({ row }) => { + const cardTypes = row.getValue('card_types') as AgentApp['card_types'] + const total = cardTypes?.length || 0 + const authorized = cardTypes?.filter(c => c.can_generate).length || 0 + return h('div', { class: 'flex items-center gap-2' }, [ + h('span', { class: 'text-sm' }, `${authorized} / ${total}`), + ]) + }, + }, + { + accessorKey: 'balance', + header: () => h('span', {}, '余额'), + cell: ({ row }) => { + const balance = row.getValue('balance') as number + return h('span', { class: 'text-sm font-medium' }, `¥${(balance || 0).toFixed(2)}`) + }, + }, + { + accessorKey: 'status', + header: () => h('span', {}, '状态'), + cell: ({ row }) => { + const status = row.getValue('status') as string + const isActive = status === 'active' + return h(Badge, { variant: isActive ? 'default' : 'secondary' }, () => + isActive ? '已启用' : '已禁用' + ) + }, + }, + { + id: 'actions', + header: () => h('span', { class: 'sr-only' }, '操作'), + cell: ({ row }) => { + const app = row.original + const isActive = app.status === 'active' + + return h( + DropdownMenu, + {}, + { + default: () => [ + h(DropdownMenuTrigger, { asChild: true }, () => + h(Button, { variant: 'ghost', class: 'h-8 w-8 p-0' }, () => [ + h(MoreHorizontal, { class: 'h-4 w-4' }), + h('span', { class: 'sr-only' }, '打开菜单'), + ])), + h( + DropdownMenuContent, + { align: 'end' }, + () => [ + h(DropdownMenuItem, { onClick: () => actions.onEdit(app) }, () => [ + h(Pencil, { class: 'mr-2 h-4 w-4' }), + '编辑', + ]), + h(DropdownMenuItem, { onClick: () => actions.onToggleStatus(app) }, () => [ + h(Check, { class: 'mr-2 h-4 w-4' }), + isActive ? '禁用' : '启用', + ]), + h(DropdownMenuSeparator), + h(DropdownMenuItem, { class: 'text-destructive', onClick: () => actions.onDelete(app) }, () => [ + h(Trash2, { class: 'mr-2 h-4 w-4' }), + '删除', + ]), + ], + ), + ], + }, + ) + }, + }, + ] +} diff --git a/frontend/src/pages/admin/agent-apps/components/data-table.vue b/frontend/src/pages/admin/agent-apps/components/data-table.vue new file mode 100644 index 0000000..b0dcc51 --- /dev/null +++ b/frontend/src/pages/admin/agent-apps/components/data-table.vue @@ -0,0 +1,61 @@ + + + diff --git a/frontend/src/pages/admin/agent-apps/create.vue b/frontend/src/pages/admin/agent-apps/create.vue new file mode 100644 index 0000000..ef442db --- /dev/null +++ b/frontend/src/pages/admin/agent-apps/create.vue @@ -0,0 +1,243 @@ + + + diff --git a/frontend/src/pages/admin/agent-apps/data/schema.ts b/frontend/src/pages/admin/agent-apps/data/schema.ts new file mode 100644 index 0000000..a05fa6d --- /dev/null +++ b/frontend/src/pages/admin/agent-apps/data/schema.ts @@ -0,0 +1,13 @@ +export interface AgentApp { + id: number + agent_id: number + agent_name?: string + application_id: number + app_name: string + status: string + discount: number + balance: number + card_types: { card_type_id: number, can_generate: boolean, name?: string }[] + created_at?: string + updated_at?: string +} diff --git a/frontend/src/pages/admin/agent-apps/index.vue b/frontend/src/pages/admin/agent-apps/index.vue new file mode 100644 index 0000000..d623cfb --- /dev/null +++ b/frontend/src/pages/admin/agent-apps/index.vue @@ -0,0 +1,190 @@ + + + diff --git a/frontend/src/pages/admin/agents/[id].vue b/frontend/src/pages/admin/agents/[id].vue index 33f9251..3023d14 100644 --- a/frontend/src/pages/admin/agents/[id].vue +++ b/frontend/src/pages/admin/agents/[id].vue @@ -1,5 +1,5 @@ @@ -386,98 +247,6 @@ onMounted(() => { - - - -
- - - 授权应用 - - 管理该代理可生成卡密的应用和卡类权限 -
- - - 添加授权 - -
- -
- -
-
- -

暂无授权应用

-

点击上方按钮为代理添加应用授权

-
-
- - - - - - - - - - - - - -
应用卡密类型余额状态操作
-
-
-
@@ -540,73 +309,5 @@ onMounted(() => {
- - - - - 添加授权 - 为该代理添加应用和卡类权限 - -
-
- 选择应用 - - - - - - - {{ app.name }} - - - -
- -
- 卡类权限 -
- - - - - - - - - - - - - - - - - -
卡类类型售价可生成
{{ ct.name }}{{ ct.billing_type }} - - - -
-
-
-
- - 取消 - - - 确认授权 - - -
-
diff --git a/frontend/src/router/routes.ts b/frontend/src/router/routes.ts index 637536f..04435eb 100644 --- a/frontend/src/router/routes.ts +++ b/frontend/src/router/routes.ts @@ -185,6 +185,24 @@ const routes: RouteRecordRaw[] = [ component: () => import('@/pages/admin/agents/[id].vue'), meta: { title: '编辑代理 - 管理后台' }, }, + { + path: 'agent-apps', + name: 'AdminAgentApps', + component: () => import('@/pages/admin/agent-apps/index.vue'), + meta: { title: '授权管理 - 管理后台' }, + }, + { + path: 'agent-apps/create', + name: 'AdminAgentAppCreate', + component: () => import('@/pages/admin/agent-apps/create.vue'), + meta: { title: '添加授权 - 管理后台' }, + }, + { + path: 'agent-apps/:id', + name: 'AdminAgentAppEdit', + component: () => import('@/pages/admin/agent-apps/[id].vue'), + meta: { title: '编辑授权 - 管理后台' }, + }, { path: 'finance', name: 'AdminFinance',