Files
verify/frontend/src/pages/developer/applications/components/columns.ts
T

207 lines
7.4 KiB
TypeScript

import type { ColumnDef } from '@tanstack/vue-table'
import type { Composer } from 'vue-i18n'
import {
Ban,
CheckCircle,
Mail,
MoreHorizontal,
Settings,
Shield,
Trash2,
} from 'lucide-vue-next'
import { h } from 'vue'
import type { App } from '../data/schema'
import { Copy } from '@/components/sva-ui/copy'
import Button from '@/components/ui/button/Button.vue'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
export function getColumns(actions: {
onGoToSettings: (app: App) => void
onGoToSecurity: (app: App) => void
onGoToEmail: (app: App) => void
onToggleStatus: (app: App) => void
onDelete: (app: App) => void
}, t: Composer['t']): ColumnDef<App>[] {
const billingTypeMap: Record<string, { label: string, class: string }> = {
free: { label: '免费', class: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300' },
subscription: { label: '订阅', class: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300' },
time: { label: '计时', class: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300' },
count: { label: '计次', class: 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-300' },
}
const loginPolicyMap: Record<string, { label: string, class: string }> = {
loose: { label: '宽松', class: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300' },
strict: { label: '严格', class: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300' },
hybrid: { label: '混合', class: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300' },
}
const statusMap: Record<string, { label: string, class: string }> = {
active: { label: '启用', class: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300' },
inactive: { label: '禁用', class: 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300' },
}
return [
{
accessorKey: 'name',
header: () => '应用名称',
cell: ({ row }) => h('div', { class: 'flex items-center space-x-3' }, [
h('div', { class: 'h-8 w-8 rounded bg-primary/10 flex items-center justify-center flex-shrink-0 overflow-hidden' }, [
row.original.icon_url
? h('img', {
src: `http://localhost:8080${row.original.icon_url}`,
alt: row.getValue('name') as string,
class: 'w-full h-full object-cover',
})
: h('span', { class: 'text-primary font-bold text-sm' }, (row.getValue('name') as string).charAt(0).toUpperCase()),
]),
h('div', { class: 'flex flex-col' }, [
h('p', { class: 'font-medium' }, row.getValue('name') as string),
h('p', { class: 'text-xs text-muted-foreground truncate max-w-[200px]' }, row.original.description),
]),
]),
},
{
accessorKey: 'app_key',
header: () => 'App Key',
cell: ({ row }) => {
const appKey = row.getValue('app_key') as string
return h('div', { class: 'flex items-center space-x-2' }, [
h('code', { class: 'text-xs bg-muted px-2 py-1 rounded font-mono' }, appKey || '-'),
appKey && h(Copy, { class: 'h-4 w-4', size: 'sm', content: appKey }),
])
},
},
{
accessorKey: 'billing_type',
header: () => '运营模式',
cell: ({ row }) => {
const billingType = row.getValue('billing_type') as string
const info = billingTypeMap[billingType]
if (!info)
return h('span', {}, billingType)
return h('span', { class: `px-2 py-1 rounded text-xs ${info.class}` }, info.label)
},
},
{
accessorKey: 'login_policy',
header: () => '登录策略',
cell: ({ row }) => {
const loginPolicy = row.getValue('login_policy') as string
const info = loginPolicyMap[loginPolicy]
if (!info)
return h('span', {}, loginPolicy)
return h('span', { class: `px-2 py-1 rounded text-xs ${info.class}` }, info.label)
},
},
{
accessorKey: 'enable_trial',
header: () => '试用',
cell: ({ row }) => {
const enableTrial = row.getValue('enable_trial') as boolean
return h('span', {
class: `px-2 py-1 rounded text-xs ${enableTrial ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300' : 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300'}`,
}, enableTrial ? `${row.original.trial_balance}余额` : '无')
},
},
{
accessorKey: 'status',
header: () => '状态',
cell: ({ row }) => {
const status = row.getValue('status') as string
const info = statusMap[status]
if (!info)
return h('span', {}, status)
return h('span', { class: `px-2 py-1 rounded text-xs ${info.class}` }, info.label)
},
},
{
accessorKey: 'created_at',
header: () => '创建时间',
cell: ({ row }) => {
const createdAt = row.getValue('created_at')
if (!createdAt)
return '-'
try {
const date = new Date(createdAt as string)
if (Number.isNaN(date.getTime()))
return '-'
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
})
}
catch {
return '-'
}
},
},
{
id: 'actions',
header: () => h('span', { class: 'sr-only' }, t('common.actions')),
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' }, t('common.openMenu')),
])),
h(
DropdownMenuContent,
{ align: 'end' },
() => [
h(DropdownMenuItem, { onClick: () => actions.onGoToSettings(app) }, () => [
h(Settings, { class: 'mr-2 h-4 w-4' }),
'基本设置',
]),
h(DropdownMenuItem, { onClick: () => actions.onGoToSecurity(app) }, () => [
h(Shield, { class: 'mr-2 h-4 w-4' }),
'安全设置',
]),
h(DropdownMenuItem, { onClick: () => actions.onGoToEmail(app) }, () => [
h(Mail, { class: 'mr-2 h-4 w-4' }),
'邮箱设置',
]),
h(DropdownMenuSeparator),
h(DropdownMenuItem, { onClick: () => actions.onToggleStatus(app) }, () => [
isActive ? h(Ban, { class: 'mr-2 h-4 w-4' }) : h(CheckCircle, { 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' }),
t('admin.applications.delete'),
]),
],
),
],
},
)
},
},
]
}