style: dashboard utilization

This commit is contained in:
jialin
2024-07-10 10:00:55 +08:00
parent fe14edbb4e
commit 324561958e
16 changed files with 250 additions and 90 deletions
+81
View File
@@ -100,3 +100,84 @@ export const lineItemConfig = {
opacity: 0.7 opacity: 0.7
} }
}; };
export const gaugeItemConfig = {
type: 'gauge',
radius: '88%',
center: ['50%', '65%'],
startAngle: 190,
endAngle: -10,
min: 0,
max: 100,
splitNumber: 5,
progress: {
show: true,
roundCap: false,
width: 12
},
pointer: {
length: '80%',
width: 4,
itemStyle: {
color: 'auto'
}
},
axisLine: {
roundCap: false,
lineStyle: {
width: 12,
// color: [[1, 'rgba(221, 221, 221, 0.5)']],
color: [
[0.5, 'rgba(84, 204, 152, 80%)'],
[0.8, 'rgba(250, 173, 20, 80%)'],
[1, 'rgba(255, 77, 79, 80%)']
]
}
},
axisTick: {
distance: -12,
length: 6,
splitNumber: 5,
lineStyle: {
width: 1.5,
color: 'rgba(255, 255, 255, 1)'
}
},
splitLine: {
distance: -12,
length: 12,
lineStyle: {
width: 1.5,
color: 'rgba(255, 255, 255, 1)'
}
},
axisLabel: {
distance: 14,
color: 'rgba(0, 0, 0, .5)',
fontSize: 12
},
detail: {
lineHeight: 40,
height: 40,
offsetCenter: [5, 30],
valueAnimation: false,
fontSize: 20,
color: 'rgba(0, 0, 0, 0.88)',
formatter(value: any) {
return '{value|' + value + '}{unit|%}';
},
rich: {
value: {
fontSize: 20,
fontWeight: '500',
color: 'rgba(0, 0, 0, 0.88)'
},
unit: {
fontSize: 16,
color: 'rgba(0, 0, 0, 0.88)',
fontWeight: '500',
padding: [0, 0, 0, 2]
}
}
}
};
+63
View File
@@ -0,0 +1,63 @@
import Chart from '@/components/echarts/chart';
import {
gaugeItemConfig,
title as titleConfig
} from '@/components/echarts/config';
import EmptyData from '@/components/empty-data';
import { memo } from 'react';
import { ChartProps } from './types';
const GaugeChart: React.FC<Omit<ChartProps, 'seriesData' | 'xAxisData'>> = (
props
) => {
const { value, height, width, labelFormatter, title, color } = props;
if (!value && value !== 0) {
return <EmptyData height={height} title={title}></EmptyData>;
}
const setDataOptions = () => {
return {
title: {
...titleConfig,
text: title,
top: '0',
left: 'center'
},
series: [
{
...gaugeItemConfig,
axisLine: {
...gaugeItemConfig.axisLine,
lineStyle: {
...gaugeItemConfig.axisLine.lineStyle,
color: [
[value / 100, color],
[1, 'rgba(221, 221, 221, 0.5)']
]
}
},
itemStyle: {
color: 'transparent'
},
detail: {
...gaugeItemConfig.detail,
formatter: labelFormatter || gaugeItemConfig.detail.formatter
},
data: [{ value }]
}
]
};
};
const dataOptions: any = setDataOptions();
return (
<Chart
height={height}
options={dataOptions}
width={width || '100%'}
></Chart>
);
};
export default memo(GaugeChart);
+4 -1
View File
@@ -1,9 +1,10 @@
import type { import type {
// 系列类型的定义后缀都为 SeriesOption // 系列类型的定义后缀都为 SeriesOption
BarSeriesOption, BarSeriesOption,
GaugeSeriesOption,
LineSeriesOption LineSeriesOption
} from 'echarts/charts'; } from 'echarts/charts';
import { BarChart, LineChart } from 'echarts/charts'; import { BarChart, GaugeChart, LineChart } from 'echarts/charts';
import type { import type {
DatasetComponentOption, DatasetComponentOption,
GridComponentOption, GridComponentOption,
@@ -34,6 +35,7 @@ type ECOption = ComposeOption<
| TooltipComponentOption | TooltipComponentOption
| GridComponentOption | GridComponentOption
| DatasetComponentOption | DatasetComponentOption
| GaugeSeriesOption
>; >;
// 注册必须的组件 // 注册必须的组件
@@ -46,6 +48,7 @@ echarts.use([
TransformComponent, TransformComponent,
BarChart, BarChart,
LineChart, LineChart,
GaugeChart,
LabelLayout, LabelLayout,
UniversalTransition, UniversalTransition,
CanvasRenderer CanvasRenderer
+2
View File
@@ -6,7 +6,9 @@ export interface ChartProps {
height: string | number; height: string | number;
width?: string | number; width?: string | number;
title?: string; title?: string;
value?: number;
smooth?: boolean; smooth?: boolean;
color?: string;
} }
export interface AreaChartItemProps { export interface AreaChartItemProps {
+1 -1
View File
@@ -1,7 +1,7 @@
import { createFromIconfontCN } from '@ant-design/icons'; import { createFromIconfontCN } from '@ant-design/icons';
const IconFont = createFromIconfontCN({ const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_4613488_n8fe7jbl7n.js' scriptUrl: '//at.alicdn.com/t/c/font_4613488_t2n96lwj8gf.js'
}); });
export default IconFont; export default IconFont;
@@ -35,9 +35,10 @@ const TableRow: React.FC<
const [firstLoad, setFirstLoad] = useState(true); const [firstLoad, setFirstLoad] = useState(true);
const pollTimer = useRef<any>(null); const pollTimer = useRef<any>(null);
const chunkRequestRef = useRef<any>(null); const chunkRequestRef = useRef<any>(null);
const childrenDataRef = useRef<any[]>([]);
console.log('table row===='); console.log('table row====');
const { updateChunkedList } = useUpdateChunkedList(childrenData, { const { updateChunkedList } = useUpdateChunkedList({
setDataList: setChildrenData, setDataList: setChildrenData,
callback: (list) => renderChildren?.(list) callback: (list) => renderChildren?.(list)
}); });
@@ -54,12 +55,16 @@ const TableRow: React.FC<
}, [rowSelection]); }, [rowSelection]);
useEffect(() => { useEffect(() => {
if (expandedRowKeys?.includes(record[rowKey])) { childrenDataRef.current = childrenData;
setExpanded(true); }, [childrenData]);
} else {
setExpanded(false); // useEffect(() => {
} // if (expandedRowKeys?.includes(record[rowKey])) {
}, [expandedRowKeys]); // setExpanded(true);
// } else {
// setExpanded(false);
// }
// }, [expandedRowKeys]);
useEffect(() => { useEffect(() => {
return () => { return () => {
@@ -102,7 +107,7 @@ const TableRow: React.FC<
const updateChildrenHandler = (list: any) => { const updateChildrenHandler = (list: any) => {
_.each(list, (data: any) => { _.each(list, (data: any) => {
updateChunkedList(data); updateChunkedList(data, childrenDataRef.current);
}); });
}; };
const createChunkRequest = () => { const createChunkRequest = () => {
+1 -1
View File
@@ -386,7 +386,7 @@ body {
.user-menu-container { .user-menu-container {
.user-avatar.ant-menu-submenu { .user-avatar.ant-menu-submenu {
.ant-menu-submenu-title { .ant-menu-submenu-title {
padding-left: 8px; padding-left: 10px;
} }
} }
+1 -1
View File
@@ -70,7 +70,7 @@ export function getRightRenderContent(opts: {
mode: 'vertical', mode: 'vertical',
expandIcon: false, expandIcon: false,
inlineCollapsed: collapsed, inlineCollapsed: collapsed,
triggerSubMenuAction: 'click', triggerSubMenuAction: 'hover',
items: [ items: [
{ {
key: 'lang', key: 'lang',
+1 -1
View File
@@ -3,7 +3,7 @@ export default {
'playground.title': '试验场', 'playground.title': '试验场',
'playground.system': '系统', 'playground.system': '系统',
'playground.user': '用户', 'playground.user': '用户',
'playground.assistant': '助手', 'playground.assistant': '助手',
'playground.newMessage': '新消息', 'playground.newMessage': '新消息',
'playground.viewcode': '查看代码', 'playground.viewcode': '查看代码',
'playground.model': '模型', 'playground.model': '模型',
+31 -13
View File
@@ -1,4 +1,5 @@
import CardWrapper from '@/components/card-wrapper'; import CardWrapper from '@/components/card-wrapper';
import GaugeChart from '@/components/echarts/gauge';
import PageTools from '@/components/page-tools'; import PageTools from '@/components/page-tools';
import breakpoints from '@/config/breakpoints'; import breakpoints from '@/config/breakpoints';
import useWindowResize from '@/hooks/use-window-resize'; import useWindowResize from '@/hooks/use-window-resize';
@@ -7,10 +8,19 @@ import { Col, Row } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import UitilBar from '../../../components/util-bar';
import { DashboardContext } from '../config/dashboard-context'; import { DashboardContext } from '../config/dashboard-context';
import ResourceUtilization from './resource-utilization'; import ResourceUtilization from './resource-utilization';
const strokeColorFunc = (percent: number) => {
if (percent <= 50) {
return 'rgb(84, 204, 152, 80%)';
}
if (percent <= 80) {
return 'rgba(250, 173, 20, 80%)';
}
return ' rgba(255, 77, 79, 80%)';
};
const SystemLoad = () => { const SystemLoad = () => {
const intl = useIntl(); const intl = useIntl();
const data = useContext(DashboardContext)?.system_load?.current || {}; const data = useContext(DashboardContext)?.system_load?.current || {};
@@ -58,36 +68,44 @@ const SystemLoad = () => {
<CardWrapper style={{ height: largeChartHeight, width: '100%' }}> <CardWrapper style={{ height: largeChartHeight, width: '100%' }}>
<Row style={{ height: largeChartHeight, width: '100%' }}> <Row style={{ height: largeChartHeight, width: '100%' }}>
<Col span={12} style={{ height: smallChartHeight }}> <Col span={12} style={{ height: smallChartHeight }}>
<UitilBar <GaugeChart
height={smallChartHeight}
value={_.round(data.gpu?.utilization_rate || 0, 1)}
color={strokeColorFunc(data.gpu?.utilization_rate)}
title={intl.formatMessage({ title={intl.formatMessage({
id: 'dashboard.gpuutilization' id: 'dashboard.gpuutilization'
})} })}
percent={_.round(data.gpu?.utilization_rate || 0, 1)} ></GaugeChart>
></UitilBar>
</Col> </Col>
<Col span={12} style={{ height: smallChartHeight }}> <Col span={12} style={{ height: smallChartHeight }}>
<UitilBar <GaugeChart
title={intl.formatMessage({ title={intl.formatMessage({
id: 'dashboard.vramutilization' id: 'dashboard.vramutilization'
})} })}
percent={_.round(data.gpu_memory?.utilization_rate || 0, 1)} height={smallChartHeight}
></UitilBar> color={strokeColorFunc(data.gpu_memory?.utilization_rate)}
value={_.round(data.gpu_memory?.utilization_rate || 0, 1)}
></GaugeChart>
</Col> </Col>
<Col span={12} style={{ height: smallChartHeight }}> <Col span={12} style={{ height: smallChartHeight }}>
<UitilBar <GaugeChart
title={intl.formatMessage({ title={intl.formatMessage({
id: 'dashboard.cpuutilization' id: 'dashboard.cpuutilization'
})} })}
percent={_.round(data.cpu?.utilization_rate || 0, 1)} height={smallChartHeight}
></UitilBar> color={strokeColorFunc(data.cpu?.utilization_rate)}
value={_.round(data.cpu?.utilization_rate || 0, 1)}
></GaugeChart>
</Col> </Col>
<Col span={12} style={{ height: smallChartHeight }}> <Col span={12} style={{ height: smallChartHeight }}>
<UitilBar <GaugeChart
title={intl.formatMessage({ title={intl.formatMessage({
id: 'dashboard.memoryutilization' id: 'dashboard.memoryutilization'
})} })}
percent={_.round(data.memory?.utilization_rate || 0, 1)} height={smallChartHeight}
></UitilBar> color={strokeColorFunc(data.memory?.utilization_rate)}
value={_.round(data.memory?.utilization_rate || 0, 1)}
></GaugeChart>
</Col> </Col>
</Row> </Row>
</CardWrapper> </CardWrapper>
@@ -29,7 +29,11 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
{ {
label: intl.formatMessage({ id: 'common.button.viewlog' }), label: intl.formatMessage({ id: 'common.button.viewlog' }),
key: 'viewlog', key: 'viewlog',
status: [InstanceStatusMap.Running, InstanceStatusMap.Error], status: [
InstanceStatusMap.Running,
InstanceStatusMap.Error,
InstanceStatusMap.Downloading
],
icon: <FieldTimeOutlined /> icon: <FieldTimeOutlined />
}, },
{ {
+40 -31
View File
@@ -1,5 +1,6 @@
import IconFont from '@/components/icon-font'; import IconFont from '@/components/icon-font';
import HotKeys from '@/config/hotkeys'; import HotKeys from '@/config/hotkeys';
import { platformCall } from '@/utils';
import { import {
CodeOutlined, CodeOutlined,
DeleteOutlined, DeleteOutlined,
@@ -7,8 +8,9 @@ import {
PlusOutlined PlusOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Col, Row, Space } from 'antd'; import { Button, Col, Dropdown, Row, Space } from 'antd';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { Roles } from '../config';
import '../style/chat-footer.less'; import '../style/chat-footer.less';
interface ChatFooterProps { interface ChatFooterProps {
@@ -23,6 +25,7 @@ interface ChatFooterProps {
} }
const ChatFooter: React.FC<ChatFooterProps> = (props) => { const ChatFooter: React.FC<ChatFooterProps> = (props) => {
const platform = platformCall();
const intl = useIntl(); const intl = useIntl();
const { const {
onSubmit, onSubmit,
@@ -42,38 +45,28 @@ const ChatFooter: React.FC<ChatFooterProps> = (props) => {
{ enabled: !disabled } { enabled: !disabled }
); );
const renderSubmitButton = () => { const MessageRoles = [
if (disabled) { { key: Roles.User, label: intl.formatMessage({ id: 'playground.user' }) },
return ( {
<> key: Roles.Assistant,
{intl.formatMessage({ id: 'common.button.stop' })} label: intl.formatMessage({ id: 'playground.assistant' })
<span className="m-l-5">
<IconFont type="icon-stop"></IconFont>
</span>
</>
);
} }
return ( ];
<>
{intl.formatMessage({ id: 'common.button.submit' })}
<span className="m-l-5 opct-7">
<IconFont type="icon-command"></IconFont> + <EnterOutlined />
</span>
</>
);
};
return ( return (
<div className="chat-footer"> <div className="chat-footer">
<Row style={{ width: '100%' }}> <Row style={{ width: '100%' }}>
<Col span={hasTokenResult ? 8 : 12}> <Col span={hasTokenResult ? 8 : 12}>
<Space size={20}> <Space size={20}>
<Button <Dropdown
disabled={disabled} menu={{ items: MessageRoles, onClick: onNewMessage }}
icon={<PlusOutlined />} placement="topLeft"
onClick={onNewMessage}
> >
{intl.formatMessage({ id: 'playground.newMessage' })} <Button disabled={disabled} icon={<PlusOutlined />}>
</Button> {intl.formatMessage({ id: 'playground.newMessage' })}
</Button>
</Dropdown>
<Button <Button
icon={<DeleteOutlined></DeleteOutlined>} icon={<DeleteOutlined></DeleteOutlined>}
onClick={onClear} onClick={onClear}
@@ -97,15 +90,31 @@ const ChatFooter: React.FC<ChatFooterProps> = (props) => {
<Button type="primary" disabled={disabled} onClick={onSubmit}> <Button type="primary" disabled={disabled} onClick={onSubmit}>
{intl.formatMessage({ id: 'common.button.submit' })} {intl.formatMessage({ id: 'common.button.submit' })}
<span className="m-l-5 opct-7"> <span className="m-l-5 opct-7">
<IconFont type="icon-command"></IconFont> + <EnterOutlined /> {platform.isMac ? (
<>
<IconFont type="icon-command"></IconFont> +{' '}
<EnterOutlined />
</>
) : (
<>
CTRL + <EnterOutlined />
</>
)}
</span> </span>
</Button> </Button>
) : ( ) : (
<Button type="primary" onClick={onStop}> <Button type="primary" onClick={onStop}>
{intl.formatMessage({ id: 'common.button.stop' })} <div className="flex flex-center">
<span className="m-l-5"> <span>
<IconFont type="icon-stop"></IconFont> {intl.formatMessage({ id: 'common.button.stop' })}
</span> </span>
<span className="m-l-5 flex flex-center">
<IconFont
type="icon-stop1"
className="font-size-14"
></IconFont>
</span>
</div>
</Button> </Button>
)} )}
</Space> </Space>
@@ -56,10 +56,11 @@ const MessageList: React.FC<MessageProps> = (props) => {
const setMessageId = () => { const setMessageId = () => {
messageId.current = messageId.current + 1; messageId.current = messageId.current + 1;
}; };
const handleNewMessage = () => { const handleNewMessage = (role: any) => {
messageList.push({ messageList.push({
role: // role:
_.last(messageList)?.role === Roles.User ? Roles.Assistant : Roles.User, // _.last(messageList)?.role === Roles.User ? Roles.Assistant : Roles.User,
role: role.key,
content: '', content: '',
uid: messageId.current + 1 uid: messageId.current + 1
}); });
@@ -19,14 +19,12 @@ type ParamsSettingsFormProps = {
}; };
type ParamsSettingsProps = { type ParamsSettingsProps = {
onClose?: () => void;
selectedModel?: string; selectedModel?: string;
params?: ParamsSettingsFormProps; params?: ParamsSettingsFormProps;
setParams: (params: any) => void; setParams: (params: any) => void;
}; };
const ParamsSettings: React.FC<ParamsSettingsProps> = ({ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
onClose,
selectedModel, selectedModel,
setParams setParams
}) => { }) => {
@@ -83,11 +81,6 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
console.log('handleOnFinishFailed', errorInfo); console.log('handleOnFinishFailed', errorInfo);
}; };
const handleCancel = () => {
form.resetFields();
onClose?.();
};
const handleValuesChange = (changedValues: any, allValues: any) => { const handleValuesChange = (changedValues: any, allValues: any) => {
setParams?.(allValues); setParams?.(allValues);
}; };
+2 -2
View File
@@ -5,11 +5,11 @@ export const Roles = {
}; };
export const playGroundRoles = [ export const playGroundRoles = [
{ {
key: 'user', key: Roles.User,
label: 'playground.user' label: 'playground.user'
}, },
{ {
key: 'assistant', key: Roles.Assistant,
label: 'playground.assitant' label: 'playground.assitant'
} }
]; ];
+1 -20
View File
@@ -8,24 +8,9 @@ import './style/play-ground.less';
const Playground: React.FC = () => { const Playground: React.FC = () => {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const [selectedModel, setSelectedModel] = useState('llama3:latest');
const [showPopover, setShowPopover] = useState(false);
const selectModel = searchParams.get('model') || ''; const selectModel = searchParams.get('model') || '';
const [params, setParams] = useState({}); const [params, setParams] = useState({});
console.log('query======', searchParams, selectModel);
const handleSelectChange = (value: string) => {
setSelectedModel(value);
};
const handleTogglePopover = () => {
setShowPopover(!showPopover);
};
const handleClosePopover = () => {
setShowPopover(false);
};
return ( return (
<div> <div>
<PageContainer ghost extra={[]}> <PageContainer ghost extra={[]}>
@@ -38,11 +23,7 @@ const Playground: React.FC = () => {
</div> </div>
<div className="params"> <div className="params">
<ParamsSettings <ParamsSettings setParams={setParams} selectedModel={selectModel} />
onClose={handleClosePopover}
setParams={setParams}
selectedModel={selectModel}
/>
</div> </div>
</div> </div>
</PageContainer> </PageContainer>