207 lines
7.4 KiB
TypeScript
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'),
|
|
]),
|
|
],
|
|
),
|
|
],
|
|
},
|
|
)
|
|
},
|
|
},
|
|
]
|
|
}
|