build: output files to dir
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import { Bar } from '@ant-design/plots';
|
||||
|
||||
interface BarChartProps {
|
||||
data: any[];
|
||||
xField: string;
|
||||
yField: string;
|
||||
title?: string;
|
||||
height?: number;
|
||||
}
|
||||
const BarChart: React.FC<BarChartProps> = (props) => {
|
||||
const { data, xField, yField, title, height = 300 } = props;
|
||||
const config = {
|
||||
data,
|
||||
xField,
|
||||
yField,
|
||||
title,
|
||||
// colorField: 'name',
|
||||
direction: 'vertical',
|
||||
height,
|
||||
group: true,
|
||||
legend: {
|
||||
color: {
|
||||
position: 'top',
|
||||
layout: {
|
||||
justifyContent: 'center'
|
||||
}
|
||||
}
|
||||
},
|
||||
axis: {
|
||||
x: {
|
||||
xAxis: true
|
||||
}
|
||||
},
|
||||
|
||||
split: {
|
||||
type: 'line',
|
||||
line: {
|
||||
color: 'red',
|
||||
style: {
|
||||
color: 'red',
|
||||
lineDash: [4, 5]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
style: {
|
||||
fill: '#2fbf85',
|
||||
radiusTopLeft: 8,
|
||||
radiusTopRight: 8,
|
||||
height: 30
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Bar {...config} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BarChart;
|
||||
@@ -0,0 +1,61 @@
|
||||
import { Column } from '@ant-design/plots';
|
||||
|
||||
interface BarChartProps {
|
||||
data: any[];
|
||||
xField: string;
|
||||
yField: string;
|
||||
title?: string;
|
||||
height?: number;
|
||||
}
|
||||
const BarChart: React.FC<BarChartProps> = (props) => {
|
||||
const { data, xField, yField, title, height = 260 } = props;
|
||||
const config = {
|
||||
data,
|
||||
xField,
|
||||
yField,
|
||||
title,
|
||||
// colorField: 'name',
|
||||
direction: 'vertical',
|
||||
height,
|
||||
group: true,
|
||||
legend: {
|
||||
color: {
|
||||
position: 'top',
|
||||
layout: {
|
||||
justifyContent: 'center'
|
||||
}
|
||||
}
|
||||
},
|
||||
axis: {
|
||||
x: {
|
||||
xAxis: true
|
||||
}
|
||||
},
|
||||
|
||||
split: {
|
||||
type: 'line',
|
||||
line: {
|
||||
color: 'red',
|
||||
style: {
|
||||
color: 'red',
|
||||
lineDash: [4, 5]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
style: {
|
||||
fill: '#2fbf85',
|
||||
radiusTopLeft: 8,
|
||||
radiusTopRight: 8,
|
||||
width: 30
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Column {...config} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BarChart;
|
||||
@@ -0,0 +1,17 @@
|
||||
.content-wrapper {
|
||||
.content {
|
||||
padding-block-start: 0;
|
||||
padding-block-end: 32px;
|
||||
padding-inline: 40px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: var(--font-size-large);
|
||||
font-weight: 600;
|
||||
line-height: 32px;
|
||||
padding-block-start: 8px;
|
||||
padding-block-end: 16px;
|
||||
padding-inline-start: 40px;
|
||||
padding-inline-end: 40px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import './index.less';
|
||||
|
||||
const ContentWrapper: React.FC<{
|
||||
children: React.ReactNode;
|
||||
title: React.ReactNode;
|
||||
titleStyle?: React.CSSProperties;
|
||||
contentStyle?: React.CSSProperties;
|
||||
}> = ({ children, title = false, titleStyle, contentStyle }) => {
|
||||
return (
|
||||
<div className="content-wrapper">
|
||||
{title && (
|
||||
<div className="title" style={{ ...titleStyle }}>
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<div className="content" style={{ ...contentStyle }}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContentWrapper;
|
||||
@@ -14,6 +14,8 @@ html {
|
||||
--color-text-1: var(--ant-color-text);
|
||||
--color-bg-light-1: #f0fff6;
|
||||
--font-size-base: 12px;
|
||||
--font-size-large: 16px;
|
||||
--font-size-middle: 14px;
|
||||
--ant-color-fill-secondary: rgba(0, 0, 0, 6%);
|
||||
--table-td-radius: 24px;
|
||||
--checkbox-border-radius: 4px;
|
||||
|
||||
@@ -161,7 +161,7 @@ const ModelBills: React.FC = () => {
|
||||
<PageTools
|
||||
marginBottom={10}
|
||||
marginTop={0}
|
||||
left={<span>Bills by model</span>}
|
||||
left={false}
|
||||
right={
|
||||
<Space>
|
||||
<Select
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { Card, Col, Row } from 'antd';
|
||||
import { Card, Col, Row, Space } from 'antd';
|
||||
import React from 'react';
|
||||
import { overviewConfigs } from '../config';
|
||||
import '../styles/index.less';
|
||||
import styles from './over-view.less';
|
||||
|
||||
const CardItem: React.FC<{ label: string; value: number; bgColor: string }> = ({
|
||||
label,
|
||||
value,
|
||||
bgColor
|
||||
const renderCardItem = (data: {
|
||||
label: string;
|
||||
value: React.ReactNode;
|
||||
bgColor: string;
|
||||
}) => {
|
||||
const { label, value, bgColor } = data;
|
||||
return (
|
||||
<Card
|
||||
bordered={false}
|
||||
@@ -21,16 +24,52 @@ const CardItem: React.FC<{ label: string; value: number; bgColor: string }> = ({
|
||||
);
|
||||
};
|
||||
const Overview: React.FC = (props) => {
|
||||
const { data = {} } = props;
|
||||
// const { data = {} } = props;
|
||||
const data = {
|
||||
workers: 8,
|
||||
models: {
|
||||
healthy: 10,
|
||||
warning: 2,
|
||||
error: 1
|
||||
},
|
||||
gpus: 30,
|
||||
allocatedGpus: 12,
|
||||
instances: {
|
||||
healthy: 32,
|
||||
warning: 3,
|
||||
error: 2
|
||||
}
|
||||
};
|
||||
|
||||
const renderValue = (
|
||||
value:
|
||||
| number
|
||||
| {
|
||||
healthy: number;
|
||||
warning: number;
|
||||
error: number;
|
||||
}
|
||||
) => {
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
return (
|
||||
<Space className="value-box">
|
||||
<span className={'value-healthy'}>{value.healthy}</span>
|
||||
<span className={'value-warning'}>{value.warning}</span>
|
||||
<span className={'value-error'}>{value.error}</span>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Row gutter={[20, 20]} className={styles.row}>
|
||||
{overviewConfigs.map((config, index) => (
|
||||
<Col span={5} key={config.key}>
|
||||
<CardItem
|
||||
label={config.label}
|
||||
value={data[config.key] || 0}
|
||||
bgColor={config.backgroundColor}
|
||||
/>
|
||||
{renderCardItem({
|
||||
label: config.label,
|
||||
value: renderValue(data[config.key] || 0),
|
||||
bgColor: config.backgroundColor
|
||||
})}
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import PageTools from '@/components/page-tools';
|
||||
import { generateRandomArray } from '@/utils';
|
||||
import { Line } from '@ant-design/plots';
|
||||
import { DatePicker } from 'antd';
|
||||
import _ from 'lodash';
|
||||
|
||||
const mockData = {
|
||||
GPU: generateRandomArray(),
|
||||
CPU: generateRandomArray(),
|
||||
Memory: generateRandomArray(),
|
||||
VRAM: generateRandomArray()
|
||||
};
|
||||
const UtilizationOvertime: React.FC = () => {
|
||||
const timeList = [
|
||||
'01:00:00',
|
||||
'02:00:00',
|
||||
'03:00:00',
|
||||
'04:00:00',
|
||||
'05:00:00',
|
||||
// '01:00:00',
|
||||
// '02:00:00',
|
||||
// '03:00:00',
|
||||
// '04:00:00',
|
||||
// '05:00:00',
|
||||
'06:00:00',
|
||||
'07:00:00',
|
||||
'08:00:00',
|
||||
@@ -21,7 +27,7 @@ const UtilizationOvertime: React.FC = () => {
|
||||
'14:00:00',
|
||||
'15:00:00'
|
||||
];
|
||||
const typeList = ['GPU', 'CPU', 'Memory'];
|
||||
const typeList = ['GPU', 'CPU', 'Memory', 'VRAM'];
|
||||
const generateData = () => {
|
||||
const data = [];
|
||||
for (let i = 0; i < timeList.length; i++) {
|
||||
@@ -29,7 +35,7 @@ const UtilizationOvertime: React.FC = () => {
|
||||
data.push({
|
||||
time: timeList[i],
|
||||
type: typeList[j],
|
||||
value: _.round(Math.random(), 3)
|
||||
value: _.get(mockData, typeList[j])[i]
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -41,10 +47,12 @@ const UtilizationOvertime: React.FC = () => {
|
||||
console.log('dateString============', date);
|
||||
};
|
||||
const config = {
|
||||
// title: 'Resource Utilization',
|
||||
xField: 'time',
|
||||
yField: 'value',
|
||||
color: ['red', 'blue', 'green'],
|
||||
colorField: 'type',
|
||||
autoFit: true,
|
||||
slider: false,
|
||||
shapeField: 'smooth',
|
||||
axis: {
|
||||
@@ -69,21 +77,15 @@ const UtilizationOvertime: React.FC = () => {
|
||||
tooltip: {
|
||||
title: 'time',
|
||||
items: [{ channel: 'y' }]
|
||||
},
|
||||
label: {
|
||||
autoRotate: true
|
||||
}
|
||||
// label: {
|
||||
// autoRotate: true
|
||||
// }
|
||||
};
|
||||
// <DatePicker onChange={handleSelectDate} style={{ width: 300 }} />
|
||||
return (
|
||||
<>
|
||||
<PageTools
|
||||
marginBottom={10}
|
||||
marginTop={0}
|
||||
left={<span>Utilization Over Time</span>}
|
||||
right={
|
||||
<DatePicker onChange={handleSelectDate} style={{ width: 300 }} />
|
||||
}
|
||||
/>
|
||||
<PageTools marginBottom={10} marginTop={0} left={false} right={false} />
|
||||
<Line height={400} data={data} {...config} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
export const overviewConfigs = [
|
||||
{
|
||||
key: 'models',
|
||||
label: 'Models',
|
||||
backgroundColor:
|
||||
'linear-gradient(180deg, rgba(0,188,203,.2) 0%, rgba(40,207,181,.2) 100%)'
|
||||
},
|
||||
{
|
||||
key: 'nodes',
|
||||
label: 'Nodes',
|
||||
key: 'workers',
|
||||
label: 'Workers',
|
||||
backgroundColor:
|
||||
'linear-gradient(180deg, rgba(0,188,203,.2) 0%, rgba(40,207,181,.2) 100%)'
|
||||
},
|
||||
@@ -17,16 +11,22 @@ export const overviewConfigs = [
|
||||
backgroundColor:
|
||||
'linear-gradient(180deg, rgba(0,188,203,.2) 0%, rgba(40,207,181,.2) 100%)'
|
||||
},
|
||||
{
|
||||
key: 'idleGpus',
|
||||
label: 'Idle GPUs',
|
||||
backgroundColor:
|
||||
'linear-gradient(180deg, rgba(0,188,203,.2) 0%, rgba(40,207,181,.2) 100%)'
|
||||
},
|
||||
{
|
||||
key: 'allocatedGpus',
|
||||
label: 'Allocated GPUs',
|
||||
backgroundColor:
|
||||
'linear-gradient(180deg, rgba(0,188,203,.2) 0%, rgba(40,207,181,.2) 100%)'
|
||||
},
|
||||
{
|
||||
key: 'models',
|
||||
label: 'Models',
|
||||
backgroundColor:
|
||||
'linear-gradient(180deg, rgba(0,188,203,.2) 0%, rgba(40,207,181,.2) 100%)'
|
||||
},
|
||||
{
|
||||
key: 'instances',
|
||||
label: 'Instances',
|
||||
backgroundColor:
|
||||
'linear-gradient(180deg, rgba(0,188,203,.2) 0%, rgba(40,207,181,.2) 100%)'
|
||||
}
|
||||
];
|
||||
|
||||
+133
-11
@@ -1,11 +1,94 @@
|
||||
import BarChart from '@/components/bar-chart';
|
||||
import HBar from '@/components/bar-chart/h-bar';
|
||||
import ContentWrapper from '@/components/content-wrapper';
|
||||
import DividerLine from '@/components/divider-line';
|
||||
import PageTools from '@/components/page-tools';
|
||||
import { generateRandomArray } from '@/utils';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { Col, Row } from 'antd';
|
||||
import GPUUtilization from './components/gpu-utilization';
|
||||
import Overview from './components/over-view';
|
||||
import UtilizationOvertime from './components/utilization-overtime';
|
||||
|
||||
const times = [
|
||||
'june 1',
|
||||
'june 2',
|
||||
'june 3',
|
||||
'june 4',
|
||||
'june 5',
|
||||
'june 6',
|
||||
'june 7'
|
||||
];
|
||||
|
||||
const users = ['Jim', 'Lucy', 'Lily', 'Tom', 'Jack', 'Rose', 'Jerry'];
|
||||
const projects = [
|
||||
'copilot-dev',
|
||||
'rag-wiki',
|
||||
'smart-auto-agent',
|
||||
'office-auto-docs',
|
||||
'smart-customer-service'
|
||||
];
|
||||
|
||||
const APIRequestData = generateRandomArray({
|
||||
length: times.length,
|
||||
max: 100,
|
||||
min: 0,
|
||||
offset: 10
|
||||
});
|
||||
|
||||
const TokensData = generateRandomArray({
|
||||
length: times.length,
|
||||
max: 2000,
|
||||
min: 200,
|
||||
offset: 200
|
||||
});
|
||||
|
||||
const usersData = generateRandomArray({
|
||||
length: users.length,
|
||||
max: 100,
|
||||
min: 0,
|
||||
offset: 10
|
||||
});
|
||||
|
||||
const projectsData = generateRandomArray({
|
||||
length: projects.length,
|
||||
max: 100,
|
||||
min: 0,
|
||||
offset: 10
|
||||
});
|
||||
|
||||
const userDataList = usersData
|
||||
.map((val, i) => {
|
||||
return {
|
||||
time: users[i],
|
||||
value: val
|
||||
};
|
||||
})
|
||||
.sort((a, b) => b.value - a.value);
|
||||
|
||||
const projectDataList = projectsData
|
||||
.map((val, i) => {
|
||||
return {
|
||||
time: projects[i],
|
||||
value: val
|
||||
};
|
||||
})
|
||||
.sort((a, b) => b.value - a.value);
|
||||
|
||||
const dataList = APIRequestData.map((val, i) => {
|
||||
console.log('val', val);
|
||||
return {
|
||||
time: times[i],
|
||||
value: val
|
||||
};
|
||||
});
|
||||
const tokenUsage = TokensData.map((val, i) => {
|
||||
console.log('val', val);
|
||||
return {
|
||||
time: times[i],
|
||||
value: val
|
||||
};
|
||||
});
|
||||
const Dashboard: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
@@ -13,6 +96,10 @@ const Dashboard: React.FC = () => {
|
||||
<Overview></Overview>
|
||||
</PageContainer>
|
||||
<DividerLine></DividerLine>
|
||||
<PageContainer ghost title="System Load">
|
||||
<UtilizationOvertime></UtilizationOvertime>
|
||||
</PageContainer>
|
||||
<DividerLine></DividerLine>
|
||||
<PageContainer ghost title={false}>
|
||||
<Row gutter={20} style={{ marginTop: '0px' }}>
|
||||
<Col span={12}>
|
||||
@@ -54,18 +141,53 @@ const Dashboard: React.FC = () => {
|
||||
</Col>
|
||||
</Row>
|
||||
</PageContainer>
|
||||
{/* <DividerLine></DividerLine>
|
||||
<PageContainer ghost title={false}>
|
||||
<div style={{ marginTop: '0px' }}>
|
||||
<ModelBills></ModelBills>
|
||||
</div>
|
||||
</PageContainer> */}
|
||||
<DividerLine></DividerLine>
|
||||
<PageContainer ghost title={false}>
|
||||
<div style={{ marginTop: '0px' }}>
|
||||
<UtilizationOvertime></UtilizationOvertime>
|
||||
</div>
|
||||
</PageContainer>
|
||||
<Row gutter={10}>
|
||||
<Col span={12}>
|
||||
<ContentWrapper
|
||||
contentStyle={{ paddingRight: 0 }}
|
||||
title={<span style={{ lineHeight: '48px' }}>API Request</span>}
|
||||
>
|
||||
<BarChart data={dataList} xField="time" yField="value"></BarChart>
|
||||
</ContentWrapper>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<ContentWrapper
|
||||
contentStyle={{ paddingRight: 0 }}
|
||||
title={<span style={{ lineHeight: '48px' }}>Tokens</span>}
|
||||
>
|
||||
<BarChart data={tokenUsage} xField="time" yField="value"></BarChart>
|
||||
</ContentWrapper>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={10}>
|
||||
<Col span={12}>
|
||||
<ContentWrapper
|
||||
contentStyle={{ paddingRight: 0 }}
|
||||
title={<span style={{ lineHeight: '48px' }}>Top Users</span>}
|
||||
>
|
||||
<HBar
|
||||
data={userDataList}
|
||||
xField="time"
|
||||
yField="value"
|
||||
height={360}
|
||||
></HBar>
|
||||
</ContentWrapper>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<ContentWrapper
|
||||
contentStyle={{ paddingRight: 0 }}
|
||||
title={<span style={{ lineHeight: '48px' }}>Top Projects</span>}
|
||||
>
|
||||
<HBar
|
||||
data={projectDataList}
|
||||
xField="time"
|
||||
yField="value"
|
||||
height={300}
|
||||
></HBar>
|
||||
</ContentWrapper>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
.value-box {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
.value-healthy {
|
||||
color: var(--ant-green-6);
|
||||
}
|
||||
|
||||
.value-warning {
|
||||
color: var(--ant-gold-6);
|
||||
}
|
||||
|
||||
.value-danger {
|
||||
color: var(--ant-red-6);
|
||||
}
|
||||
}
|
||||
@@ -129,6 +129,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
onChange={handleInputRepoChange}
|
||||
onSearch={debounceSearch}
|
||||
options={repoOptions}
|
||||
description="Only .gguf format is supported"
|
||||
></SealAutoComplete>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData>
|
||||
@@ -210,11 +211,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
>
|
||||
<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>
|
||||
<SealInput.Input label="Name" required></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData> name="source" rules={[{ required: true }]}>
|
||||
<SealSelect
|
||||
|
||||
@@ -16,14 +16,21 @@ import ViewCodeModal from './view-code-modal';
|
||||
interface MessageProps {
|
||||
parameters: any;
|
||||
}
|
||||
|
||||
interface MessageItemProps {
|
||||
role: string;
|
||||
content: string;
|
||||
uid: number;
|
||||
}
|
||||
|
||||
const MessageList: React.FC<MessageProps> = (props) => {
|
||||
const { parameters } = props;
|
||||
const [messageList, setMessageList] = useState<
|
||||
{ role: string; content: string }[]
|
||||
>([
|
||||
const messageId = useRef<number>(0);
|
||||
const [messageList, setMessageList] = useState<MessageItemProps[]>([
|
||||
{
|
||||
role: 'user',
|
||||
content: ''
|
||||
content: '',
|
||||
uid: messageId.current
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -37,11 +44,17 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
const handleSystemMessageChange = (e: any) => {
|
||||
setSystemMessage(e.target.value);
|
||||
};
|
||||
|
||||
const setMessageId = () => {
|
||||
messageId.current = messageId.current + 1;
|
||||
};
|
||||
const handleNewMessage = () => {
|
||||
messageList.push({
|
||||
role: 'user',
|
||||
content: ''
|
||||
content: '',
|
||||
uid: messageId.current + 1
|
||||
});
|
||||
setMessageId();
|
||||
setMessageList([...messageList]);
|
||||
setActiveIndex(messageList.length - 1);
|
||||
};
|
||||
@@ -71,9 +84,11 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
...messageList,
|
||||
{
|
||||
role: Roles.Assistant,
|
||||
content: assistant.content
|
||||
content: assistant.content,
|
||||
uid: messageId.current + 1
|
||||
}
|
||||
]);
|
||||
setMessageId();
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
@@ -101,10 +116,7 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
setMessageList([...messageList]);
|
||||
};
|
||||
|
||||
const handleUpdateMessage = (
|
||||
index: number,
|
||||
message: { role: string; content: string }
|
||||
) => {
|
||||
const handleUpdateMessage = (index: number, message: MessageItemProps) => {
|
||||
messageList[index] = message;
|
||||
console.log('updatemessage========', index, message);
|
||||
setMessageList([...messageList]);
|
||||
@@ -143,12 +155,12 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
{messageList.map((item, index) => {
|
||||
return (
|
||||
<MessageItem
|
||||
key={index}
|
||||
key={item.uid}
|
||||
isFocus={index === activeIndex}
|
||||
islast={index === messageList.length - 1}
|
||||
loading={loading}
|
||||
onDelete={() => handleDelete(index)}
|
||||
updateMessage={(message: { role: string; content: string }) =>
|
||||
updateMessage={(message: MessageItemProps) =>
|
||||
handleUpdateMessage(index, message)
|
||||
}
|
||||
message={item}
|
||||
|
||||
@@ -4,18 +4,20 @@ import _ from 'lodash';
|
||||
import { memo, useEffect, useRef, useState } from 'react';
|
||||
import { Roles } from '../config';
|
||||
import '../style/message-item.less';
|
||||
interface MessageItemProps {
|
||||
role: string;
|
||||
content: string;
|
||||
uid: number;
|
||||
}
|
||||
|
||||
const MessageItem: React.FC<{
|
||||
message: {
|
||||
role: string;
|
||||
content: string;
|
||||
};
|
||||
message: MessageItemProps;
|
||||
loading?: boolean;
|
||||
islast?: boolean;
|
||||
updateMessage: (message: { role: string; content: string }) => void;
|
||||
updateMessage: (message: MessageItemProps) => void;
|
||||
isFocus: boolean;
|
||||
onDelete: () => void;
|
||||
}> = ({ message, isFocus, onDelete, updateMessage, loading, islast }) => {
|
||||
}> = ({ message, isFocus, onDelete, updateMessage }) => {
|
||||
const [roleType, setRoleType] = useState(message.role);
|
||||
const [isTyping, setIsTyping] = useState(false);
|
||||
const [messageContent, setMessageContent] = useState(message.content);
|
||||
@@ -51,7 +53,11 @@ const MessageItem: React.FC<{
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAnimating && !isInitialRender.current) {
|
||||
updateMessage({ role: roleType, content: messageContent });
|
||||
updateMessage({
|
||||
role: roleType,
|
||||
content: messageContent,
|
||||
uid: message.uid
|
||||
});
|
||||
} else {
|
||||
isInitialRender.current = false;
|
||||
}
|
||||
|
||||
@@ -28,3 +28,30 @@ export const convertFileSize = (sizeInBytes: number) => {
|
||||
return `${(sizeInBytes / (1024 * 1024 * 1024 * 1024)).toFixed(2)} TB`;
|
||||
}
|
||||
};
|
||||
|
||||
export const generateRandomArray = (config?: {
|
||||
min: number;
|
||||
max: number;
|
||||
length: number;
|
||||
offset: number;
|
||||
}) => {
|
||||
const { min, max, length, offset } = config || {
|
||||
min: 10,
|
||||
max: 80,
|
||||
length: 10,
|
||||
offset: 10
|
||||
};
|
||||
|
||||
const data = [];
|
||||
let prevValue = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
// 确保波动不太大
|
||||
let newValue = prevValue + Math.floor(Math.random() * 21) - offset; // 波动范围 [-10, 10]
|
||||
newValue = Math.max(min, Math.min(max, newValue)); // 保证在 [10, 100] 范围内
|
||||
data.push(newValue);
|
||||
prevValue = newValue;
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user