feat: pagination and search api

This commit is contained in:
scarqin
2024-12-25 16:51:41 +08:00
parent 129c7a15c7
commit de008edc68
4 changed files with 120 additions and 110 deletions
@@ -1,4 +1,12 @@
import { SearchOutlined } from '@ant-design/icons'
import type { ActionType, ParamsType, ProColumns, ProTableProps } from '@ant-design/pro-components'
import { DragSortTable, ProTable } from '@ant-design/pro-components'
import WithPermission from '@common/components/aoplatform/WithPermission'
import { PERMISSION_DEFINITION } from '@common/const/permissions'
import { withMinimumDelay } from '@common/utils/ux'
import { Button, Dropdown, Input, MenuProps, TablePaginationConfig } from 'antd'
import { FilterValue, SorterResult, TableCurrentDataSource } from 'antd/es/table/interface'
import { debounce } from 'lodash-es'
import {
ChangeEvent,
RefAttributes,
@@ -9,16 +17,8 @@ import {
useRef,
useState
} from 'react'
import type { ActionType, ParamsType, ProColumns, ProTableProps } from '@ant-design/pro-components'
import { DragSortTable, ProTable } from '@ant-design/pro-components'
import './PageList.module.css'
import { SearchOutlined } from '@ant-design/icons'
import { debounce } from 'lodash-es'
import WithPermission from '@common/components/aoplatform/WithPermission'
import { FilterValue, SorterResult, TableCurrentDataSource } from 'antd/es/table/interface'
import { useGlobalContext } from '../../contexts/GlobalStateContext'
import { PERMISSION_DEFINITION } from '@common/const/permissions'
import { withMinimumDelay } from '@common/utils/ux'
import './PageList.module.css'
export type PageProColumns<T = any, ValueType = 'text'> = ProColumns<T, ValueType> & { btnNums?: number }
@@ -104,7 +104,6 @@ const PageList = <T extends Record<string, unknown>>(
const { accessData, checkPermission, accessInit, state } = useGlobalContext()
const [minTableWidth, setMinTableWidth] = useState<number>(0)
// 使用useImperativeHandle来自定义暴露给父组件的实例值
useImperativeHandle(ref, () => actionRef.current!)
useEffect(() => {
@@ -209,6 +208,32 @@ const PageList = <T extends Record<string, unknown>>(
)
}
const getTableActions = () => {
return [
...(beforeSearchNode ? [beforeSearchNode] : []),
...(searchPlaceholder
? [
<Input
key="search-input"
className="my-btnbase ml-btnbase"
onChange={onSearchWordChange ? (e) => debounce(onSearchWordChange, 100)(e) : undefined}
onPressEnter={() => (manualReloadTable ? manualReloadTable() : actionRef.current?.reload?.())}
allowClear
placeholder={searchPlaceholder}
prefix={
<SearchOutlined
className="cursor-pointer"
onClick={() => {
actionRef.current?.reload?.()
}}
/>
}
/>
]
: [])
]
}
const requestWithDelay = (
params: ParamsType & { pageSize?: number | undefined; current?: number | undefined; keyword?: string | undefined },
sort: unknown,
@@ -229,7 +254,15 @@ const PageList = <T extends Record<string, unknown>>(
columns={newColumns}
rowKey={primaryKey}
search={false}
pagination={false}
pagination={
showPagination
? {
showSizeChanger: true,
showQuickJumper: true,
size: 'default'
}
: false
}
request={request}
dragSortKey={dragSortKey}
onDragSortEnd={onDragSortEnd}
@@ -239,6 +272,9 @@ const PageList = <T extends Record<string, unknown>>(
density: false,
setting: false
}}
toolbar={{
actions: getTableActions()
}}
headerTitle={headerTitle()}
/>
) : (
@@ -260,28 +296,7 @@ const PageList = <T extends Record<string, unknown>>(
) : null
]}
toolbar={{
actions: [
...[beforeSearchNode],
...[
searchPlaceholder ? (
<Input
className="my-btnbase ml-btnbase"
onChange={onSearchWordChange ? (e) => debounce(onSearchWordChange, 100)(e) : undefined}
onPressEnter={() => (manualReloadTable ? manualReloadTable() : actionRef.current?.reload?.())}
allowClear
placeholder={searchPlaceholder}
prefix={
<SearchOutlined
className="cursor-pointer"
onClick={() => {
actionRef.current?.reload?.()
}}
/>
}
/>
) : null
]
]
actions: getTableActions()
}}
options={{
reload: false,
@@ -304,7 +319,6 @@ const PageList = <T extends Record<string, unknown>>(
}
: false
}
rowKey={primaryKey}
onChange={(
pagination: TablePaginationConfig,
filters: Record<string, FilterValue | null>,
@@ -319,6 +333,7 @@ const PageList = <T extends Record<string, unknown>>(
)
onChange?.(pagination, filters, sorter, extra)
}}
rowKey={primaryKey}
dataSource={dataSource}
search={false}
headerTitle={headerTitle()}
@@ -76,7 +76,7 @@ const ApiKeyModal: React.FC<ApiKeyModalProps> = ({
return (
<Modal
title={mode === 'add' ? $t('Add {{vendorName}} name', { vendorName }) : $t('Edit API Key')}
title={mode === 'add' ? $t('添加 {{vendorName}} APIKey', { vendorName }) : $t('编辑 APIKey')}
open={visible}
onCancel={onCancel}
onOk={handleOk}
@@ -84,11 +84,7 @@ const ApiKeyModal: React.FC<ApiKeyModalProps> = ({
width={600}
>
<Form form={form} layout="vertical">
<Form.Item
name="id"
label={$t('* KEY Name')}
rules={[{ required: true, message: $t('Please input the KEY name') }]}
>
<Form.Item name="id" label={$t('APIKey 名称')} rules={[{ required: true, message: $t('请输入 APIKey') }]}>
<Input disabled={mode === 'edit'} />
</Form.Item>
@@ -96,15 +92,15 @@ const ApiKeyModal: React.FC<ApiKeyModalProps> = ({
name="name"
label={
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<span>{$t('* API KEY')}</span>
<span>{$t('API Key')}</span>
{mode === 'add' && (
<a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer">
{$t('Get API KEY from OpenAI')}
{$t('从 OpenAI 获取 API Key')}
</a>
)}
</div>
}
rules={[{ required: true, message: $t('Please input the API key') }]}
rules={[{ required: true, message: $t('请输入 API Key') }]}
>
{mode === 'add' ? (
<TextArea
@@ -125,8 +121,8 @@ const ApiKeyModal: React.FC<ApiKeyModalProps> = ({
<Form.Item name="neverExpire" valuePropName="checked">
<Switch
checkedChildren={$t('Never Expire')}
unCheckedChildren={$t('Set Expiration')}
checkedChildren={$t('永不过期')}
unCheckedChildren={$t('设置过期时间')}
onChange={handleNeverExpireChange}
/>
</Form.Item>
@@ -134,16 +130,16 @@ const ApiKeyModal: React.FC<ApiKeyModalProps> = ({
{!neverExpire && (
<Form.Item
name="expire_time"
label={$t('Expiration Date')}
rules={[{ required: true, message: $t('Please select expiration date') }]}
label={$t('过期时间')}
rules={[{ required: true, message: $t('请选择过期时间') }]}
>
<DatePicker style={{ width: '100%' }} showTime />
</Form.Item>
)}
{mode === 'edit' && (
<Form.Item name="enabled" label={$t('Enabled')} valuePropName="checked">
<Switch checkedChildren={$t('Yes')} unCheckedChildren={$t('No')} />
<Form.Item name="enabled" label={$t('启用状态')} valuePropName="checked">
<Switch checkedChildren={$t('')} unCheckedChildren={$t('')} />
</Form.Item>
)}
</Form>
@@ -29,20 +29,9 @@ const KeySettings: React.FC = () => {
const [editingKey, setEditingKey] = useState<APIKey | null>(null)
const [apiKeys, setApiKeys] = useState<APIKey[]>([])
const { fetchData } = useFetch()
const [searchWord, setSearchWord] = useState<string>('')
useEffect(() => {
fetchData<BasicResponse<{ data: APIKey[] }>>('ai/resource/keys', {
method: 'GET',
eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
}).then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setApiKeys(data.keys)
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
})
}, [])
useEffect(() => {}, [])
const handleEdit = (record: APIKey) => {
setEditingKey(record)
@@ -104,6 +93,41 @@ const KeySettings: React.FC = () => {
)
}
const requestApiKeys = async (params: { pageSize: number; current: number }) => {
try {
const response = await fetchData<BasicResponse<{ data: APIKey[] }>>('ai/resource/keys', {
method: 'GET',
eoParams: {
provider: selectedProvider,
page_size: params.pageSize,
keyword: searchWord,
page: params.current
},
eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
})
if (response.code === STATUS_CODE.SUCCESS) {
return {
data: response.data.keys,
success: true,
total: response.data.total
}
} else {
message.error(response.msg || $t(RESPONSE_TIPS.error))
return {
data: [],
success: false,
total: response.data.total
}
}
} catch (error) {
return {
data: [],
success: false,
total: 0
}
}
}
const statusEnum = {
normal: { text: <Typography.Text type="success">{$t('正常')}</Typography.Text> },
exceeded: { text: <Typography.Text type="warning">{$t('超额')}</Typography.Text> },
@@ -140,6 +164,11 @@ const KeySettings: React.FC = () => {
]
const columns: PageProColumns<APIKey>[] = [
{
title: '',
dataIndex: 'drag',
width: '40px'
},
{
title: $t('调用优先级'),
dataIndex: 'priority',
@@ -197,45 +226,18 @@ const KeySettings: React.FC = () => {
showBorder={false}
scrollPage={false}
>
<div className="h-[calc(100%-1rem)]">
<div className="h-[calc(100%-1rem-36px)]">
<PageList
actionRef={actionRef}
rowKey="id"
request={async (params) => {
try {
const response = await fetchData<BasicResponse<{ data: APIKey[] }>>('ai/resource/keys', {
method: 'GET',
eoParams: {
provider: selectedProvider,
...params
},
eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
})
if (response.code === STATUS_CODE.SUCCESS) {
return {
data: response.data.keys,
success: true,
total: response.data.keys.length
}
} else {
message.error(response.msg || $t(RESPONSE_TIPS.error))
return {
data: [],
success: false,
total: 0
}
}
} catch (error) {
return {
data: [],
success: false,
total: 0
}
}
request={requestApiKeys}
onSearchWordChange={(e) => {
setSearchWord(e.target.value)
}}
showPagination={true}
searchPlaceholder={$t('请输入名称搜索')}
columns={columns}
dragSortKey="sort"
dragSortKey="drag"
// onDragSortEnd={handleDragSortEnd}
addNewBtnTitle={$t('添加 APIKey')}
onAddNewBtnClick={handleAdd}
@@ -52,7 +52,7 @@ const SystemList: FC = () => {
eoTransformKeys: ['api_num', 'service_num', 'create_time']
}
)
.then(response => {
.then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setTableListDataSource(data.services)
@@ -78,7 +78,7 @@ const SystemList: FC = () => {
fetchData<BasicResponse<{ teams: SimpleTeamItem[] }>>(
!checkPermission('system.workspace.team.view_all') ? 'simple/teams/mine' : 'simple/teams',
{ method: 'GET', eoTransformKeys: [] }
).then(response => {
).then((response) => {
const { code, data, msg } = response
setTeamList(data.teams)
if (code === STATUS_CODE.SUCCESS) {
@@ -101,10 +101,9 @@ const SystemList: FC = () => {
const getMemberList = async () => {
setMemberValueEnum({})
const { code, data, msg } = await fetchData<BasicResponse<{ members: SimpleMemberItem[] }>>(
'simple/member',
{ method: 'GET' }
)
const { code, data, msg } = await fetchData<BasicResponse<{ members: SimpleMemberItem[] }>>('simple/member', {
method: 'GET'
})
if (code === STATUS_CODE.SUCCESS) {
const tmpValueEnum: { [k: string]: { text: string } } = {}
data.members?.forEach((x: SimpleMemberItem) => {
@@ -131,7 +130,7 @@ const SystemList: FC = () => {
}
const columns = useMemo(() => {
const res = SYSTEM_TABLE_COLUMNS.map(x => {
const res = SYSTEM_TABLE_COLUMNS.map((x) => {
const dataIndex = x.dataIndex as string[]
if (x.filters && dataIndex?.indexOf('master') !== -1) {
@@ -142,8 +141,8 @@ const SystemList: FC = () => {
}
if ((x.dataIndex as string) === 'service_kind') {
x.valueEnum = {}
SERVICE_KIND_OPTIONS.forEach(option => {
(x.valueEnum as any)[option.value] = { text: $t(option.label) }
SERVICE_KIND_OPTIONS.forEach((option) => {
;(x.valueEnum as any)[option.value] = { text: $t(option.label) }
})
}
@@ -188,13 +187,11 @@ const SystemList: FC = () => {
onChange={() => {
setTableHttpReload(false)
}}
onSearchWordChange={e => {
onSearchWordChange={(e) => {
setTableSearchWord(e.target.value)
}}
onRowClick={(row: SystemTableListItem) =>
navigate(
`/service/${row.team.id}/${row.service_kind === 'ai' ? 'aiInside' : 'inside'}/${row.id}`
)
navigate(`/service/${row.team.id}/${row.service_kind === 'ai' ? 'aiInside' : 'inside'}/${row.id}`)
}
/>
<DrawerWithFooter
@@ -202,7 +199,7 @@ const SystemList: FC = () => {
open={open}
onClose={onClose}
onSubmit={() =>
drawerFormRef.current?.save()?.then(res => {
drawerFormRef.current?.save()?.then((res) => {
res && manualReloadTable()
return res
})