fix: table list action
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
import { atomWithStorage } from 'jotai/utils';
|
import { atomWithStorage } from 'jotai/utils';
|
||||||
|
|
||||||
export const userAtom = atomWithStorage<any>('userInfo', null);
|
export const userAtom = atomWithStorage<any>('userInfo', null);
|
||||||
|
|
||||||
|
export const initialPasswordAtom = atomWithStorage<string>(
|
||||||
|
'initialPassword',
|
||||||
|
''
|
||||||
|
);
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ const LineChart: React.FC<LineChartProps> = (props) => {
|
|||||||
labelFormatter
|
labelFormatter
|
||||||
} = props;
|
} = props;
|
||||||
const config = {
|
const config = {
|
||||||
title,
|
|
||||||
height,
|
height,
|
||||||
xField: xField || 'time',
|
xField: xField || 'time',
|
||||||
yField: yField || 'value',
|
yField: yField || 'value',
|
||||||
@@ -50,6 +49,15 @@ const LineChart: React.FC<LineChartProps> = (props) => {
|
|||||||
style: {
|
style: {
|
||||||
fill: 'rgba(84, 204, 152,0.8)'
|
fill: 'rgba(84, 204, 152,0.8)'
|
||||||
},
|
},
|
||||||
|
title: {
|
||||||
|
title,
|
||||||
|
style: {
|
||||||
|
align: 'center',
|
||||||
|
titleFontSize: 14,
|
||||||
|
titleFill: 'rgba(0,0,0,0.88)',
|
||||||
|
titleFontWeight: 500
|
||||||
|
}
|
||||||
|
},
|
||||||
legend: {
|
legend: {
|
||||||
color: {
|
color: {
|
||||||
layout: { justifyContent: 'center' }
|
layout: { justifyContent: 'center' }
|
||||||
|
|||||||
@@ -85,15 +85,14 @@ const BarChart: React.FC<BarChartProps> = (props) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
style: {
|
style: {
|
||||||
// fill: (params: any) => {
|
fill: (params: any) => {
|
||||||
// return (
|
return (
|
||||||
// params.color ||
|
params.color ||
|
||||||
// 'linear-gradient(90deg,rgba(84, 204, 152,0.8) 0%,rgb(0, 168, 143,.7) 100%)'
|
'linear-gradient(90deg,rgba(84, 204, 152,0.8) 0%,rgb(0, 168, 143,.7) 100%)'
|
||||||
// );
|
);
|
||||||
// },
|
},
|
||||||
// radiusTopLeft: 12,
|
// radiusTopLeft: 12,
|
||||||
// radiusTopRight: 12,
|
// radiusTopRight: 12,
|
||||||
fill: 'rgba(84, 204, 152,0.8)',
|
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 20
|
width: 20
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export const titleStyle = {
|
||||||
|
style: {
|
||||||
|
align: 'center',
|
||||||
|
titleFontSize: 14,
|
||||||
|
titleFill: 'rgba(0,0,0,0.88)',
|
||||||
|
titleFontWeight: 500
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { MoreOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Dropdown, Tooltip, type MenuProps } from 'antd';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
interface DropdownButtonsProps {
|
||||||
|
items: MenuProps['items'];
|
||||||
|
size?: 'small' | 'middle' | 'large';
|
||||||
|
onSelect: (val: any, item?: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DropdownButtons: React.FC<DropdownButtonsProps> = ({
|
||||||
|
items,
|
||||||
|
size = 'small',
|
||||||
|
onSelect
|
||||||
|
}) => {
|
||||||
|
const handleMenuClick = (item: any) => {
|
||||||
|
console.log('menu click', item.key);
|
||||||
|
const selectItem = _.find(items, { key: item.key });
|
||||||
|
onSelect(item.key, selectItem);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleButtonClick = (e: any) => {
|
||||||
|
const headItem = _.head(items);
|
||||||
|
onSelect(headItem.key, headItem);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!items?.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{items?.length === 1 ? (
|
||||||
|
<Button
|
||||||
|
icon={_.get(items, '0.icon')}
|
||||||
|
size={size}
|
||||||
|
onClick={handleButtonClick}
|
||||||
|
></Button>
|
||||||
|
) : (
|
||||||
|
<Dropdown.Button
|
||||||
|
menu={{
|
||||||
|
items: _.tail(items),
|
||||||
|
onClick: handleMenuClick
|
||||||
|
}}
|
||||||
|
buttonsRender={([leftButton, rightButton]) => [
|
||||||
|
<Tooltip title={_.head(items)?.label} key="leftButton">
|
||||||
|
<Button
|
||||||
|
onClick={handleButtonClick}
|
||||||
|
size={size}
|
||||||
|
icon={_.head(items)?.icon}
|
||||||
|
></Button>
|
||||||
|
</Tooltip>,
|
||||||
|
<Button icon={<MoreOutlined />} size={size} key="menu"></Button>
|
||||||
|
]}
|
||||||
|
></Dropdown.Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DropdownButtons;
|
||||||
@@ -39,7 +39,6 @@ const SealInputNumber: React.FC<InputNumberProps & SealFormItemProps> = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (e: any) => {
|
const handleChange = (e: any) => {
|
||||||
e.target.value = e.target.value?.trim?.();
|
|
||||||
props.onChange?.(e);
|
props.onChange?.(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -49,7 +48,6 @@ const SealInputNumber: React.FC<InputNumberProps & SealFormItemProps> = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOnBlur = (e: any) => {
|
const handleOnBlur = (e: any) => {
|
||||||
e.target.value = e.target.value?.trim?.();
|
|
||||||
if (!inputRef.current?.value) {
|
if (!inputRef.current?.value) {
|
||||||
setIsFocus(false);
|
setIsFocus(false);
|
||||||
props.onBlur?.(e);
|
props.onBlur?.(e);
|
||||||
|
|||||||
@@ -6,19 +6,20 @@ export default {
|
|||||||
'dashboard.allocategpus': 'Allocated GPUs',
|
'dashboard.allocategpus': 'Allocated GPUs',
|
||||||
'dashboard.instances': 'Instances',
|
'dashboard.instances': 'Instances',
|
||||||
'dashboard.systemload': 'System Load',
|
'dashboard.systemload': 'System Load',
|
||||||
'dashboard.memory': 'Memory',
|
'dashboard.memory': 'RAM',
|
||||||
'dashboard.disk': 'Storage',
|
'dashboard.disk': 'Storage',
|
||||||
'dashboard.vram': 'VRAM',
|
'dashboard.vram': 'VRAM',
|
||||||
'dashboard.cpuutilization': 'CPU Utilization',
|
'dashboard.cpuutilization': 'CPU Utilization',
|
||||||
'dashboard.memoryutilization': 'Memory Utilization',
|
'dashboard.memoryutilization': 'RAM Utilization',
|
||||||
'dashboard.diskutilization': 'Storage Utilization',
|
'dashboard.diskutilization': 'Storage Utilization',
|
||||||
'dashboard.vramutilization': 'VRAM Utilization',
|
'dashboard.vramutilization': 'VRAM Utilization',
|
||||||
'dashboard.gpuutilization': 'GPU Utilization',
|
'dashboard.gpuutilization': 'GPU Utilization',
|
||||||
'dashboard.usage': 'Usage',
|
'dashboard.usage': 'Usage',
|
||||||
'dashboard.apirequest': 'API Request',
|
'dashboard.apirequest': 'API Request',
|
||||||
'dashboard.tokens': 'Tokens',
|
'dashboard.tokens': 'Token Usage',
|
||||||
'dashboard.topusers': 'Top Users',
|
'dashboard.topusers': 'Top Users',
|
||||||
'dashboard.activeModels': 'Active Models',
|
'dashboard.activeModels': 'Active Models',
|
||||||
'dashboard.runninginstances': 'Running Instances',
|
'dashboard.runninginstances': 'Running Instances',
|
||||||
'dashboard.activeModels.name': 'Model Name'
|
'dashboard.activeModels.name': 'Model Name',
|
||||||
|
'dashboard.allocatevram': 'Allocated VRAM'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ export default {
|
|||||||
'resources.table.hostname': 'Hostname',
|
'resources.table.hostname': 'Hostname',
|
||||||
'resources.table.ip': 'IP',
|
'resources.table.ip': 'IP',
|
||||||
'resources.table.cpu': 'CPU',
|
'resources.table.cpu': 'CPU',
|
||||||
'resources.table.memory': 'Memory',
|
'resources.table.memory': 'RAM',
|
||||||
'resources.table.gpu': 'GPU',
|
'resources.table.gpu': 'GPU',
|
||||||
'resources.table.disk': 'Storage',
|
'resources.table.disk': 'Storage',
|
||||||
'resources.table.vram': 'VRAM',
|
'resources.table.vram': 'VRAM',
|
||||||
'resources.table.index': 'Index',
|
'resources.table.index': 'Index',
|
||||||
'resources.table.workername': 'Wroker Name',
|
'resources.table.workername': 'Worker Name',
|
||||||
'resources.table.vender': 'Vender',
|
'resources.table.vender': 'Vender',
|
||||||
'resources.table.temperature': 'Temperature',
|
'resources.table.temperature': 'Temperature',
|
||||||
'resources.table.core': 'Core',
|
'resources.table.core': 'Core',
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ export default {
|
|||||||
'dashboard.gpuutilization': 'GPU 利用率',
|
'dashboard.gpuutilization': 'GPU 利用率',
|
||||||
'dashboard.usage': '使用量',
|
'dashboard.usage': '使用量',
|
||||||
'dashboard.apirequest': 'API 请求',
|
'dashboard.apirequest': 'API 请求',
|
||||||
'dashboard.tokens': 'Tokens',
|
'dashboard.tokens': 'Token 使用量',
|
||||||
'dashboard.topusers': '用户排行',
|
'dashboard.topusers': '用户排行',
|
||||||
'dashboard.activeModels': '活跃模型',
|
'dashboard.activeModels': '活跃模型',
|
||||||
'dashboard.activeModels.name': '模型名称',
|
'dashboard.activeModels.name': '模型名称',
|
||||||
'dashboard.runninginstances': '运行实例'
|
'dashboard.runninginstances': '运行实例',
|
||||||
|
'dashboard.allocatevram': '已分配显存'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import PageTools from '@/components/page-tools';
|
import PageTools from '@/components/page-tools';
|
||||||
import ProgressBar from '@/components/progress-bar';
|
import { convertFileSize } from '@/utils';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Col, Row, Table } from 'antd';
|
import { Col, Row, Table } from 'antd';
|
||||||
import _ from 'lodash';
|
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { DashboardContext } from '../config/dashboard-context';
|
import { DashboardContext } from '../config/dashboard-context';
|
||||||
|
|
||||||
@@ -77,21 +76,19 @@ const ActiveTable = () => {
|
|||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name'
|
key: 'name'
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// title: intl.formatMessage({ id: 'dashboard.gpuutilization' }),
|
||||||
|
// dataIndex: 'gpu_utilization',
|
||||||
|
// key: 'gpu_utilization',
|
||||||
|
// render: (text: any, record: any) => (
|
||||||
|
// <ProgressBar percent={_.round(text, 0)}></ProgressBar>
|
||||||
|
// )
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: 'dashboard.gpuutilization' }),
|
title: intl.formatMessage({ id: 'dashboard.allocatevram' }),
|
||||||
dataIndex: 'gpu_utilization',
|
dataIndex: 'allocate_gpu_memory_utilization',
|
||||||
key: 'gpu_utilization',
|
|
||||||
render: (text: any, record: any) => (
|
|
||||||
<ProgressBar percent={_.round(text, 0)}></ProgressBar>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: intl.formatMessage({ id: 'dashboard.vramutilization' }),
|
|
||||||
dataIndex: 'gpu_memory_utilization',
|
|
||||||
key: 'gpu_memory_utilization',
|
key: 'gpu_memory_utilization',
|
||||||
render: (text: any, record: any) => (
|
render: (text: any, record: any) => <span>{convertFileSize(text)}</span>
|
||||||
<ProgressBar percent={_.round(text, 0)}></ProgressBar>
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: 'dashboard.runninginstances' }),
|
title: intl.formatMessage({ id: 'dashboard.runninginstances' }),
|
||||||
@@ -99,7 +96,7 @@ const ActiveTable = () => {
|
|||||||
key: 'instance_count'
|
key: 'instance_count'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Tokens',
|
title: intl.formatMessage({ id: 'dashboard.tokens' }),
|
||||||
dataIndex: 'token_count',
|
dataIndex: 'token_count',
|
||||||
key: 'token_count'
|
key: 'token_count'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,9 +57,9 @@ const Overview: React.FC = () => {
|
|||||||
<Col
|
<Col
|
||||||
xs={{ flex: '100%' }}
|
xs={{ flex: '100%' }}
|
||||||
sm={{ flex: '50%' }}
|
sm={{ flex: '50%' }}
|
||||||
md={{ flex: '30%' }}
|
md={{ flex: '50%' }}
|
||||||
lg={{ flex: '20%' }}
|
lg={{ flex: '25%' }}
|
||||||
xl={{ flex: '20%' }}
|
xl={{ flex: '25%' }}
|
||||||
key={config.key}
|
key={config.key}
|
||||||
>
|
>
|
||||||
{renderCardItem({
|
{renderCardItem({
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const UtilizationOvertime: React.FC = () => {
|
|||||||
const list: { value: number; time: string; type: string }[] = [];
|
const list: { value: number; time: string; type: string }[] = [];
|
||||||
_.each(typeList, (type: any) => {
|
_.each(typeList, (type: any) => {
|
||||||
const dataList = _.map(_.get(data, type, []), (item: any) => {
|
const dataList = _.map(_.get(data, type, []), (item: any) => {
|
||||||
const value = _.get(item, 'value', 0);
|
const value = _.round(_.get(item, 'value', 0), 1);
|
||||||
const time = dayjs(item.timestamp * 1000).format('HH:mm:ss');
|
const time = dayjs(item.timestamp * 1000).format('HH:mm:ss');
|
||||||
const itemtype = _.get(TypeKeyMap, [type, 'intl'], false)
|
const itemtype = _.get(TypeKeyMap, [type, 'intl'], false)
|
||||||
? intl.formatMessage({
|
? intl.formatMessage({
|
||||||
|
|||||||
@@ -92,6 +92,19 @@ const tokenUsage = TokensData.map((val, i) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getCurrentMonthDays = () => {
|
||||||
|
const now = dayjs();
|
||||||
|
const firstDayOfMonth = now.startOf('month');
|
||||||
|
const dateRange = [];
|
||||||
|
let currentDate = firstDayOfMonth;
|
||||||
|
|
||||||
|
while (currentDate.isBefore(now) || currentDate.isSame(now, 'day')) {
|
||||||
|
dateRange.push(currentDate.format('YYYY-MM-DD'));
|
||||||
|
currentDate = currentDate.add(1, 'day');
|
||||||
|
}
|
||||||
|
return dateRange;
|
||||||
|
};
|
||||||
|
|
||||||
const Usage = () => {
|
const Usage = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { size } = useWindowResize();
|
const { size } = useWindowResize();
|
||||||
@@ -105,8 +118,11 @@ const Usage = () => {
|
|||||||
const [userData, setUserData] = useState<{ name: string; value: number }[]>(
|
const [userData, setUserData] = useState<{ name: string; value: number }[]>(
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
const [dateRange, setDateRange] = useState<string[]>(getCurrentMonthDays());
|
||||||
|
|
||||||
const data = useContext(DashboardContext)?.model_usage || {};
|
const data = useContext(DashboardContext)?.model_usage || {};
|
||||||
|
|
||||||
|
console.log('dateRange', dateRange);
|
||||||
const handleSelectDate = (dateString: string) => {};
|
const handleSelectDate = (dateString: string) => {};
|
||||||
|
|
||||||
const generateData = () => {
|
const generateData = () => {
|
||||||
@@ -126,26 +142,67 @@ const Usage = () => {
|
|||||||
|
|
||||||
_.each(data.api_request_history, (item: any) => {
|
_.each(data.api_request_history, (item: any) => {
|
||||||
requestList.push({
|
requestList.push({
|
||||||
time: dayjs(item.timestamp * 1000).format('YYYY-MM'),
|
time: dayjs(item.timestamp * 1000).format('YYYY-MM-DD'),
|
||||||
value: item.value
|
value: item.value
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
_.each(data.completion_token_history, (item: any) => {
|
_.each(dateRange, (date: string) => {
|
||||||
tokenList.push({
|
// tokens data
|
||||||
time: dayjs(item.timestamp * 1000).format('YYYY-MM'),
|
const item = _.find(data.completion_token_history, (item: any) => {
|
||||||
name: 'completion_token',
|
return dayjs(item.timestamp * 1000).format('YYYY-MM-DD') === date;
|
||||||
color: 'rgba(84, 204, 152,0.8)',
|
|
||||||
value: item.value
|
|
||||||
});
|
});
|
||||||
});
|
if (!item) {
|
||||||
_.each(data.prompt_token_history, (item: any) => {
|
tokenList.push({
|
||||||
tokenList.push({
|
time: date,
|
||||||
time: dayjs(item.timestamp * 1000).format('YYYY-MM'),
|
name: 'completion_token',
|
||||||
name: 'prompt_token',
|
color: 'rgba(84, 204, 152,0.8)',
|
||||||
color: 'rgba(0, 170, 173, 0.8)',
|
value: 0
|
||||||
value: item.value
|
});
|
||||||
|
} else {
|
||||||
|
tokenList.push({
|
||||||
|
time: dayjs(item.timestamp * 1000).format('YYYY-MM-DD'),
|
||||||
|
name: 'completion_token',
|
||||||
|
color: 'rgba(84, 204, 152,0.8)',
|
||||||
|
value: item.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const promptItem = _.find(data.prompt_token_history, (item: any) => {
|
||||||
|
return dayjs(item.timestamp * 1000).format('YYYY-MM-DD') === date;
|
||||||
});
|
});
|
||||||
|
if (!promptItem) {
|
||||||
|
tokenList.push({
|
||||||
|
time: date,
|
||||||
|
name: 'prompt_token',
|
||||||
|
color: 'rgba(0, 170, 173, 0.8)',
|
||||||
|
value: 0
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
tokenList.push({
|
||||||
|
time: dayjs(promptItem.timestamp * 1000).format('YYYY-MM-DD'),
|
||||||
|
name: 'prompt_token',
|
||||||
|
color: 'rgba(0, 170, 173, 0.8)',
|
||||||
|
value: promptItem.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// api request data
|
||||||
|
const requestItem = _.find(data.api_request_history, (item: any) => {
|
||||||
|
return dayjs(item.timestamp * 1000).format('YYYY-MM-DD') === date;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!requestItem) {
|
||||||
|
requestList.push({
|
||||||
|
time: date,
|
||||||
|
value: 0
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
requestList.push({
|
||||||
|
time: dayjs(requestItem.timestamp * 1000).format('YYYY-MM-DD'),
|
||||||
|
value: requestItem.value
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_.each(data.top_users, (item: any) => {
|
_.each(data.top_users, (item: any) => {
|
||||||
@@ -190,7 +247,7 @@ const Usage = () => {
|
|||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<RangePicker
|
<DatePicker
|
||||||
onChange={handleSelectDate}
|
onChange={handleSelectDate}
|
||||||
style={{ width: 300 }}
|
style={{ width: 300 }}
|
||||||
picker="month"
|
picker="month"
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ export const overviewConfigs = [
|
|||||||
backgroundColor: 'var(--color-white-1)'
|
backgroundColor: 'var(--color-white-1)'
|
||||||
// backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
|
// backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
key: 'allocatedGpus',
|
// key: 'allocatedGpus',
|
||||||
label: 'dashboard.allocategpus',
|
// label: 'dashboard.allocategpus',
|
||||||
backgroundColor: 'var(--color-white-1)'
|
// backgroundColor: 'var(--color-white-1)'
|
||||||
// backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
|
// // backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
key: 'model_count',
|
key: 'model_count',
|
||||||
label: 'dashboard.models',
|
label: 'dashboard.models',
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { StatusMaps } from '@/config';
|
import { StatusMaps } from '@/config';
|
||||||
|
import { EditOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
export const ollamaModelOptions = [
|
export const ollamaModelOptions = [
|
||||||
{ label: 'llama3', value: 'llama3' },
|
{ label: 'llama3', value: 'llama3' },
|
||||||
@@ -20,3 +21,21 @@ export const modelSourceMap = {
|
|||||||
export const status: any = {
|
export const status: any = {
|
||||||
Running: StatusMaps.success
|
Running: StatusMaps.success
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ActionList = [
|
||||||
|
{
|
||||||
|
label: 'common.button.edit',
|
||||||
|
key: 'edit',
|
||||||
|
icon: EditOutlined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'models.openinplayground',
|
||||||
|
key: 'chat',
|
||||||
|
icon: EditOutlined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'common.button.delete',
|
||||||
|
key: 'delete',
|
||||||
|
icon: EditOutlined
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export interface ModelInstanceListItem {
|
|||||||
gpu_index: number;
|
gpu_index: number;
|
||||||
pid: number;
|
pid: number;
|
||||||
port: number;
|
port: number;
|
||||||
|
name: string;
|
||||||
state: string;
|
state: string;
|
||||||
download_progress: number;
|
download_progress: number;
|
||||||
model_id: number;
|
model_id: number;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import DropdownButtons from '@/components/drop-down-buttons';
|
||||||
import PageTools from '@/components/page-tools';
|
import PageTools from '@/components/page-tools';
|
||||||
import SealTable from '@/components/seal-table';
|
import SealTable from '@/components/seal-table';
|
||||||
import RowChildren from '@/components/seal-table/components/row-children';
|
import RowChildren from '@/components/seal-table/components/row-children';
|
||||||
@@ -24,17 +25,7 @@ import {
|
|||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { Access, useAccess, useIntl, useNavigate } from '@umijs/max';
|
import { Access, useAccess, useIntl, useNavigate } from '@umijs/max';
|
||||||
import {
|
import { Button, Col, Input, Modal, Row, Space, message } from 'antd';
|
||||||
Button,
|
|
||||||
Col,
|
|
||||||
Input,
|
|
||||||
Modal,
|
|
||||||
Row,
|
|
||||||
Space,
|
|
||||||
Tag,
|
|
||||||
Tooltip,
|
|
||||||
message
|
|
||||||
} from 'antd';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
@@ -79,6 +70,38 @@ const Models: React.FC = () => {
|
|||||||
);
|
);
|
||||||
const [currentInstanceUrl, setCurrentInstanceUrl] = useState<string>('');
|
const [currentInstanceUrl, setCurrentInstanceUrl] = useState<string>('');
|
||||||
|
|
||||||
|
const ActionList = [
|
||||||
|
{
|
||||||
|
label: intl.formatMessage({ id: 'common.button.edit' }),
|
||||||
|
key: 'edit',
|
||||||
|
icon: <EditOutlined />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: intl.formatMessage({ id: 'models.openinplayground' }),
|
||||||
|
key: 'chat',
|
||||||
|
icon: <WechatWorkOutlined />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: intl.formatMessage({ id: 'common.button.delete' }),
|
||||||
|
key: 'delete',
|
||||||
|
danger: true,
|
||||||
|
icon: <DeleteOutlined />
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const childActionList = [
|
||||||
|
{
|
||||||
|
label: intl.formatMessage({ id: 'common.button.viewlog' }),
|
||||||
|
key: 'viewlog',
|
||||||
|
icon: <FieldTimeOutlined />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: intl.formatMessage({ id: 'common.button.delete' }),
|
||||||
|
key: 'delete',
|
||||||
|
danger: true,
|
||||||
|
icon: <DeleteOutlined />
|
||||||
|
}
|
||||||
|
];
|
||||||
const chunkRequedtRef = useRef<any>();
|
const chunkRequedtRef = useRef<any>();
|
||||||
const timer = useRef<any>();
|
const timer = useRef<any>();
|
||||||
let axiosToken = createAxiosToken();
|
let axiosToken = createAxiosToken();
|
||||||
@@ -370,6 +393,27 @@ const Models: React.FC = () => {
|
|||||||
setTitle(intl.formatMessage({ id: 'models.title.edit' }));
|
setTitle(intl.formatMessage({ id: 'models.title.edit' }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSelect = (val: any, row: ListItem) => {
|
||||||
|
if (val === 'edit') {
|
||||||
|
handleEdit(row);
|
||||||
|
}
|
||||||
|
if (val === 'chat') {
|
||||||
|
handleOpenPlayGround(row);
|
||||||
|
}
|
||||||
|
if (val === 'delete') {
|
||||||
|
handleDelete(row);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChildSelect = (val: any, row: ModelInstanceListItem) => {
|
||||||
|
if (val === 'delete') {
|
||||||
|
handleDeleteInstace(row);
|
||||||
|
}
|
||||||
|
if (val === 'viewlog') {
|
||||||
|
handleViewLogs(row);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// fetchData();
|
// fetchData();
|
||||||
createModelsChunkRequest();
|
createModelsChunkRequest();
|
||||||
@@ -394,10 +438,7 @@ const Models: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<RowChildren key={`${item.id}_row`}>
|
<RowChildren key={`${item.id}_row`}>
|
||||||
<Row style={{ width: '100%' }} align="middle">
|
<Row style={{ width: '100%' }} align="middle">
|
||||||
<Col span={6}>
|
<Col span={6}>{item.name}</Col>
|
||||||
<Tag>{item.gpu_index}</Tag>
|
|
||||||
{item.worker_ip}:{item.port}
|
|
||||||
</Col>
|
|
||||||
<Col span={4}>
|
<Col span={4}>
|
||||||
<span>{item.huggingface_filename}</span>
|
<span>{item.huggingface_filename}</span>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -423,33 +464,10 @@ const Models: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={5}>
|
<Col span={5}>
|
||||||
{hoverChildIndex === `${item.id}-${index}` && (
|
<DropdownButtons
|
||||||
<Space size={20}>
|
items={childActionList}
|
||||||
<Tooltip
|
onSelect={(val) => handleChildSelect(val, item)}
|
||||||
title={intl.formatMessage({
|
></DropdownButtons>
|
||||||
id: 'common.button.delete'
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
danger
|
|
||||||
onClick={() => handleDeleteInstace(item)}
|
|
||||||
icon={<DeleteOutlined></DeleteOutlined>}
|
|
||||||
></Button>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip
|
|
||||||
title={intl.formatMessage({
|
|
||||||
id: 'common.button.viewlog'
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
onClick={() => handleViewLogs(item)}
|
|
||||||
icon={<FieldTimeOutlined />}
|
|
||||||
></Button>
|
|
||||||
</Tooltip>
|
|
||||||
</Space>
|
|
||||||
)}
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</RowChildren>
|
</RowChildren>
|
||||||
@@ -570,43 +588,10 @@ const Models: React.FC = () => {
|
|||||||
key="operation"
|
key="operation"
|
||||||
render={(text, record) => {
|
render={(text, record) => {
|
||||||
return !record.transition ? (
|
return !record.transition ? (
|
||||||
<Space size={20}>
|
<DropdownButtons
|
||||||
<Tooltip
|
items={ActionList}
|
||||||
title={intl.formatMessage({
|
onSelect={(val) => handleSelect(val, record)}
|
||||||
id: 'common.button.edit'
|
></DropdownButtons>
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
onClick={() => handleEdit(record)}
|
|
||||||
icon={<EditOutlined></EditOutlined>}
|
|
||||||
></Button>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip
|
|
||||||
title={intl.formatMessage({
|
|
||||||
id: 'models.openinplayground'
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
onClick={() => handleOpenPlayGround(record)}
|
|
||||||
icon={<WechatWorkOutlined />}
|
|
||||||
></Button>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip
|
|
||||||
title={intl.formatMessage({ id: 'common.button.delete' })}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
danger
|
|
||||||
onClick={() => handleDelete(record)}
|
|
||||||
icon={<DeleteOutlined></DeleteOutlined>}
|
|
||||||
></Button>
|
|
||||||
</Tooltip>
|
|
||||||
</Space>
|
|
||||||
) : null;
|
) : null;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import LogoIcon from '@/assets/images/logo.png';
|
import LogoIcon from '@/assets/images/logo.png';
|
||||||
import { userAtom } from '@/atoms/user';
|
import { initialPasswordAtom, userAtom } from '@/atoms/user';
|
||||||
import SealInput from '@/components/seal-form/seal-input';
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
import { GlobalOutlined, LockOutlined, UserOutlined } from '@ant-design/icons';
|
import { GlobalOutlined, LockOutlined, UserOutlined } from '@ant-design/icons';
|
||||||
import { SelectLang, history, useIntl, useModel } from '@umijs/max';
|
import { SelectLang, history, useIntl, useModel } from '@umijs/max';
|
||||||
@@ -26,6 +26,7 @@ const renderLogo = () => {
|
|||||||
};
|
};
|
||||||
const LoginForm = () => {
|
const LoginForm = () => {
|
||||||
const [userInfo, setUserInfo] = useAtom(userAtom);
|
const [userInfo, setUserInfo] = useAtom(userAtom);
|
||||||
|
const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom);
|
||||||
const { initialState, setInitialState } = useModel('@@initialState');
|
const { initialState, setInitialState } = useModel('@@initialState');
|
||||||
const { globalState, setGlobalState } = useModel('global');
|
const { globalState, setGlobalState } = useModel('global');
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -62,6 +63,7 @@ const LoginForm = () => {
|
|||||||
userInfo
|
userInfo
|
||||||
});
|
});
|
||||||
setUserInfo(userInfo);
|
setUserInfo(userInfo);
|
||||||
|
setInitialPassword(values.password);
|
||||||
if (!userInfo?.require_password_change) {
|
if (!userInfo?.require_password_change) {
|
||||||
gotoDefaultPage(userInfo);
|
gotoDefaultPage(userInfo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { userAtom } from '@/atoms/user';
|
import { initialPasswordAtom, userAtom } from '@/atoms/user';
|
||||||
import SealInput from '@/components/seal-form/seal-input';
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
import { PasswordReg } from '@/config';
|
import { PasswordReg } from '@/config';
|
||||||
import { GlobalOutlined, LockOutlined } from '@ant-design/icons';
|
import { GlobalOutlined, LockOutlined } from '@ant-design/icons';
|
||||||
@@ -12,6 +12,7 @@ const PasswordForm: React.FC = () => {
|
|||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
const [userInfo, setUserInfo] = useAtom(userAtom);
|
const [userInfo, setUserInfo] = useAtom(userAtom);
|
||||||
|
const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom);
|
||||||
const gotoDefaultPage = (userInfo: any) => {
|
const gotoDefaultPage = (userInfo: any) => {
|
||||||
const pathname =
|
const pathname =
|
||||||
userInfo && userInfo?.is_admin ? '/dashboard' : '/playground';
|
userInfo && userInfo?.is_admin ? '/dashboard' : '/playground';
|
||||||
@@ -23,13 +24,14 @@ const PasswordForm: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
await updatePassword({
|
await updatePassword({
|
||||||
new_password: values.new_password,
|
new_password: values.new_password,
|
||||||
current_password: values.current_password
|
current_password: initialPassword
|
||||||
});
|
});
|
||||||
|
|
||||||
await setUserInfo({
|
await setUserInfo({
|
||||||
...userInfo,
|
...userInfo,
|
||||||
require_password_change: false
|
require_password_change: false
|
||||||
});
|
});
|
||||||
|
setInitialPassword('');
|
||||||
gotoDefaultPage(userInfo);
|
gotoDefaultPage(userInfo);
|
||||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -63,7 +65,7 @@ const PasswordForm: React.FC = () => {
|
|||||||
{intl.formatMessage({ id: 'users.password.modify.description' })}
|
{intl.formatMessage({ id: 'users.password.modify.description' })}
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
{/*
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="current_password"
|
name="current_password"
|
||||||
rules={[
|
rules={[
|
||||||
@@ -82,7 +84,7 @@ const PasswordForm: React.FC = () => {
|
|||||||
prefix={<LockOutlined />}
|
prefix={<LockOutlined />}
|
||||||
label={intl.formatMessage({ id: 'users.form.currentpassword' })}
|
label={intl.formatMessage({ id: 'users.form.currentpassword' })}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item> */}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="new_password"
|
name="new_password"
|
||||||
rules={[
|
rules={[
|
||||||
|
|||||||
+27
-24
@@ -1,3 +1,4 @@
|
|||||||
|
import DropdownButtons from '@/components/drop-down-buttons';
|
||||||
import PageTools from '@/components/page-tools';
|
import PageTools from '@/components/page-tools';
|
||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import type { PageActionType } from '@/config/types';
|
import type { PageActionType } from '@/config/types';
|
||||||
@@ -14,7 +15,7 @@ import {
|
|||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Input, Modal, Space, Table, Tooltip, message } from 'antd';
|
import { Button, Input, Modal, Space, Table, message } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
@@ -44,6 +45,19 @@ const Models: React.FC = () => {
|
|||||||
query: ''
|
query: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ActionList = [
|
||||||
|
{
|
||||||
|
key: 'edit',
|
||||||
|
label: intl.formatMessage({ id: 'common.button.edit' }),
|
||||||
|
icon: <EditOutlined></EditOutlined>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
danger: true,
|
||||||
|
label: intl.formatMessage({ id: 'common.button.delete' }),
|
||||||
|
icon: <DeleteOutlined></DeleteOutlined>
|
||||||
|
}
|
||||||
|
];
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
@@ -172,6 +186,14 @@ const Models: React.FC = () => {
|
|||||||
setTitle(intl.formatMessage({ id: 'users.form.edit' }));
|
setTitle(intl.formatMessage({ id: 'users.form.edit' }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSelect = (val: any, row: ListItem) => {
|
||||||
|
if (val === 'edit') {
|
||||||
|
handleEditUser(row);
|
||||||
|
} else if (val === 'delete') {
|
||||||
|
handleDelete(row);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [queryParams]);
|
}, [queryParams]);
|
||||||
@@ -296,29 +318,10 @@ const Models: React.FC = () => {
|
|||||||
width={200}
|
width={200}
|
||||||
render={(text, record: ListItem) => {
|
render={(text, record: ListItem) => {
|
||||||
return (
|
return (
|
||||||
<Space size={20}>
|
<DropdownButtons
|
||||||
<Tooltip
|
items={ActionList}
|
||||||
title={intl.formatMessage({ id: 'common.button.edit' })}
|
onSelect={(val) => handleSelect(val, record)}
|
||||||
>
|
></DropdownButtons>
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
onClick={() => handleEditUser(record)}
|
|
||||||
icon={<EditOutlined></EditOutlined>}
|
|
||||||
></Button>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip
|
|
||||||
title={intl.formatMessage({ id: 'common.button.delete' })}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
danger
|
|
||||||
onClick={() => handleDelete(record)}
|
|
||||||
icon={<DeleteOutlined></DeleteOutlined>}
|
|
||||||
></Button>
|
|
||||||
</Tooltip>
|
|
||||||
</Space>
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+2
-2
@@ -20,9 +20,9 @@ export const handleBatchRequest = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const convertFileSize = (sizeInBytes: number, prec?: number) => {
|
export const convertFileSize = (sizeInBytes: number, prec?: number) => {
|
||||||
const precision = prec ?? 2;
|
const precision = prec ?? 1;
|
||||||
if (!sizeInBytes) {
|
if (!sizeInBytes) {
|
||||||
return '0 B';
|
return '0';
|
||||||
}
|
}
|
||||||
if (sizeInBytes < 1024) {
|
if (sizeInBytes < 1024) {
|
||||||
return `${sizeInBytes.toFixed(precision)} B`;
|
return `${sizeInBytes.toFixed(precision)} B`;
|
||||||
|
|||||||
Reference in New Issue
Block a user