mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-16 01:06:00 +08:00
### What problem does this PR solve? fix: Add Task Executor to system panel #2061 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
cc6a48b128
commit
60767e66e0
@ -10,44 +10,6 @@ import {
|
|||||||
} from 'recharts';
|
} from 'recharts';
|
||||||
import { CategoricalChartProps } from 'recharts/types/chart/generateCategoricalChart';
|
import { CategoricalChartProps } from 'recharts/types/chart/generateCategoricalChart';
|
||||||
|
|
||||||
const data = [
|
|
||||||
{
|
|
||||||
name: 'Page A',
|
|
||||||
uv: 4000,
|
|
||||||
pv: 2400,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Page B',
|
|
||||||
uv: 3000,
|
|
||||||
pv: 1398,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Page C',
|
|
||||||
uv: 2000,
|
|
||||||
pv: 9800,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Page D',
|
|
||||||
uv: 2780,
|
|
||||||
pv: 3908,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Page E',
|
|
||||||
uv: 1890,
|
|
||||||
pv: 4800,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Page F',
|
|
||||||
uv: 2390,
|
|
||||||
pv: 3800,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Page G',
|
|
||||||
uv: 3490,
|
|
||||||
pv: 4300,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
interface IProps extends CategoricalChartProps {
|
interface IProps extends CategoricalChartProps {
|
||||||
data?: Array<{ xAxis: string; yAxis: number }>;
|
data?: Array<{ xAxis: string; yAxis: number }>;
|
||||||
showLegend?: boolean;
|
showLegend?: boolean;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { LanguageTranslationMap } from '@/constants/common';
|
import { LanguageTranslationMap } from '@/constants/common';
|
||||||
import { ResponseGetType } from '@/interfaces/database/base';
|
import { ResponseGetType } from '@/interfaces/database/base';
|
||||||
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
||||||
import { ISystemStatus, IUserInfo } from '@/interfaces/database/userSetting';
|
import { ISystemStatus, IUserInfo } from '@/interfaces/database/user-setting';
|
||||||
import userService from '@/services/user-service';
|
import userService from '@/services/user-service';
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
|
@ -20,11 +20,17 @@ export interface IUserInfo {
|
|||||||
update_time: number;
|
update_time: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TaskExecutorElapsed = Record<string, number[]>;
|
||||||
|
|
||||||
export interface ISystemStatus {
|
export interface ISystemStatus {
|
||||||
es: Es;
|
es: Es;
|
||||||
minio: Minio;
|
minio: Minio;
|
||||||
mysql: Minio;
|
mysql: Minio;
|
||||||
redis: Redis;
|
redis: Redis;
|
||||||
|
task_executor: {
|
||||||
|
status: string;
|
||||||
|
elapsed: TaskExecutorElapsed;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Redis {
|
interface Redis {
|
@ -18,3 +18,16 @@
|
|||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.taskBarTooltip {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskBar {
|
||||||
|
width: '100%';
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskBarTitle {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import SvgIcon from '@/components/svg-icon';
|
import SvgIcon from '@/components/svg-icon';
|
||||||
import { useFetchSystemStatus } from '@/hooks/user-setting-hooks';
|
import { useFetchSystemStatus } from '@/hooks/user-setting-hooks';
|
||||||
import { ISystemStatus, Minio } from '@/interfaces/database/userSetting';
|
import {
|
||||||
|
ISystemStatus,
|
||||||
|
TaskExecutorElapsed,
|
||||||
|
} from '@/interfaces/database/user-setting';
|
||||||
import { Badge, Card, Flex, Spin, Typography } from 'antd';
|
import { Badge, Card, Flex, Spin, Typography } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import lowerCase from 'lodash/lowerCase';
|
import lowerCase from 'lodash/lowerCase';
|
||||||
@ -9,6 +12,7 @@ import { useEffect } from 'react';
|
|||||||
|
|
||||||
import { toFixed } from '@/utils/common-util';
|
import { toFixed } from '@/utils/common-util';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
import TaskBarChat from './task-bar-chat';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
@ -23,6 +27,7 @@ const TitleMap = {
|
|||||||
minio: 'MinIO Object Storage',
|
minio: 'MinIO Object Storage',
|
||||||
redis: 'Redis',
|
redis: 'Redis',
|
||||||
mysql: 'Mysql',
|
mysql: 'Mysql',
|
||||||
|
task_executor: 'Task Executor',
|
||||||
};
|
};
|
||||||
|
|
||||||
const SystemInfo = () => {
|
const SystemInfo = () => {
|
||||||
@ -48,7 +53,11 @@ const SystemInfo = () => {
|
|||||||
type="inner"
|
type="inner"
|
||||||
title={
|
title={
|
||||||
<Flex align="center" gap={10}>
|
<Flex align="center" gap={10}>
|
||||||
<SvgIcon name={key} width={26}></SvgIcon>
|
{key === 'task_executor' ? (
|
||||||
|
<img src="/logo.svg" alt="" width={26} />
|
||||||
|
) : (
|
||||||
|
<SvgIcon name={key} width={26}></SvgIcon>
|
||||||
|
)}
|
||||||
<span className={styles.title}>
|
<span className={styles.title}>
|
||||||
{TitleMap[key as keyof typeof TitleMap]}
|
{TitleMap[key as keyof typeof TitleMap]}
|
||||||
</span>
|
</span>
|
||||||
@ -60,28 +69,34 @@ const SystemInfo = () => {
|
|||||||
}
|
}
|
||||||
key={key}
|
key={key}
|
||||||
>
|
>
|
||||||
{Object.keys(info)
|
{key === 'task_executor' ? (
|
||||||
.filter((x) => x !== 'status')
|
<TaskBarChat
|
||||||
.map((x) => {
|
data={info.elapsed as TaskExecutorElapsed}
|
||||||
return (
|
></TaskBarChat>
|
||||||
<Flex
|
) : (
|
||||||
key={x}
|
Object.keys(info)
|
||||||
align="center"
|
.filter((x) => x !== 'status')
|
||||||
gap={16}
|
.map((x) => {
|
||||||
className={styles.text}
|
return (
|
||||||
>
|
<Flex
|
||||||
<b>{upperFirst(lowerCase(x))}:</b>
|
key={x}
|
||||||
<Text
|
align="center"
|
||||||
className={classNames({
|
gap={16}
|
||||||
[styles.error]: x === 'error',
|
className={styles.text}
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
{toFixed(info[x as keyof Minio]) as any}
|
<b>{upperFirst(lowerCase(x))}:</b>
|
||||||
{x === 'elapsed' && ' ms'}
|
<Text
|
||||||
</Text>
|
className={classNames({
|
||||||
</Flex>
|
[styles.error]: x === 'error',
|
||||||
);
|
})}
|
||||||
})}
|
>
|
||||||
|
{toFixed((info as Record<string, any>)[x]) as any}
|
||||||
|
{x === 'elapsed' && ' ms'}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
91
web/src/pages/user-setting/setting-system/task-bar-chat.tsx
Normal file
91
web/src/pages/user-setting/setting-system/task-bar-chat.tsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { TaskExecutorElapsed } from '@/interfaces/database/user-setting';
|
||||||
|
import { Divider, Flex } from 'antd';
|
||||||
|
import { max } from 'lodash';
|
||||||
|
import {
|
||||||
|
Bar,
|
||||||
|
BarChart,
|
||||||
|
CartesianGrid,
|
||||||
|
ResponsiveContainer,
|
||||||
|
Tooltip,
|
||||||
|
} from 'recharts';
|
||||||
|
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
data: TaskExecutorElapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getColor = (value: number) => {
|
||||||
|
if (value > 120) {
|
||||||
|
return 'red';
|
||||||
|
} else if (value <= 120 && value > 50) {
|
||||||
|
return '#faad14';
|
||||||
|
}
|
||||||
|
return '#52c41a';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMaxLength = (data: TaskExecutorElapsed) => {
|
||||||
|
const lengths = Object.keys(data).reduce<number[]>((pre, cur) => {
|
||||||
|
pre.push(data[cur].length);
|
||||||
|
return pre;
|
||||||
|
}, []);
|
||||||
|
return max(lengths) ?? 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fillEmptyElementByMaxLength = (list: any[], maxLength: number) => {
|
||||||
|
if (list.length === maxLength) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
return list.concat(
|
||||||
|
new Array(maxLength - list.length).fill({
|
||||||
|
value: 0,
|
||||||
|
actualValue: 0,
|
||||||
|
fill: getColor(0),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CustomTooltip = ({ active, payload }: any) => {
|
||||||
|
if (active && payload && payload.length) {
|
||||||
|
return (
|
||||||
|
<div className="custom-tooltip">
|
||||||
|
<p
|
||||||
|
className={styles.taskBarTooltip}
|
||||||
|
>{`${payload[0].payload.actualValue}`}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TaskBarChat = ({ data }: IProps) => {
|
||||||
|
const maxLength = getMaxLength(data);
|
||||||
|
return (
|
||||||
|
<Flex gap="middle" vertical>
|
||||||
|
{Object.keys(data).map((key) => {
|
||||||
|
const list = data[key].map((x) => ({
|
||||||
|
value: x > 120 ? 120 : x,
|
||||||
|
actualValue: x,
|
||||||
|
fill: getColor(x),
|
||||||
|
}));
|
||||||
|
const nextList = fillEmptyElementByMaxLength(list, maxLength);
|
||||||
|
return (
|
||||||
|
<Flex key={key} className={styles.taskBar} vertical>
|
||||||
|
<b className={styles.taskBarTitle}>ID: {key}</b>
|
||||||
|
<ResponsiveContainer>
|
||||||
|
<BarChart data={nextList} barSize={20}>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
|
<Tooltip content={<CustomTooltip></CustomTooltip>} />
|
||||||
|
<Bar dataKey="value" />
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
<Divider></Divider>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskBarChat;
|
Loading…
x
Reference in New Issue
Block a user