Initial commit: 网络验证平台
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
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('developer.applications.delete'),
|
||||
]),
|
||||
],
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user