chore: api request
This commit is contained in:
@@ -1,7 +1,14 @@
|
||||
import { defineConfig } from '@umijs/max';
|
||||
import proxy from './proxy';
|
||||
import routes from './routes';
|
||||
const { REACT_APP_ENV = 'dev' } = process.env;
|
||||
|
||||
console.log('env+++++++++++++++', process.env.NODE_ENV, REACT_APP_ENV);
|
||||
|
||||
export default defineConfig({
|
||||
proxy: {
|
||||
...proxy()
|
||||
},
|
||||
clickToComponent: {},
|
||||
antd: {
|
||||
style: 'less',
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
const proxyTableList = ['cli', 'v1'];
|
||||
|
||||
// @ts-ingore
|
||||
export default function createProxyTable(target?: string) {
|
||||
const proxyTable = proxyTableList.reduce(
|
||||
(obj: Record<string, object>, api) => {
|
||||
const newTarget = target || 'http://localhost';
|
||||
obj[`/${api}/`] = {
|
||||
target: newTarget,
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
pathRewrite: (pth: string) => pth.replace(`/^/${api}/`, `/${api}`),
|
||||
headers: {
|
||||
origin: newTarget
|
||||
}
|
||||
};
|
||||
return obj;
|
||||
},
|
||||
{}
|
||||
);
|
||||
return proxyTable;
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
"antd-style": "^3.6.2",
|
||||
"classnames": "^2.5.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.11",
|
||||
"lodash": "^4.17.21",
|
||||
"numeral": "^2.0.6"
|
||||
},
|
||||
|
||||
Generated
+3
@@ -35,6 +35,9 @@ dependencies:
|
||||
crypto-js:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
dayjs:
|
||||
specifier: ^1.11.11
|
||||
version: 1.11.11
|
||||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { RequestConfig } from '@umijs/max';
|
||||
import { requestConfig } from './request-config';
|
||||
// 运行时配置
|
||||
|
||||
// 全局初始化数据配置,用于 Layout 用户信息和权限初始化
|
||||
@@ -8,3 +10,8 @@ export async function getInitialState(): Promise<{ name: string }> {
|
||||
name: 'admin'
|
||||
};
|
||||
}
|
||||
|
||||
export const request: RequestConfig = {
|
||||
baseURL: ' /v1',
|
||||
...requestConfig
|
||||
};
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
import { Button, Space } from 'antd';
|
||||
|
||||
type ModalFooterProps = {
|
||||
onOk: () => void;
|
||||
onOk?: () => void;
|
||||
onCancel: () => void;
|
||||
cancelText?: string;
|
||||
okText?: string;
|
||||
htmlType?: 'button' | 'submit';
|
||||
form?: any;
|
||||
};
|
||||
const ModalFooter: React.FC<ModalFooterProps> = ({
|
||||
onOk,
|
||||
onCancel,
|
||||
cancelText,
|
||||
okText
|
||||
okText,
|
||||
htmlType = 'button',
|
||||
form
|
||||
}) => {
|
||||
return (
|
||||
<Space size={20}>
|
||||
<Button onClick={onCancel} style={{ width: '88px' }}>
|
||||
{cancelText || '取消'}
|
||||
</Button>
|
||||
<Button type="primary" onClick={onOk} style={{ width: '88px' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={onOk}
|
||||
style={{ width: '88px' }}
|
||||
htmlType={htmlType}
|
||||
>
|
||||
{okText || '保存'}
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
@@ -4,10 +4,11 @@ import { useEffect, useState } from 'react';
|
||||
import styles from './index.less';
|
||||
|
||||
export const StatusMaps = {
|
||||
running: 'blue',
|
||||
transitioning: 'blue',
|
||||
error: 'red',
|
||||
warning: 'orange',
|
||||
success: 'success'
|
||||
success: 'success',
|
||||
inactive: 'inactive'
|
||||
};
|
||||
|
||||
type StatusTagProps = {
|
||||
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
declare namespace Global {
|
||||
interface Pagination {
|
||||
page: number;
|
||||
perPage: number;
|
||||
watch?: boolean;
|
||||
}
|
||||
interface PageResponse<T> {
|
||||
items: T[];
|
||||
pagination: {
|
||||
total: number;
|
||||
totalPage: number;
|
||||
page: number;
|
||||
perPage: number;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { request } from '@umijs/max';
|
||||
import { ListItem } from '../config/types';
|
||||
|
||||
export const NODES_API = '/nodes';
|
||||
|
||||
export async function queryNodesList(
|
||||
params: Global.Pagination & { query?: string }
|
||||
) {
|
||||
return request<Global.PageResponse<ListItem>>(`${NODES_API}`, {
|
||||
methos: 'GET',
|
||||
params
|
||||
});
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import useTableSort from '@/hooks/use-table-sort';
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { Button, Input, Space, Table } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import { NodeItem } from '../config/types';
|
||||
import { Gpu } from '../config/types';
|
||||
import RenderProgress from './render-progress';
|
||||
const { Column } = Table;
|
||||
|
||||
const dataSource: NodeItem[] = [
|
||||
const dataSource: Gpu[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'bj-web-service-1',
|
||||
@@ -161,7 +161,6 @@ const Models: React.FC = () => {
|
||||
></PageTools>
|
||||
<Table
|
||||
dataSource={dataSource}
|
||||
rowSelection={rowSelection}
|
||||
loading={loading}
|
||||
rowKey="id"
|
||||
onChange={handleTableChange}
|
||||
@@ -193,7 +192,11 @@ const Models: React.FC = () => {
|
||||
/>
|
||||
<Column title="IP" dataIndex="address" key="address" />
|
||||
|
||||
<Column title="Temperature" dataIndex="Temperature" key="Temperature" />
|
||||
<Column
|
||||
title="Temperature(˚C)"
|
||||
dataIndex="Temperature"
|
||||
key="Temperature"
|
||||
/>
|
||||
<Column title="Core" dataIndex="core" key="Core" />
|
||||
<Column title="GPU-Util" dataIndex="gpuUtil" key="gpuUtil" />
|
||||
|
||||
@@ -201,10 +204,8 @@ const Models: React.FC = () => {
|
||||
title="VRAM"
|
||||
dataIndex="GRAM"
|
||||
key="VRAM"
|
||||
render={(text, record: NodeItem) => {
|
||||
return (
|
||||
<RenderProgress record={record} dataIndex="gram"></RenderProgress>
|
||||
);
|
||||
render={(text, record: Gpu) => {
|
||||
return <RenderProgress percent={0}></RenderProgress>;
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
|
||||
@@ -4,129 +4,59 @@ import useTableRowSelection from '@/hooks/use-table-row-selection';
|
||||
import useTableSort from '@/hooks/use-table-sort';
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { Button, Input, Space, Table } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import { NodeItem } from '../config/types';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { queryNodesList } from '../apis';
|
||||
import { ListItem } from '../config/types';
|
||||
import RenderProgress from './render-progress';
|
||||
const { Column } = Table;
|
||||
|
||||
const dataSource: NodeItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'bj-web-service-1',
|
||||
address: '183.14.31.136',
|
||||
hostname: 'bj-web-service-1',
|
||||
labels: {},
|
||||
resources: {
|
||||
capacity: {
|
||||
cpu: 4,
|
||||
gpu: 2,
|
||||
memory: '64 GiB',
|
||||
gram: '24 Gib'
|
||||
},
|
||||
allocable: {
|
||||
cpu: 2.5,
|
||||
gpu: 1.6,
|
||||
memory: '64',
|
||||
gram: '24 Gib'
|
||||
}
|
||||
},
|
||||
state: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'bj-db-service-2',
|
||||
address: '172.24.1.36',
|
||||
hostname: 'bj-db-service-2',
|
||||
labels: {},
|
||||
resources: {
|
||||
capacity: {
|
||||
cpu: 4,
|
||||
gpu: 2,
|
||||
memory: '64 GiB',
|
||||
gram: '24 Gib'
|
||||
},
|
||||
allocable: {
|
||||
cpu: 2,
|
||||
gpu: 1.5,
|
||||
memory: '32 GiB',
|
||||
gram: '12 Gib'
|
||||
}
|
||||
},
|
||||
state: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'guangzhou-computed-node-2',
|
||||
address: '170.10.2.10',
|
||||
hostname: 'guangzhou-computed-node-2',
|
||||
labels: {},
|
||||
resources: {
|
||||
capacity: {
|
||||
cpu: 8,
|
||||
gpu: 4,
|
||||
memory: '64 GiB',
|
||||
gram: '24 Gib'
|
||||
},
|
||||
allocable: {
|
||||
cpu: 2,
|
||||
gpu: 1.5,
|
||||
memory: '32 GiB',
|
||||
gram: '12 Gib'
|
||||
}
|
||||
},
|
||||
state: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'hangzhou-cache-node-1',
|
||||
address: '115.2.21.10',
|
||||
hostname: 'hangzhou-cache-node-1',
|
||||
labels: {},
|
||||
resources: {
|
||||
capacity: {
|
||||
cpu: 8,
|
||||
gpu: 4,
|
||||
memory: '64 GiB',
|
||||
gram: '24 Gib'
|
||||
},
|
||||
allocable: {
|
||||
cpu: 4,
|
||||
gpu: 2.5,
|
||||
memory: '40 GiB',
|
||||
gram: '16 Gib'
|
||||
}
|
||||
},
|
||||
state: 'ACTIVE'
|
||||
}
|
||||
];
|
||||
|
||||
const Models: React.FC = () => {
|
||||
const rowSelection = useTableRowSelection();
|
||||
const { sortOrder, setSortOrder } = useTableSort({
|
||||
defaultSortOrder: 'descend'
|
||||
});
|
||||
const [total, setTotal] = useState(10);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [dataSource, setDataSource] = useState<ListItem[]>([]);
|
||||
const [queryParams, setQueryParams] = useState({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
name: ''
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
query: ''
|
||||
});
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = {
|
||||
..._.pickBy(queryParams, (val: any) => !!val)
|
||||
};
|
||||
const res = await queryNodesList(params);
|
||||
console.log('res=======', res);
|
||||
setDataSource(res.items);
|
||||
setTotal(res.pagination.total);
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const handleShowSizeChange = (current: number, size: number) => {
|
||||
console.log(current, size);
|
||||
};
|
||||
|
||||
const handlePageChange = (page: number, pageSize: number | undefined) => {
|
||||
console.log(page, pageSize);
|
||||
const handlePageChange = (page: number, perPage: number | undefined) => {
|
||||
console.log(page, perPage);
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
page: page
|
||||
});
|
||||
};
|
||||
|
||||
const handleTableChange = (pagination: any, filters: any, sorter: any) => {
|
||||
setSortOrder(sorter.order);
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
console.log('fetchData');
|
||||
};
|
||||
const handleSearch = (e: any) => {
|
||||
fetchData();
|
||||
};
|
||||
@@ -134,10 +64,20 @@ const Models: React.FC = () => {
|
||||
const handleNameChange = (e: any) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
name: e.target.value
|
||||
query: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
const formateUtilazation = (val1: number, val2: number): number => {
|
||||
if (!val2 || !val1) {
|
||||
return 0;
|
||||
}
|
||||
return _.round((val1 / val2) * 100, 2);
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [queryParams]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTools
|
||||
@@ -161,14 +101,13 @@ const Models: React.FC = () => {
|
||||
></PageTools>
|
||||
<Table
|
||||
dataSource={dataSource}
|
||||
rowSelection={rowSelection}
|
||||
loading={loading}
|
||||
rowKey="id"
|
||||
onChange={handleTableChange}
|
||||
pagination={{
|
||||
showSizeChanger: true,
|
||||
pageSize: 10,
|
||||
current: 2,
|
||||
pageSize: queryParams.perPage,
|
||||
current: queryParams.page,
|
||||
total: total,
|
||||
hideOnSinglePage: true,
|
||||
onShowSizeChange: handleShowSizeChange,
|
||||
@@ -180,12 +119,13 @@ const Models: React.FC = () => {
|
||||
title="State"
|
||||
dataIndex="state"
|
||||
key="state"
|
||||
render={(text, record) => {
|
||||
render={(text, record: ListItem) => {
|
||||
return (
|
||||
<StatusTag
|
||||
statusValue={{
|
||||
status: 'success',
|
||||
text: 'ALIVE'
|
||||
status:
|
||||
record.status?.state === 'active' ? 'success' : 'error',
|
||||
text: record.status?.state
|
||||
}}
|
||||
></StatusTag>
|
||||
);
|
||||
@@ -196,9 +136,11 @@ const Models: React.FC = () => {
|
||||
title="CPU"
|
||||
dataIndex="CPU"
|
||||
key="CPU"
|
||||
render={(text, record: NodeItem) => {
|
||||
render={(text, record: ListItem) => {
|
||||
return (
|
||||
<RenderProgress record={record} dataIndex="cpu"></RenderProgress>
|
||||
<RenderProgress
|
||||
percent={_.round(record?.status?.cpu.utilization_rate, 2)}
|
||||
></RenderProgress>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@@ -206,11 +148,13 @@ const Models: React.FC = () => {
|
||||
title="Memory"
|
||||
dataIndex="memory"
|
||||
key="Memory"
|
||||
render={(text, record: NodeItem) => {
|
||||
render={(text, record: ListItem) => {
|
||||
return (
|
||||
<RenderProgress
|
||||
record={record}
|
||||
dataIndex="memory"
|
||||
percent={formateUtilazation(
|
||||
record?.status?.memory.used,
|
||||
record?.status?.memory.total
|
||||
)}
|
||||
></RenderProgress>
|
||||
);
|
||||
}}
|
||||
@@ -219,20 +163,16 @@ const Models: React.FC = () => {
|
||||
title="GPU"
|
||||
dataIndex="GPU"
|
||||
key="GPU"
|
||||
render={(text, record: NodeItem) => {
|
||||
return (
|
||||
<RenderProgress record={record} dataIndex="gpu"></RenderProgress>
|
||||
);
|
||||
render={(text, record: ListItem) => {
|
||||
return <RenderProgress percent={0}></RenderProgress>;
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="VRAM"
|
||||
dataIndex="GRAM"
|
||||
key="VRAM"
|
||||
render={(text, record: NodeItem) => {
|
||||
return (
|
||||
<RenderProgress record={record} dataIndex="gram"></RenderProgress>
|
||||
);
|
||||
render={(text, record: ListItem) => {
|
||||
return <RenderProgress percent={0}></RenderProgress>;
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
|
||||
@@ -1,53 +1,50 @@
|
||||
import { Progress } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { NodeItem } from '../config/types';
|
||||
|
||||
const RenderProgress = memo(
|
||||
(props: { record: NodeItem; dataIndex: string }) => {
|
||||
const { record, dataIndex } = props;
|
||||
const value1 = useMemo(() => {
|
||||
let value = _.get(record, ['resources', 'allocable', dataIndex]);
|
||||
if (['gram', 'memory'].includes(dataIndex)) {
|
||||
value = _.toNumber(value.replace(/GiB|Gib/, ''));
|
||||
}
|
||||
return value;
|
||||
}, [record, dataIndex]);
|
||||
const RenderProgress = memo((props: { percent: number }) => {
|
||||
const { percent } = props;
|
||||
// const { record, dataIndex } = props;
|
||||
// const value1 = useMemo(() => {
|
||||
// let value = _.get(record, ['resources', 'allocable', dataIndex]);
|
||||
// if (['gram', 'memory'].includes(dataIndex)) {
|
||||
// value = _.toNumber(value.replace(/GiB|Gib/, ''));
|
||||
// }
|
||||
// return value;
|
||||
// }, [record, dataIndex]);
|
||||
|
||||
const value2 = useMemo(() => {
|
||||
let value = _.get(record, ['resources', 'capacity', dataIndex]);
|
||||
if (['gram', 'memory'].includes(dataIndex)) {
|
||||
value = _.toNumber(value.replace(/GiB|Gib/, ''));
|
||||
}
|
||||
return value;
|
||||
}, [record, dataIndex]);
|
||||
// const value2 = useMemo(() => {
|
||||
// let value = _.get(record, ['resources', 'capacity', dataIndex]);
|
||||
// if (['gram', 'memory'].includes(dataIndex)) {
|
||||
// value = _.toNumber(value.replace(/GiB|Gib/, ''));
|
||||
// }
|
||||
// return value;
|
||||
// }, [record, dataIndex]);
|
||||
|
||||
if (!value1 || !value2) {
|
||||
return <Progress percent={0} strokeColor="var(--ant-color-primary)" />;
|
||||
// if (!value1 || !value2) {
|
||||
// return <Progress percent={0} strokeColor="var(--ant-color-primary)" />;
|
||||
// }
|
||||
// const percent = _.round(value1 / value2, 2) * 100;
|
||||
const strokeColor = useMemo(() => {
|
||||
if (percent <= 50) {
|
||||
return 'var(--ant-color-primary)';
|
||||
}
|
||||
const percent = _.round(value1 / value2, 2) * 100;
|
||||
const strokeColor = useMemo(() => {
|
||||
if (percent <= 50) {
|
||||
return 'var(--ant-color-primary)';
|
||||
}
|
||||
if (percent <= 80) {
|
||||
return 'var(--ant-color-warning)';
|
||||
}
|
||||
return 'var(--ant-color-error)';
|
||||
}, [percent]);
|
||||
return (
|
||||
<Progress
|
||||
steps={5}
|
||||
format={() => {
|
||||
return (
|
||||
<span style={{ color: 'var(--ant-color-text)' }}>{percent}%</span>
|
||||
);
|
||||
}}
|
||||
percent={percent}
|
||||
strokeColor={strokeColor}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
if (percent <= 80) {
|
||||
return 'var(--ant-color-warning)';
|
||||
}
|
||||
return 'var(--ant-color-error)';
|
||||
}, [percent]);
|
||||
return (
|
||||
<Progress
|
||||
steps={5}
|
||||
format={() => {
|
||||
return (
|
||||
<span style={{ color: 'var(--ant-color-text)' }}>{percent}%</span>
|
||||
);
|
||||
}}
|
||||
percent={percent}
|
||||
strokeColor={strokeColor}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default RenderProgress;
|
||||
|
||||
@@ -1,19 +1,68 @@
|
||||
export interface Capacity {
|
||||
cpu: number;
|
||||
gpu: number;
|
||||
memory: string;
|
||||
gram: string;
|
||||
export interface Gpu {
|
||||
uuid: string;
|
||||
name: string;
|
||||
vendor: string;
|
||||
index: number;
|
||||
core_total: number;
|
||||
core_allocated: number;
|
||||
core_utilization_rate: number;
|
||||
memory_total: number;
|
||||
memory_allocated: number;
|
||||
memory_used: number;
|
||||
temperature: number;
|
||||
}
|
||||
|
||||
export interface NodeItem {
|
||||
id: number;
|
||||
export interface Filesystem {
|
||||
name: string;
|
||||
mount_point: string;
|
||||
mount_from: string;
|
||||
total: number;
|
||||
used: number;
|
||||
free: number;
|
||||
available: number;
|
||||
}
|
||||
|
||||
export interface Kernel {
|
||||
name: string;
|
||||
release: string;
|
||||
version: string;
|
||||
architecture: string;
|
||||
}
|
||||
|
||||
export interface ListItem {
|
||||
name: string;
|
||||
hostname: string;
|
||||
address: string;
|
||||
hostname: string;
|
||||
labels: object;
|
||||
resources: {
|
||||
capacity: Capacity;
|
||||
allocable: Capacity;
|
||||
status: {
|
||||
cpu: {
|
||||
total: number;
|
||||
allocated: number;
|
||||
utilization_rate: number;
|
||||
};
|
||||
memory: {
|
||||
total: number;
|
||||
used: number;
|
||||
allocated: number;
|
||||
};
|
||||
gpu: Gpu[];
|
||||
swap: {
|
||||
total: number;
|
||||
used: number;
|
||||
};
|
||||
filesystem: Filesystem[];
|
||||
os: {
|
||||
name: string;
|
||||
version: string;
|
||||
};
|
||||
kernel: Kernel;
|
||||
uptime: {
|
||||
uptime: number;
|
||||
boot_time: string;
|
||||
};
|
||||
state: string;
|
||||
};
|
||||
state: string;
|
||||
id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { request } from '@umijs/max';
|
||||
import { FormData, ListItem } from '../config/types';
|
||||
|
||||
export const MODELS_API = '/models';
|
||||
|
||||
export async function queryModelsList(
|
||||
params: Global.Pagination & { query?: string }
|
||||
) {
|
||||
return request<Global.PageResponse<ListItem>>(`${MODELS_API}`, {
|
||||
methos: 'GET',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
export async function createModel(params: { data: FormData }) {
|
||||
return request(`${MODELS_API}`, {
|
||||
method: 'POST',
|
||||
data: params.data
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteModel(id: number) {
|
||||
return request(`${MODELS_API}/${id}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateModel(params: { id: number; data: FormData }) {
|
||||
return request(`${MODELS_API}/${params.id}`, {
|
||||
method: 'PUT',
|
||||
data: params.data
|
||||
});
|
||||
}
|
||||
@@ -1,34 +1,92 @@
|
||||
import ModalFooter from '@/components/modal-footer';
|
||||
import FieldWrapper from '@/components/seal-form/field-wrapper';
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import SealSelect from '@/components/seal-form/seal-select';
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { Form, Modal, Slider } from 'antd';
|
||||
import { Form, Modal } from 'antd';
|
||||
import { useEffect } from 'react';
|
||||
import { FormData } from '../config/types';
|
||||
|
||||
type AddModalProps = {
|
||||
title: string;
|
||||
action: PageActionType;
|
||||
open: boolean;
|
||||
onOk: () => void;
|
||||
onOk: (values: FormData) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
const sourceOptions = [
|
||||
{ label: 'Huggingface', value: 'huggingface' },
|
||||
{ label: 'S3', value: 's3' }
|
||||
{ label: 'Huggingface', value: 'huggingface', key: 'huggingface' },
|
||||
{ label: 'S3', value: 's3', key: 's3' }
|
||||
];
|
||||
|
||||
const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
const { title, action, open, onOk, onCancel } = props || {};
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
console.log('modal open', open);
|
||||
const [form] = Form.useForm();
|
||||
const modelSource = Form.useWatch('source', form);
|
||||
|
||||
const initFormValue = () => {
|
||||
if (action === PageAction.CREATE && open) {
|
||||
form.setFieldsValue({
|
||||
source: 'huggingface'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
initFormValue();
|
||||
}, [open]);
|
||||
|
||||
const renderHuggingfaceFields = () => {
|
||||
return (
|
||||
<>
|
||||
<Form.Item<FormData>
|
||||
name="huggingface_repo_id"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input label="Huggingface ID" required></SealInput.Input>
|
||||
</Form.Item>
|
||||
{/* <Form.Item<FormData>
|
||||
name="huggingface_filename"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label="Huggingface File Name"
|
||||
required
|
||||
></SealInput.Input>
|
||||
</Form.Item> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const renderS3Fields = () => {
|
||||
return (
|
||||
<>
|
||||
<Form.Item<FormData> name="s3_address" rules={[{ required: true }]}>
|
||||
<SealInput.Input label="S3 Address" required></SealInput.Input>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const handleSourceChange = (value: string) => {
|
||||
console.log('source change', value);
|
||||
};
|
||||
const handleSumit = () => {
|
||||
form.submit();
|
||||
};
|
||||
const handleOnFinish = (values: FormData) => {
|
||||
console.log('onFinish', values);
|
||||
onOk(values);
|
||||
};
|
||||
return (
|
||||
<Modal
|
||||
title={title}
|
||||
open={open}
|
||||
onOk={onOk}
|
||||
onOk={handleSumit}
|
||||
onCancel={onCancel}
|
||||
destroyOnClose={true}
|
||||
closeIcon={false}
|
||||
@@ -36,30 +94,29 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
keyboard={false}
|
||||
width={600}
|
||||
styles={{}}
|
||||
footer={<ModalFooter onOk={onOk} onCancel={onCancel}></ModalFooter>}
|
||||
footer={
|
||||
<ModalFooter onCancel={onCancel} onOk={handleSumit}></ModalFooter>
|
||||
}
|
||||
>
|
||||
<Form name="addModalForm" form={form} onFinish={onOk}>
|
||||
<Form.Item name="name" rules={[{ required: true }]}>
|
||||
<Form name="addModalForm" form={form} onFinish={onOk} preserve={false}>
|
||||
<Form.Item<FormData> name="name" rules={[{ required: true }]}>
|
||||
<SealInput.Input
|
||||
label="Name"
|
||||
required
|
||||
description="description info"
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item name="source" rules={[{ required: true }]}>
|
||||
<SealSelect label="Model Source" options={sourceOptions}></SealSelect>
|
||||
</Form.Item>
|
||||
<Form.Item name="id" rules={[{ required: true }]}>
|
||||
<SealSelect label="Huggingface ID" showSearch></SealSelect>
|
||||
</Form.Item>
|
||||
<Form.Item name="tokenPers" rules={[{ required: true }]}>
|
||||
<FieldWrapper
|
||||
label="tokenPers"
|
||||
<Form.Item<FormData> name="source" rules={[{ required: true }]}>
|
||||
<SealSelect
|
||||
label="Source"
|
||||
options={sourceOptions}
|
||||
required
|
||||
description="description info"
|
||||
>
|
||||
<Slider defaultValue={30} style={{ width: '100%' }} />
|
||||
</FieldWrapper>
|
||||
onChange={handleSourceChange}
|
||||
></SealSelect>
|
||||
</Form.Item>
|
||||
{modelSource === 's3' ? renderS3Fields() : renderHuggingfaceFields()}
|
||||
<Form.Item<FormData> name="description">
|
||||
<SealInput.TextArea label="Description"></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
export const modelInstanceCols = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: 'Create Time',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime'
|
||||
},
|
||||
{
|
||||
title: 'Status',
|
||||
dataIndex: 'status',
|
||||
key: 'status'
|
||||
},
|
||||
{
|
||||
title: 'Utilization',
|
||||
dataIndex: 'utilization',
|
||||
key: 'utilization'
|
||||
},
|
||||
{
|
||||
title: 'Host Name',
|
||||
dataIndex: 'hostName',
|
||||
key: 'hostName'
|
||||
},
|
||||
{
|
||||
title: 'Operation',
|
||||
key: 'Operation'
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,20 @@
|
||||
export interface ListItem {
|
||||
source: string;
|
||||
huggingface_repo_id: string;
|
||||
huggingface_file_name: string;
|
||||
s3Address: string;
|
||||
name: string;
|
||||
description: string;
|
||||
id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface FormData {
|
||||
source: string;
|
||||
huggingface_repo_id: string;
|
||||
huggingface_filename: string;
|
||||
s3_address: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
@@ -5,9 +5,7 @@ import useTableRowSelection from '@/hooks/use-table-row-selection';
|
||||
import useTableSort from '@/hooks/use-table-sort';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
PlusOutlined,
|
||||
RightOutlined,
|
||||
SyncOutlined,
|
||||
WechatWorkOutlined
|
||||
} from '@ant-design/icons';
|
||||
@@ -24,30 +22,14 @@ import {
|
||||
Tooltip,
|
||||
message
|
||||
} from 'antd';
|
||||
import { StrictMode, useState } from 'react';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { createModel, deleteModel, queryModelsList } from './apis';
|
||||
import AddModal from './components/add-modal';
|
||||
import { FormData, ListItem } from './config/types';
|
||||
const { Column } = Table;
|
||||
|
||||
const dataSource = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'llama3:latest',
|
||||
progress: 30,
|
||||
transition: true,
|
||||
createTime: '2024-05-22 12:20:10'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'openbmb/MiniCPM-Llama3-V-2_5',
|
||||
createTime: '2024-05-19 13:30:22'
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'openbmb/MiniCPM-Llama3-V-2_5',
|
||||
createTime: '2024-05-18 10:28:32'
|
||||
}
|
||||
];
|
||||
|
||||
const Models: React.FC = () => {
|
||||
const { modal } = App.useApp();
|
||||
const access = useAccess();
|
||||
@@ -62,17 +44,43 @@ const Models: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [action, setAction] = useState<PageActionType>(PageAction.CREATE);
|
||||
const [title, setTitle] = useState<string>('');
|
||||
const [dataSource, setDataSource] = useState<ListItem[]>([]);
|
||||
const [queryParams, setQueryParams] = useState({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
name: ''
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
query: ''
|
||||
});
|
||||
const handleShowSizeChange = (current: number, size: number) => {
|
||||
console.log(current, size);
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = {
|
||||
..._.pickBy(queryParams, (val: any) => !!val)
|
||||
};
|
||||
const res = await queryModelsList(params);
|
||||
console.log('res=======', res);
|
||||
setDataSource(res.items);
|
||||
setTotal(res.pagination.total);
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const handleShowSizeChange = (page: number, size: number) => {
|
||||
console.log(page, size);
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
perPage: size
|
||||
});
|
||||
};
|
||||
|
||||
const handlePageChange = (page: number, pageSize: number | undefined) => {
|
||||
console.log(page, pageSize);
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
page: page
|
||||
});
|
||||
};
|
||||
|
||||
const handleTableChange = (pagination: any, filters: any, sorter: any) => {
|
||||
@@ -80,9 +88,6 @@ const Models: React.FC = () => {
|
||||
setSortOrder(sorter.order);
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
console.log('fetchData');
|
||||
};
|
||||
const handleSearch = (e: any) => {
|
||||
fetchData();
|
||||
};
|
||||
@@ -90,7 +95,7 @@ const Models: React.FC = () => {
|
||||
const handleNameChange = (e: any) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
name: e.target.value
|
||||
query: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
@@ -104,17 +109,32 @@ const Models: React.FC = () => {
|
||||
console.log('click', e);
|
||||
};
|
||||
|
||||
const handleModalOk = () => {
|
||||
console.log('handleModalOk');
|
||||
const handleModalOk = async (data: FormData) => {
|
||||
console.log('handleModalOk', data);
|
||||
await createModel({ data });
|
||||
setOpenAddModal(false);
|
||||
message.success('successfully!');
|
||||
};
|
||||
|
||||
const handleModalCancel = () => {
|
||||
console.log('handleModalCancel');
|
||||
setOpenAddModal(false);
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
const handleDelete = async (row: any) => {
|
||||
Modal.confirm({
|
||||
title: '',
|
||||
content: 'Are you sure you want to delete the selected models?',
|
||||
async onOk() {
|
||||
await deleteModel(row.id);
|
||||
message.success('successfully!');
|
||||
fetchData();
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleDeleteBatch = () => {
|
||||
Modal.confirm({
|
||||
title: '',
|
||||
content: 'Are you sure you want to delete the selected models?',
|
||||
@@ -132,8 +152,15 @@ const Models: React.FC = () => {
|
||||
console.log('handleOpenPlayGround', row);
|
||||
navigate('/playground');
|
||||
};
|
||||
|
||||
// request data
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [queryParams]);
|
||||
|
||||
return (
|
||||
<StrictMode>
|
||||
<>
|
||||
<PageContainer
|
||||
ghost
|
||||
header={{
|
||||
@@ -148,6 +175,7 @@ const Models: React.FC = () => {
|
||||
<Input
|
||||
placeholder="按名称查询"
|
||||
style={{ width: 300 }}
|
||||
allowClear
|
||||
onChange={handleNameChange}
|
||||
></Input>
|
||||
<Button
|
||||
@@ -171,7 +199,7 @@ const Models: React.FC = () => {
|
||||
<Button
|
||||
icon={<DeleteOutlined />}
|
||||
danger
|
||||
onClick={handleDelete}
|
||||
onClick={handleDeleteBatch}
|
||||
disabled={!rowSelection.selectedRowKeys.length}
|
||||
>
|
||||
Delete
|
||||
@@ -184,27 +212,17 @@ const Models: React.FC = () => {
|
||||
dataSource={dataSource}
|
||||
rowSelection={rowSelection}
|
||||
loading={loading}
|
||||
rowKey="id"
|
||||
onChange={handleTableChange}
|
||||
pagination={{
|
||||
showSizeChanger: true,
|
||||
pageSize: 10,
|
||||
current: 2,
|
||||
pageSize: queryParams.perPage,
|
||||
current: queryParams.page,
|
||||
total: total,
|
||||
hideOnSinglePage: true,
|
||||
onShowSizeChange: handleShowSizeChange,
|
||||
onChange: handlePageChange
|
||||
}}
|
||||
expandable={{
|
||||
expandIcon: ({ expanded, onExpand, record }) => {
|
||||
return expanded ? (
|
||||
<DownOutlined onClick={(e) => onExpand(record, e)} />
|
||||
) : (
|
||||
<RightOutlined onClick={(e) => onExpand(record, e)} />
|
||||
);
|
||||
},
|
||||
expandedRowRender: (record) => <p style={{ margin: 0 }}>list</p>,
|
||||
rowExpandable: (record) => record.name !== 'Not Expandable'
|
||||
}}
|
||||
>
|
||||
<Column
|
||||
title="Model Name"
|
||||
@@ -227,12 +245,15 @@ const Models: React.FC = () => {
|
||||
/>
|
||||
<Column
|
||||
title="Create Time"
|
||||
dataIndex="createTime"
|
||||
dataIndex="created_at"
|
||||
key="createTime"
|
||||
defaultSortOrder="descend"
|
||||
sortOrder={sortOrder}
|
||||
showSorterTooltip={false}
|
||||
sorter={true}
|
||||
render={(val, row) => {
|
||||
return dayjs(val).format('YYYY-MM-DD HH:mm:ss');
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Operation"
|
||||
@@ -253,6 +274,7 @@ const Models: React.FC = () => {
|
||||
size="small"
|
||||
type="primary"
|
||||
danger
|
||||
onClick={() => handleDelete(record)}
|
||||
icon={<DeleteOutlined></DeleteOutlined>}
|
||||
></Button>
|
||||
</Tooltip>
|
||||
@@ -269,7 +291,7 @@ const Models: React.FC = () => {
|
||||
onCancel={handleModalCancel}
|
||||
onOk={handleModalOk}
|
||||
></AddModal>
|
||||
</StrictMode>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { request } from '@umijs/max';
|
||||
import { ListItem } from '../config/types';
|
||||
|
||||
export const USERS_API = '/users';
|
||||
|
||||
export async function queryUsersList(
|
||||
params: Global.Pagination & { query?: string }
|
||||
) {
|
||||
return request<Global.PageResponse<ListItem>>(`${USERS_API}`, {
|
||||
methos: 'GET',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
export async function createUser(params: { data: FormData }) {
|
||||
return request(`${USERS_API}`, {
|
||||
method: 'POST',
|
||||
data: params.data
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateUser(params: { data: FormData }) {
|
||||
return request(`${USERS_API}`, {
|
||||
method: 'PUT',
|
||||
data: params.data
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteUser(id: number) {
|
||||
return request(`${USERS_API}/${id}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
}
|
||||
@@ -4,12 +4,14 @@ import SealSelect from '@/components/seal-form/seal-select';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { Form, Modal } from 'antd';
|
||||
import { UserRolesOptions } from '../config';
|
||||
import { FormData } from '../config/types';
|
||||
|
||||
type AddModalProps = {
|
||||
title: string;
|
||||
action: PageActionType;
|
||||
open: boolean;
|
||||
onOk: () => void;
|
||||
onOk: (values: FormData) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
const AddModal: React.FC<AddModalProps> = ({
|
||||
@@ -19,6 +21,10 @@ const AddModal: React.FC<AddModalProps> = ({
|
||||
onOk,
|
||||
onCancel
|
||||
}) => {
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const suffix = (
|
||||
<SyncOutlined
|
||||
@@ -28,11 +34,16 @@ const AddModal: React.FC<AddModalProps> = ({
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const handleSumit = () => {
|
||||
form.submit();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={title}
|
||||
open={open}
|
||||
onOk={onOk}
|
||||
onOk={handleSumit}
|
||||
onCancel={onCancel}
|
||||
destroyOnClose={true}
|
||||
closeIcon={false}
|
||||
@@ -40,19 +51,22 @@ const AddModal: React.FC<AddModalProps> = ({
|
||||
keyboard={false}
|
||||
width={600}
|
||||
styles={{}}
|
||||
footer={<ModalFooter onOk={onOk} onCancel={onCancel}></ModalFooter>}
|
||||
footer={
|
||||
<ModalFooter onOk={handleSumit} onCancel={onCancel}></ModalFooter>
|
||||
}
|
||||
>
|
||||
<Form name="addUserForm" form={form} onFinish={onOk}>
|
||||
<Form.Item name="name" rules={[{ required: true }]}>
|
||||
<Form name="addUserForm" form={form} onFinish={onOk} preserve={false}>
|
||||
<Form.Item<FormData> name="name" rules={[{ required: true }]}>
|
||||
<SealInput.Input label="Name"></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item name="role" rules={[{ required: true }]}>
|
||||
<SealSelect label="Role"></SealSelect>
|
||||
<Form.Item<FormData> name="full_name" rules={[{ required: true }]}>
|
||||
<SealInput.Input label="FullName"></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item name="email" rules={[{ required: true }]}>
|
||||
<SealInput.Input label="Email"></SealInput.Input>
|
||||
<Form.Item<FormData> name="is_admin" rules={[{ required: true }]}>
|
||||
<SealSelect label="Role" options={UserRolesOptions}></SealSelect>
|
||||
</Form.Item>
|
||||
<Form.Item name="password" rules={[{ required: true }]}>
|
||||
|
||||
<Form.Item<FormData> name="password" rules={[{ required: true }]}>
|
||||
<SealInput.Input label="Password"></SealInput.Input>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export const UserRoles = {
|
||||
ADMIN: 'admin',
|
||||
USER: 'user'
|
||||
};
|
||||
|
||||
export const UserRolesOptions = [
|
||||
{ label: '管理员', value: UserRoles.ADMIN },
|
||||
{ label: '普通用户', value: UserRoles.USER }
|
||||
];
|
||||
@@ -0,0 +1,15 @@
|
||||
export interface ListItem {
|
||||
name: string;
|
||||
is_admin: boolean;
|
||||
full_name: string;
|
||||
id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface FormData {
|
||||
name: string;
|
||||
is_admin: boolean;
|
||||
full_name: string;
|
||||
password: string;
|
||||
}
|
||||
+78
-34
@@ -13,46 +13,61 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { Button, Input, Modal, Space, Table, Tooltip, message } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { createUser, deleteUser, queryUsersList } from './apis';
|
||||
import AddModal from './components/add-modal';
|
||||
import { FormData, ListItem } from './config/types';
|
||||
const { Column } = Table;
|
||||
|
||||
const dataSource = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'sam',
|
||||
role: 'admin',
|
||||
createTime: '2021-09-01 12:00:00'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'wang',
|
||||
role: 'user',
|
||||
createTime: '2021-09-01 12:00:00'
|
||||
}
|
||||
];
|
||||
|
||||
const Models: React.FC = () => {
|
||||
const rowSelection = useTableRowSelection();
|
||||
const { sortOrder, setSortOrder } = useTableSort({
|
||||
defaultSortOrder: 'descend'
|
||||
});
|
||||
const [total, setTotal] = useState(100);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [openAddModal, setOpenAddModal] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [dataSource, setDataSource] = useState<ListItem[]>([]);
|
||||
const [action, setAction] = useState<PageActionType>(PageAction.CREATE);
|
||||
const [title, setTitle] = useState<string>('');
|
||||
const [queryParams, setQueryParams] = useState({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
name: ''
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
query: ''
|
||||
});
|
||||
const handleShowSizeChange = (current: number, size: number) => {
|
||||
console.log(current, size);
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = {
|
||||
..._.pickBy(queryParams, (val: any) => !!val)
|
||||
};
|
||||
const res = await queryUsersList(params);
|
||||
console.log('res=======', res);
|
||||
setDataSource(res.items);
|
||||
setTotal(res.pagination.total);
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleShowSizeChange = (page: number, size: number) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
perPage: size
|
||||
});
|
||||
};
|
||||
|
||||
const handlePageChange = (page: number, pageSize: number | undefined) => {
|
||||
console.log(page, pageSize);
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
page: page
|
||||
});
|
||||
};
|
||||
|
||||
const handleTableChange = (pagination: any, filters: any, sorter: any) => {
|
||||
@@ -60,9 +75,6 @@ const Models: React.FC = () => {
|
||||
setSortOrder(sorter.order);
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
console.log('fetchData');
|
||||
};
|
||||
const handleSearch = (e: any) => {
|
||||
fetchData();
|
||||
};
|
||||
@@ -70,7 +82,7 @@ const Models: React.FC = () => {
|
||||
const handleNameChange = (e: any) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
name: e.target.value
|
||||
query: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
@@ -84,9 +96,15 @@ const Models: React.FC = () => {
|
||||
console.log('click', e);
|
||||
};
|
||||
|
||||
const handleModalOk = () => {
|
||||
const handleModalOk = async (data: FormData) => {
|
||||
console.log('handleModalOk');
|
||||
const params = {
|
||||
...data,
|
||||
is_admin: data.is_admin === 'admin'
|
||||
};
|
||||
await createUser({ data: params });
|
||||
setOpenAddModal(false);
|
||||
message.success('successfully!');
|
||||
};
|
||||
|
||||
const handleModalCancel = () => {
|
||||
@@ -94,7 +112,23 @@ const Models: React.FC = () => {
|
||||
setOpenAddModal(false);
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
const handleDelete = (row: ListItem) => {
|
||||
Modal.confirm({
|
||||
title: '',
|
||||
content: 'Are you sure you want to delete the selected users?',
|
||||
async onOk() {
|
||||
console.log('OK');
|
||||
await deleteUser(row.id);
|
||||
message.success('successfully!');
|
||||
fetchData();
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteBatch = () => {
|
||||
Modal.confirm({
|
||||
title: '',
|
||||
content: 'Are you sure you want to delete the selected users?',
|
||||
@@ -113,6 +147,11 @@ const Models: React.FC = () => {
|
||||
setAction(PageAction.EDIT);
|
||||
setTitle('Edit User');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [queryParams]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContainer
|
||||
@@ -151,7 +190,7 @@ const Models: React.FC = () => {
|
||||
<Button
|
||||
icon={<DeleteOutlined />}
|
||||
danger
|
||||
onClick={handleDelete}
|
||||
onClick={handleDeleteBatch}
|
||||
disabled={!rowSelection.selectedRowKeys.length}
|
||||
>
|
||||
Delete
|
||||
@@ -163,11 +202,12 @@ const Models: React.FC = () => {
|
||||
dataSource={dataSource}
|
||||
rowSelection={rowSelection}
|
||||
loading={loading}
|
||||
rowKey="id"
|
||||
onChange={handleTableChange}
|
||||
pagination={{
|
||||
showSizeChanger: true,
|
||||
pageSize: 10,
|
||||
current: 2,
|
||||
pageSize: queryParams.perPage,
|
||||
current: queryParams.page,
|
||||
total: total,
|
||||
hideOnSinglePage: true,
|
||||
onShowSizeChange: handleShowSizeChange,
|
||||
@@ -177,20 +217,23 @@ const Models: React.FC = () => {
|
||||
<Column title="Name" dataIndex="name" key="name" width={400} />
|
||||
<Column
|
||||
title="Create Time"
|
||||
dataIndex="createTime"
|
||||
dataIndex="created_at"
|
||||
key="createTime"
|
||||
defaultSortOrder="descend"
|
||||
sortOrder={sortOrder}
|
||||
showSorterTooltip={false}
|
||||
sorter={true}
|
||||
render={(text, record) => {
|
||||
return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Role"
|
||||
dataIndex="role"
|
||||
key="role"
|
||||
width={400}
|
||||
render={(text, record) => {
|
||||
return text === 'admin' ? (
|
||||
render={(text, record: ListItem) => {
|
||||
return record.is_admin ? (
|
||||
<UserSwitchOutlined className="size-16" />
|
||||
) : (
|
||||
<UserOutlined className="size-16" />
|
||||
@@ -216,6 +259,7 @@ const Models: React.FC = () => {
|
||||
size="small"
|
||||
type="primary"
|
||||
danger
|
||||
onClick={() => handleDelete(record)}
|
||||
icon={<DeleteOutlined></DeleteOutlined>}
|
||||
></Button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { RequestConfig } from '@umijs/max';
|
||||
import { message } from 'antd';
|
||||
|
||||
export const requestConfig: RequestConfig = {
|
||||
errorConfig: {
|
||||
errorThrower: (res: any) => {
|
||||
console.log('errorThrower+++++++++++++++', res);
|
||||
},
|
||||
errorHandler: (error: any, opts: any) => {
|
||||
const { message: errorMessage, response } = error;
|
||||
const errMsg = response?.data?.message || errorMessage;
|
||||
message.error(errMsg);
|
||||
console.log('errorHandler+++++++++++++++', error, opts);
|
||||
}
|
||||
},
|
||||
requestInterceptors: [
|
||||
(url, options) => {
|
||||
console.log('requestInterceptors+++++++++++++++', url, options);
|
||||
return { url, options };
|
||||
}
|
||||
],
|
||||
responseInterceptors: [
|
||||
(response) => {
|
||||
console.log('responseInterceptors+++++++++++++++', response);
|
||||
return response;
|
||||
}
|
||||
]
|
||||
};
|
||||
Reference in New Issue
Block a user