fix: table list action

This commit is contained in:
jialin
2024-07-01 15:35:48 +08:00
parent 018c8cd208
commit 0f9123ee4f
21 changed files with 316 additions and 169 deletions
+5
View File
@@ -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',
''
);
+9 -1
View File
@@ -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' }
+6 -7
View File
@@ -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
} }
+8
View File
@@ -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);
+5 -4
View File
@@ -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'
}; };
+2 -2
View File
@@ -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',
+3 -2
View File
@@ -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': '已分配显存'
}; };
+13 -16
View File
@@ -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'
} }
+3 -3
View File
@@ -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({
+72 -15
View File
@@ -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"
+6 -6
View File
@@ -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',
+19
View File
@@ -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
}
];
+1
View File
@@ -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;
+64 -79
View File
@@ -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;
}} }}
/> />
+3 -1
View File
@@ -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);
} }
+6 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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`;