This commit is contained in:
2026-03-13 15:51:59 +08:00
parent 4db2386bbf
commit 4e91f4cede
133 changed files with 19502 additions and 37 deletions

View File

@@ -0,0 +1,173 @@
import { useState } from 'react'
import {
Table, Tag, Button, Space, Card, Modal, Form, Input,
message, Popconfirm, Alert, Typography,
} from 'antd'
import { KeyOutlined, PlusOutlined } from '@ant-design/icons'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import type { ColumnsType } from 'antd/es/table'
import dayjs from 'dayjs'
import { appApi } from '../../api/app'
import type { App } from '../../types'
const { Text } = Typography
export default function AppPage() {
const [createOpen, setCreateOpen] = useState(false)
const [secretModal, setSecretModal] = useState<{ appID: string; secret: string; tip: string } | null>(null)
const [form] = Form.useForm()
const qc = useQueryClient()
const { data, isLoading } = useQuery({
queryKey: ['apps'],
queryFn: () => appApi.list({ limit: 50, offset: 0 }),
})
const createMutation = useMutation({
mutationFn: appApi.create,
onSuccess: (res) => {
setCreateOpen(false)
form.resetFields()
qc.invalidateQueries({ queryKey: ['apps'] })
const d = res.data.data
setSecretModal({ appID: d.app_id, secret: d.app_secret, tip: d.secret_tip })
},
})
const disableMutation = useMutation({
mutationFn: appApi.disable,
onSuccess: () => {
message.success('已禁用')
qc.invalidateQueries({ queryKey: ['apps'] })
},
})
const enableMutation = useMutation({
mutationFn: appApi.enable,
onSuccess: () => {
message.success('已启用')
qc.invalidateQueries({ queryKey: ['apps'] })
},
})
const resetMutation = useMutation({
mutationFn: appApi.resetSecret,
onSuccess: (res) => {
const d = res.data.data
setSecretModal({ appID: d.app_id, secret: d.app_secret, tip: d.secret_tip })
},
})
const columns: ColumnsType<App> = [
{ title: 'App ID', dataIndex: 'app_id', width: 200 },
{ title: '应用名称', dataIndex: 'app_name', width: 160 },
{
title: '状态',
dataIndex: 'status',
width: 80,
render: (v: number) =>
v === 1 ? <Tag color="success"></Tag> : <Tag color="default"></Tag>,
},
{
title: '创建时间',
dataIndex: 'created_at',
width: 180,
render: (v) => dayjs(v).format('YYYY-MM-DD HH:mm:ss'),
},
{
title: '操作',
width: 220,
render: (_, record) => (
<Space>
<Popconfirm
title="重置后旧密钥立即失效,确认重置?"
onConfirm={() => resetMutation.mutate(record.app_id)}
>
<Button size="small" icon={<KeyOutlined />}></Button>
</Popconfirm>
{record.status === 1 ? (
<Popconfirm title="禁用后该应用将无法访问,确认?" onConfirm={() => disableMutation.mutate(record.app_id)}>
<Button size="small" danger></Button>
</Popconfirm>
) : (
<Popconfirm title="确认启用该应用?" onConfirm={() => enableMutation.mutate(record.app_id)}>
<Button size="small"></Button>
</Popconfirm>
)}
</Space>
),
},
]
const list: App[] = data?.data?.data?.list ?? []
return (
<>
<Card
extra={
<Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateOpen(true)}>
</Button>
}
>
<Table
rowKey="app_id"
columns={columns}
dataSource={list}
loading={isLoading}
scroll={{ x: 800 }}
/>
</Card>
{/* 新建应用弹窗 */}
<Modal
title="新建接入应用"
open={createOpen}
onOk={() => form.submit()}
onCancel={() => { setCreateOpen(false); form.resetFields() }}
confirmLoading={createMutation.isPending}
>
<Form form={form} layout="vertical" onFinish={(v) => createMutation.mutate(v)}>
<Form.Item name="app_name" label="应用名称" rules={[{ required: true, message: '请输入应用名称' }]}>
<Input placeholder="例如商城系统、ERP系统" />
</Form.Item>
</Form>
<Alert
type="info"
showIcon
message="App ID 和 App Secret 将在创建后自动生成Secret 仅展示一次,请妥善保存。"
style={{ marginTop: 8 }}
/>
</Modal>
{/* Secret 展示弹窗(创建/重置后) */}
<Modal
title="请妥善保存以下凭证"
open={!!secretModal}
footer={<Button type="primary" onClick={() => setSecretModal(null)}></Button>}
onCancel={() => setSecretModal(null)}
closable={false}
maskClosable={false}
>
{secretModal && (
<>
<Alert
type="warning"
showIcon
message={secretModal.tip}
style={{ marginBottom: 16 }}
/>
<Form layout="vertical">
<Form.Item label="App ID">
<Text copyable code>{secretModal.appID}</Text>
</Form.Item>
<Form.Item label="App Secret">
<Text copyable code>{secretModal.secret}</Text>
</Form.Item>
</Form>
</>
)}
</Modal>
</>
)
}