From 2c9f5062a952b61eb7931f1a7b559d4392b45ce9 Mon Sep 17 00:00:00 2001 From: jialin Date: Mon, 13 Oct 2025 16:21:00 +0800 Subject: [PATCH] refactor: user table list --- src/locales/en-US/models.ts | 3 +- src/locales/ja-JP/models.ts | 6 +- src/locales/ru-RU/models.ts | 6 +- src/locales/zh-CN/models.ts | 3 +- .../llmodels/components/instance-item.tsx | 13 +- .../llmodels/components/search-model.tsx | 13 +- .../resources/hooks/use-worker-columns.tsx | 11 +- src/pages/users/components/add-modal.tsx | 2 +- src/pages/users/hooks/use-users-columns.tsx | 163 +++++++++ src/pages/users/index.tsx | 319 ++++-------------- 10 files changed, 254 insertions(+), 285 deletions(-) create mode 100644 src/pages/users/hooks/use-users-columns.tsx diff --git a/src/locales/en-US/models.ts b/src/locales/en-US/models.ts index 2b0767b1..b1747839 100644 --- a/src/locales/en-US/models.ts +++ b/src/locales/en-US/models.ts @@ -181,5 +181,6 @@ export default { 'models.table.noselected': 'No users selected', 'models.table.users.all': 'All Users', 'models.table.users.selected': 'Selected Users', - 'models.table.nouserFound': 'No users found' + 'models.table.nouserFound': 'No users found', + 'models.form.performance': 'Performance' }; diff --git a/src/locales/ja-JP/models.ts b/src/locales/ja-JP/models.ts index ca8c72b6..0f9d888f 100644 --- a/src/locales/ja-JP/models.ts +++ b/src/locales/ja-JP/models.ts @@ -178,7 +178,8 @@ export default { 'models.table.noselected': 'No users selected', 'models.table.users.all': 'All Users', 'models.table.users.selected': 'Selected Users', - 'models.table.nouserFound': 'No users found' + 'models.table.nouserFound': 'No users found', + 'models.form.performance': 'Performance' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -200,5 +201,6 @@ export default { // 16. 'models.table.nouserFound': 'No users found', // 17. 'models.table.users.all': 'All Users', // 18. 'models.table.users.selected': 'Selected Users', -// 19. 'models.table.nouserFound': 'No users found' +// 19. 'models.table.nouserFound': 'No users found', +// 20. 'models.form.performance': 'Performance' // ========== End of To-Do List ========== diff --git a/src/locales/ru-RU/models.ts b/src/locales/ru-RU/models.ts index 9a07cc69..166a8706 100644 --- a/src/locales/ru-RU/models.ts +++ b/src/locales/ru-RU/models.ts @@ -181,7 +181,8 @@ export default { 'models.table.noselected': 'No users selected', 'models.table.users.all': 'All users', 'models.table.users.selected': 'Selected users', - 'models.table.nouserFound': 'No users found' + 'models.table.nouserFound': 'No users found', + 'models.form.performance': 'Performance' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -195,5 +196,6 @@ export default { // 8. 'models.table.noselected': 'No users selected', // 9. 'models.table.users.all': 'All users', // 10. 'models.table.users.selected': 'Selected users', -// 11. 'models.table.nouserFound': 'No users found' +// 11. 'models.table.nouserFound': 'No users found', +// 12. 'models.form.performance': 'Performance' // ========== End of To-Do List ========== diff --git a/src/locales/zh-CN/models.ts b/src/locales/zh-CN/models.ts index 73919256..6c44bab8 100644 --- a/src/locales/zh-CN/models.ts +++ b/src/locales/zh-CN/models.ts @@ -171,5 +171,6 @@ export default { 'models.table.noselected': '未选择用户', 'models.table.users.all': '所有用户', 'models.table.users.selected': '已选用户', - 'models.table.nouserFound': '未找到用户' + 'models.table.nouserFound': '未找到用户', + 'models.form.performance': '性能' }; diff --git a/src/pages/llmodels/components/instance-item.tsx b/src/pages/llmodels/components/instance-item.tsx index b607b607..80ce785c 100644 --- a/src/pages/llmodels/components/instance-item.tsx +++ b/src/pages/llmodels/components/instance-item.tsx @@ -12,7 +12,6 @@ import useDownloadStream from '@/hooks/use-download-stream'; import { ListItem as WorkerListItem } from '@/pages/resources/config/types'; import { convertFileSize } from '@/utils'; import { - CodeOutlined, DeleteOutlined, DownloadOutlined, HddFilled, @@ -273,12 +272,12 @@ const childActionList = [ ], icon: }, - { - label: 'Terminal', - locale: false, - key: 'terminal', - icon: - }, + // { + // label: 'Terminal', + // locale: false, + // key: 'terminal', + // icon: + // }, { label: 'common.button.downloadLog', key: 'download', diff --git a/src/pages/llmodels/components/search-model.tsx b/src/pages/llmodels/components/search-model.tsx index 58c91e2b..d7589bb7 100644 --- a/src/pages/llmodels/components/search-model.tsx +++ b/src/pages/llmodels/components/search-model.tsx @@ -23,6 +23,7 @@ import SearchResult from './search-result'; const filterOptions = [ { label: 'AWQ', value: 'awq' }, { label: 'GPTQ', value: 'gptq' } + // { label: 'GGUF', value: 'gguf' } ]; const PaginationMain = styled(Pagination)` .ant-pagination-slash { @@ -532,13 +533,9 @@ const SearchModel: React.FC = (props) => { { - return ( - - {intl.formatMessage({ id: 'model.deploy.sort' })}: {label} - - ); - }} + prefix={ + {intl.formatMessage({ id: 'model.deploy.sort' })}: + } options={modelFilesSortOptions} size="middle" style={{ width: '150px' }} @@ -549,7 +546,7 @@ const SearchModel: React.FC = (props) => { onChange={handleFilterChange} options={filterOptions} size="middle" - placeholder="quantization type" + placeholder={intl.formatMessage({ id: 'common.input.type' })} style={{ width: 150 }} > diff --git a/src/pages/resources/hooks/use-worker-columns.tsx b/src/pages/resources/hooks/use-worker-columns.tsx index 9f811932..531b4072 100644 --- a/src/pages/resources/hooks/use-worker-columns.tsx +++ b/src/pages/resources/hooks/use-worker-columns.tsx @@ -9,7 +9,6 @@ import { DeleteOutlined, DownloadOutlined, EditOutlined, - FileTextOutlined, InfoCircleOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; @@ -22,11 +21,11 @@ import { Filesystem, GPUDeviceItem, ListItem } from '../config/types'; const ActionList = [ { label: 'common.button.edit', key: 'edit', icon: }, - { - label: 'common.button.detail', - key: 'details', - icon: - }, + // { + // label: 'common.button.detail', + // key: 'details', + // icon: + // }, { label: 'resources.worker.download.privatekey', key: 'download_ssh_key', diff --git a/src/pages/users/components/add-modal.tsx b/src/pages/users/components/add-modal.tsx index b0a8fc01..7a87a74d 100644 --- a/src/pages/users/components/add-modal.tsx +++ b/src/pages/users/components/add-modal.tsx @@ -17,7 +17,7 @@ type AddModalProps = { action: PageActionType; open: boolean; onOk: (values: FormData) => void; - data?: ListItem; + data?: ListItem | null; onCancel: () => void; }; const AddModal: React.FC = ({ diff --git a/src/pages/users/hooks/use-users-columns.tsx b/src/pages/users/hooks/use-users-columns.tsx new file mode 100644 index 00000000..264e645d --- /dev/null +++ b/src/pages/users/hooks/use-users-columns.tsx @@ -0,0 +1,163 @@ +// columns.ts +import AutoTooltip from '@/components/auto-tooltip'; +import DropdownButtons from '@/components/drop-down-buttons'; +import IconFont from '@/components/icon-font'; +import icons from '@/components/icon-font/icons'; +import { useIntl } from '@umijs/max'; +import { Tag } from 'antd'; +import type { SortOrder } from 'antd/es/table/interface'; +import { ColumnsType } from 'antd/lib/table'; +import dayjs from 'dayjs'; +import { useMemo } from 'react'; +import { ListItem } from '../config/types'; + +interface ColumnsHookProps { + handleSelect: (val: string, record: ListItem) => void; + sortOrder: SortOrder; +} + +const actionList: Global.ActionItem[] = [ + { + label: 'common.button.edit', + key: 'edit', + icon: icons.EditOutlined + }, + { + label: 'common.button.delete', + key: 'delete', + icon: icons.DeleteOutlined, + props: { danger: true } + } +]; + +const useUsersColumns = ({ + handleSelect, + sortOrder +}: ColumnsHookProps): ColumnsType => { + const intl = useIntl(); + + return useMemo(() => { + return [ + { + title: intl.formatMessage({ id: 'common.table.name' }), + dataIndex: 'username', + key: 'username', + render: (text: string, record: ListItem) => ( + + {text} + + ) + }, + { + title: intl.formatMessage({ id: 'users.table.role' }), + dataIndex: 'role', + key: 'role', + ellipsis: { + showTitle: false + }, + render: (text: string, record: ListItem) => { + return record.is_admin ? ( + + + + {intl.formatMessage({ id: 'users.form.admin' })} + + + ) : ( + + + + {intl.formatMessage({ id: 'users.form.user' })} + + + ); + } + }, + { + title: intl.formatMessage({ id: 'users.form.fullname' }), + dataIndex: 'full_name', + key: 'full_name', + ellipsis: { + showTitle: false + }, + render: (text: string, record: ListItem) => ( + + {text} + + ) + }, + { + title: intl.formatMessage({ id: 'users.form.source' }), + dataIndex: 'source', + key: 'source', + ellipsis: { + showTitle: false + }, + render: (text: string[], record: ListItem) => ( + + {text} + + ) + }, + { + title: intl.formatMessage({ id: 'users.table.status' }), + dataIndex: 'is_active', + key: 'is_active', + ellipsis: { + showTitle: false + }, + render: (text: string, record: ListItem) => { + return ( + <> + + {intl.formatMessage({ + id: record.is_active + ? 'users.status.active' + : 'users.status.inactive' + })} + + + ); + } + }, + { + title: intl.formatMessage({ id: 'common.table.createTime' }), + dataIndex: 'created_at', + key: 'created_at', + defaultSortOrder: 'descend', + showSorterTooltip: false, + sortOrder, + sorter: false, + ellipsis: { + showTitle: false + }, + render: (text: number) => ( + + {dayjs(text).format('YYYY-MM-DD HH:mm:ss')} + + ) + }, + { + title: intl.formatMessage({ id: 'common.table.operation' }), + key: 'operation', + dataIndex: 'operation', + span: 3, + render: (text, record) => ( + handleSelect(val, record)} + /> + ) + } + ]; + }, [sortOrder, intl, handleSelect]); +}; + +export default useUsersColumns; diff --git a/src/pages/users/index.tsx b/src/pages/users/index.tsx index e013ce26..15ed07fd 100644 --- a/src/pages/users/index.tsx +++ b/src/pages/users/index.tsx @@ -1,40 +1,18 @@ -import AutoTooltip from '@/components/auto-tooltip'; import DeleteModal from '@/components/delete-modal'; -import DropdownButtons from '@/components/drop-down-buttons'; -import IconFont from '@/components/icon-font'; -import PageTools from '@/components/page-tools'; +import { FilterBar } from '@/components/page-tools'; import { PageAction } from '@/config'; -import HotKeys from '@/config/hotkeys'; import type { PageActionType } from '@/config/types'; import useTableFetch from '@/hooks/use-table-fetch'; -import { - DeleteOutlined, - EditOutlined, - PlusOutlined, - SearchOutlined, - SyncOutlined -} from '@ant-design/icons'; import { PageContainer } from '@ant-design/pro-components'; import { useIntl } from '@umijs/max'; -import { - Button, - ConfigProvider, - Empty, - Input, - message, - Space, - Switch, - Table, - Tag -} from 'antd'; -import dayjs from 'dayjs'; +import { useMemoizedFn } from 'ahooks'; +import { ConfigProvider, Empty, message, Switch, Table } from 'antd'; import { useState } from 'react'; -import { useHotkeys } from 'react-hotkeys-hook'; import styled from 'styled-components'; import { createUser, deleteUser, queryUsersList, updateUser } from './apis'; import AddModal from './components/add-modal'; import { FormData, ListItem } from './config/types'; -const { Column } = Table; +import useUsersColumns from './hooks/use-users-columns'; const StyledSwitch = styled(Switch)` .ant-switch-handle::before { @@ -43,22 +21,6 @@ const StyledSwitch = styled(Switch)` } `; -const ActionList = [ - { - key: 'edit', - label: 'common.button.edit', - icon: - }, - { - key: 'delete', - props: { - danger: true - }, - label: 'common.button.delete', - icon: - } -]; - const Users: React.FC = () => { const { dataSource, @@ -80,65 +42,81 @@ const Users: React.FC = () => { }); const intl = useIntl(); - const [openAddModal, setOpenAddModal] = useState(false); - - const [action, setAction] = useState(PageAction.CREATE); - const [title, setTitle] = useState(''); - const [currentData, setCurrentData] = useState( - undefined - ); + const [openAddModalStatus, setOpenAddModalStatus] = useState<{ + action: PageActionType; + open: boolean; + title: string; + currentData?: ListItem | null; + }>({ + action: PageAction.CREATE, + title: '', + open: false, + currentData: null + }); const handleAddUser = () => { - setOpenAddModal(true); - setAction(PageAction.CREATE); - setTitle(intl.formatMessage({ id: 'users.form.create' })); + setOpenAddModalStatus({ + action: PageAction.CREATE, + title: intl.formatMessage({ id: 'users.form.create' }), + open: true, + currentData: null + }); }; const handleModalOk = async (data: FormData) => { const params = { - ...currentData, + ...openAddModalStatus.currentData, ...data, is_admin: data.is_admin === 'admin' }; try { - if (action === PageAction.EDIT) { + if (openAddModalStatus.action === PageAction.EDIT) { await updateUser({ data: { ...params, - id: currentData?.id + id: openAddModalStatus.currentData?.id } }); } else { await createUser({ data: params }); } fetchData(); - setOpenAddModal(false); + setOpenAddModalStatus({ + ...openAddModalStatus, + open: false + }); message.success(intl.formatMessage({ id: 'common.message.success' })); } catch (error) { - setOpenAddModal(false); + setOpenAddModalStatus({ ...openAddModalStatus, open: false }); message.error(intl.formatMessage({ id: 'common.message.fail' })); } }; const handleModalCancel = () => { console.log('handleModalCancel'); - setOpenAddModal(false); + setOpenAddModalStatus({ + ...openAddModalStatus, + open: false, + currentData: null + }); }; const handleEditUser = (row: ListItem) => { - setCurrentData(row); - setOpenAddModal(true); - setAction(PageAction.EDIT); - setTitle(intl.formatMessage({ id: 'users.form.edit' })); + setOpenAddModalStatus({ + title: intl.formatMessage({ id: 'users.form.edit' }), + action: PageAction.EDIT, + open: true, + currentData: row + }); }; - const handleSelect = (val: any, row: ListItem) => { + const handleSelect = useMemoizedFn((val: any, row: ListItem) => { if (val === 'edit') { handleEditUser(row); } else if (val === 'delete') { handleDelete({ ...row, name: row.username }); } - }; + }); const handleActiveChange = async (checked: boolean, row: ListItem) => { try { @@ -168,15 +146,10 @@ const Users: React.FC = () => { return
; }; - useHotkeys( - HotKeys.CREATE, - () => { - handleAddUser(); - }, - { - enabled: !openAddModal - } - ); + const columns = useUsersColumns({ + handleSelect, + sortOrder + }); return ( <> @@ -191,56 +164,20 @@ const Users: React.FC = () => { }} extra={[]} > - - - } - placeholder={intl.formatMessage({ id: 'common.filter.name' })} - style={{ width: 300 }} - allowClear - onChange={handleNameChange} - > - - - } - right={ - - - - - } - > + marginTop={30} + buttonText={intl.formatMessage({ id: 'users.button.create' })} + handleSearch={handleSearch} + handleDeleteByBatch={handleDeleteBatch} + handleClickPrimary={handleAddUser} + handleInputChange={handleNameChange} + rowSelection={rowSelection} + width={{ input: 300 }} + > { hideOnSinglePage: queryParams.perPage === 10, onChange: handlePageChange }} - > - { - return ( - - {text} - - ); - }} - /> - { - return record.is_admin ? ( - - - - {intl.formatMessage({ id: 'users.form.admin' })} - - - ) : ( - - - - {intl.formatMessage({ id: 'users.form.user' })} - - - ); - }} - /> - { - return ( - - {text} - - ); - }} - /> - { - return ( - - {text} - - ); - }} - /> - { - return ( - <> - - {intl.formatMessage({ - id: record.is_active - ? 'users.status.active' - : 'users.status.inactive' - })} - - - ); - }} - /> - { - return ( - - {dayjs(text).format('YYYY-MM-DD HH:mm:ss')} - - ); - }} - /> - { - return ( - handleSelect(val, record)} - > - ); - }} - /> -
+ >