Merge pull request #2 from seal-io/dev

Dev
This commit is contained in:
jialin
2024-06-26 19:03:04 +08:00
committed by GitHub
43 changed files with 1040 additions and 432 deletions
+1
View File
@@ -65,6 +65,7 @@ export default defineConfig({
} }
: {}), : {}),
// esbuildMinifyIIFE: true, // esbuildMinifyIIFE: true,
favicons: ['/static/favicon.ico'],
jsMinifier: 'terser', jsMinifier: 'terser',
cssMinifier: 'cssnano', cssMinifier: 'cssnano',
presets: ['umi-presets-pro'], presets: ['umi-presets-pro'],

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+6
View File
@@ -0,0 +1,6 @@
.cardWrapper {
border-radius: var(--border-radius-middle);
background-color: var(--color-white-1);
box-shadow: var(--box-shadow-base);
padding: 10px;
}
+12
View File
@@ -0,0 +1,12 @@
import styles from './index.less';
const CardWrapper = (props: any) => {
const { children, style } = props;
return (
<div className={styles.cardWrapper} style={{ ...style }}>
{children}
</div>
);
};
export default CardWrapper;
+56 -15
View File
@@ -6,34 +6,69 @@ interface BarChartProps {
yField: string; yField: string;
title?: string; title?: string;
height?: number; height?: number;
group?: boolean;
colorField?: string;
seriesField?: string;
stack?: boolean;
legend?: any;
} }
const BarChart: React.FC<BarChartProps> = (props) => { const BarChart: React.FC<BarChartProps> = (props) => {
const { data, xField, yField, title, height = 260 } = props; const {
data,
xField,
yField,
title,
height = 260,
group,
colorField,
seriesField,
stack,
legend = undefined
} = props;
const config = { const config = {
data, data,
xField, xField,
yField, yField,
// colorField: 'name', colorField: colorField || 'name',
direction: 'vertical', direction: 'vertical',
stack,
seriesField,
height, height,
group: true, group,
legend: { scale: {
color: { x: {
position: 'top', type: 'band',
layout: { padding: 0.5
justifyContent: 'center'
}
} }
}, },
legend:
legend === 'undefined'
? {
color: {
position: 'top',
layout: {
justifyContent: 'center'
}
}
}
: legend,
axis: { axis: {
x: { x: {
xAxis: true xAxis: true,
tick: false
},
y: {
tick: false,
labelAutoWrap: true
} }
}, },
title: { title: {
title, title,
style: { style: {
align: 'center' align: 'center',
titleFontSize: 14,
titleFill: 'rgba(0,0,0,0.88)',
titleFontWeight: 500
} }
}, },
split: { split: {
@@ -48,10 +83,16 @@ const BarChart: React.FC<BarChartProps> = (props) => {
}, },
style: { style: {
fill: '#54cc98', fill: (params: any) => {
radiusTopLeft: 8, return (
radiusTopRight: 8, params.color ||
width: 30 'linear-gradient(90deg,rgba(84, 204, 152,0.8) 0%,rgb(0, 168, 143,.7) 100%)'
);
},
radiusTopLeft: 12,
radiusTopRight: 12,
align: 'center',
width: 20
} }
}; };
+17 -2
View File
@@ -16,6 +16,19 @@ const GaugeChart: React.FC<GaugeChartProps> = (props) => {
const config = { const config = {
width, width,
height, height,
box: {
padding: [0, 0, 0, 0],
style: {
padding: [0, 0, 0, 0]
}
},
guide: {
arc: {
style: {
lineWidth: 30
}
}
},
autoFit: true, autoFit: true,
data: { data: {
target, target,
@@ -34,9 +47,11 @@ const GaugeChart: React.FC<GaugeChartProps> = (props) => {
title: { title: {
title, title,
size: 0, size: 0,
titleFontSize: 14,
style: { style: {
align: 'center' align: 'center',
titleFontSize: 14,
titleFill: 'rgba(0,0,0,0.88)',
titleFontWeight: 500
} }
}, },
style: { style: {
+55 -18
View File
@@ -6,52 +6,89 @@ interface BarChartProps {
yField: string; yField: string;
title?: string; title?: string;
height?: number; height?: number;
group?: boolean;
colorField?: string;
seriesField?: string;
stack?: boolean;
legend?: any;
} }
const BarChart: React.FC<BarChartProps> = (props) => { const BarChart: React.FC<BarChartProps> = (props) => {
const { data, xField, yField, title, height = 300 } = props; const {
data,
xField,
yField,
title,
height,
group,
colorField,
seriesField,
stack,
legend = undefined
} = props;
const config = { const config = {
data, data,
xField, xField,
yField, yField,
// colorField: 'name', colorField: colorField || 'name',
direction: 'vertical', direction: 'vertical',
seriesField,
height, height,
group: true, group,
legend: { stack,
color: { legend:
position: 'top', legend === 'undefined'
layout: { ? {
justifyContent: 'center' color: {
} position: 'top',
layout: {
justifyContent: 'center'
}
}
}
: legend,
scale: {
x: {
type: 'band',
padding: 0.5
} }
}, },
axis: { axis: {
x: { x: {
xAxis: true xAxis: true,
tick: false
},
y: {
tick: false
} }
}, },
title: { title: {
title, title,
style: { style: {
align: 'center' align: 'center',
titleFontSize: 14,
titleFill: 'rgba(0,0,0,0.88)',
titleFontWeight: 500
} }
}, },
split: { split: {
type: 'line', type: 'line',
line: { line: {
color: 'red',
style: { style: {
color: 'red',
lineDash: [4, 5] lineDash: [4, 5]
} }
} }
}, },
markBackground: {},
style: { style: {
fill: '#54cc98', fill: (params: any) => {
radiusTopLeft: 8, return (
radiusTopRight: 8, params.color ||
height: 30 'linear-gradient(90deg,rgba(84, 204, 152,0.8) 0%,rgb(0, 168, 143,.7) 100%)'
);
},
radiusTopLeft: 12,
radiusTopRight: 12,
height: 20
} }
}; };
+23 -11
View File
@@ -7,16 +7,18 @@ interface LineChartProps {
color?: string[]; color?: string[];
xField?: string; xField?: string;
yField?: string; yField?: string;
slider?: boolean; labelFormatter?: (v: any) => string;
slider?: any;
} }
const LineChart: React.FC<LineChartProps> = (props) => { const LineChart: React.FC<LineChartProps> = (props) => {
const { data, title, color, xField, yField, slider, height } = props; const { data, title, color, xField, yField, slider, height, labelFormatter } =
props;
const config = { const config = {
title, title,
height, height,
xField: xField || 'time', xField: xField || 'time',
yField: yField || 'value', yField: yField || 'value',
color: color || ['red', 'blue', 'green'], // color: color || ['red', 'blue', 'green', 'yellow'],
colorField: 'type', colorField: 'type',
autoFit: true, autoFit: true,
slider, slider,
@@ -26,21 +28,31 @@ const LineChart: React.FC<LineChartProps> = (props) => {
textStyle: { textStyle: {
autoRoate: true autoRoate: true
} }
},
y: {
tick: false,
// size: 14,
// title: '%',
titlePosition: 'top',
titleFontSize: 12,
labelFormatter
} }
}, },
point: { // point: {
shapeField: 'circle', // shapeField: 'circle',
sizeField: 2 // sizeField: 2
}, // },
style: { style: {
lineWidth: 1.5 lineWidth: 1.5,
opacity: 0.8
}, },
legend: { legend: {
itemMarker: {
symbol: 'circle'
},
color: { color: {
layout: { justifyContent: 'center' } layout: { justifyContent: 'center' }
},
size: {
itemLabelFontSize: 14,
itemLabelFontWeight: 500
} }
}, },
tooltip: { tooltip: {
+79
View File
@@ -0,0 +1,79 @@
import { Liquid } from '@ant-design/plots';
import { useEffect, useState } from 'react';
interface LiquidChartProps {
percent: number;
color?: string;
title?: string;
height?: number;
width?: number;
thresholds?: number[];
rangColor?: string[];
}
const LiquidChart: React.FC<LiquidChartProps> = (props) => {
const {
percent = 0,
color,
title,
width,
height,
thresholds = [],
rangColor = []
} = props;
const primaryColor = 'rgba(84, 204, 152,0.8)';
const [fillColor, setFillColor] = useState<string>(primaryColor);
const calcColorByPercent = () => {
if (color) {
return color;
}
if (!rangColor.length) {
return primaryColor;
}
if (rangColor.length && thresholds.length) {
let index = 0;
for (let i = 0; i < thresholds.length; i++) {
if (percent <= thresholds[i]) {
index = i;
break;
}
}
return rangColor[index];
}
return primaryColor;
};
useEffect(() => {
const fc = calcColorByPercent();
setFillColor(fc);
}, [percent]);
const config = {
percent,
textAlign: 'center',
autoFit: true,
title: {
title,
size: 0,
style: {
align: 'center',
titleFontSize: 12,
titleFill: 'rgba(0,0,0,0.88)',
titleFontWeight: 500
}
},
style: {
textAlign: 'center',
outlineBorder: 2,
waveLength: 128,
stroke: fillColor,
fill: fillColor
}
};
return <Liquid {...config} />;
};
export default LiquidChart;
+39
View File
@@ -0,0 +1,39 @@
.g-polygon {
position: absolute;
opacity: 0.5;
}
.g-polygon-1 {
// 定位代码,容器高宽随意
background: #fe5;
clip-path: polygon(0 10%, 30% 0, 100% 40%, 70% 100%, 20% 90%);
width: 50%;
height: 50%;
}
.g-polygon-2 {
// 定位代码,容器高宽随意
background: #e950d1;
clip-path: polygon(10% 0, 100% 70%, 100% 100%, 20% 90%);
width: 50%;
height: 50%;
}
.g-polygon-3 {
// 定位代码,容器高宽随意
background: rgba(87, 80, 233);
clip-path: polygon(80% 0, 100% 70%, 100% 100%, 20% 90%);
width: 50%;
height: 50%;
}
.g-bg::before {
content: '';
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
backdrop-filter: blur(150px);
z-index: 1;
}
+13
View File
@@ -0,0 +1,13 @@
import './index.less';
const GlassBg = () => {
return (
<div className="g-bg">
<div className="g-polygon g-polygon-1"></div>
<div className="g-polygon g-polygon-2"></div>
<div className="g-polygon g-polygon-3"></div>
</div>
);
};
export default GlassBg;
+14 -2
View File
@@ -2,57 +2,69 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
.header-row-wrapper { .header-row-wrapper {
height: 40px; height: 40px;
margin-bottom: 10px; margin-bottom: 10px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
.header-row-prefix-wrapper { .header-row-prefix-wrapper {
padding-left: var(--ant-table-cell-padding-inline); padding-left: var(--ant-table-cell-padding-inline);
} }
.row { .row {
flex: 1; flex: 1;
} }
} }
.row-box { .row-box {
margin-bottom: 20px; margin-bottom: 20px;
border-radius: var(--ant-table-header-border-radius); border-radius: var(--ant-table-header-border-radius);
overflow: hidden; overflow: hidden;
} }
.expanded-row { .expanded-row {
background-color: var(--color-white-1); background-color: var(--color-white-1);
padding: 16px 16px; padding: 16px;
border: 1px solid var(--color-fill-1); border: 1px solid var(--color-fill-1);
border-top: 0; border-top: 0;
border-radius: 0 0 var(--ant-table-header-border-radius) border-radius: 0 0 var(--ant-table-header-border-radius)
var(--ant-table-header-border-radius); var(--ant-table-header-border-radius);
} }
.row-wrapper { .row-wrapper {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
background-color: var(--color-fill-1); background-color: var(--color-white-1);
transition: all 0.2s ease; transition: all 0.2s ease;
&:hover { &:hover {
background-color: var(--ant-table-row-hover-bg); background-color: var(--ant-table-row-hover-bg);
transition: all 0.2s ease; transition: all 0.2s ease;
} }
&-selected { &-selected {
background-color: var(--ant-table-row-selected-bg); background-color: var(--ant-table-row-selected-bg);
transition: all 0.2s ease; transition: all 0.2s ease;
&:hover { &:hover {
background-color: var(--ant-table-row-selected-hover-bg); background-color: var(--ant-table-row-selected-hover-bg);
transition: all 0.2s ease; transition: all 0.2s ease;
} }
} }
.row-prefix-wrapper { .row-prefix-wrapper {
padding-left: var(--ant-table-cell-padding-inline); padding-left: var(--ant-table-cell-padding-inline);
} }
} }
.seal-table-row { .seal-table-row {
flex: 1; flex: 1;
} }
.spin { .spin {
text-align: center; text-align: center;
display: flex; display: flex;
+8
View File
@@ -0,0 +1,8 @@
export default {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200,
xxl: 1600
};
+90 -28
View File
@@ -4,13 +4,14 @@
html { html {
--ant-color-fill-tertiary: rgba(0, 0, 0, 4%); --ant-color-fill-tertiary: rgba(0, 0, 0, 4%);
--color-fill-1: var(--ant-color-fill-tertiary); --color-fill-1: var(--ant-color-fill-tertiary);
// --color-fill-1: #f3f6fa; // --color-fill-1: #fff;
--color-fill-2: #fff; --color-fill-2: #fff;
--color-fill-3: #f3f6fa; --color-fill-3: #f3f6fa;
--menu-border-radius-base: 32px; --menu-border-radius-base: 20px;
--border-radius-base: 16px; --border-radius-base: 16px;
--border-radius-middle: 20px;
--border-radius-small: 8px; --border-radius-small: 8px;
--color-white-1: #fff; --color-white-1: rgba(255, 255, 255, 100%);
--font-weight-normal: 500; --font-weight-normal: 500;
--font-weight-bold: 700; --font-weight-bold: 700;
--color-text-1: var(--ant-color-text); --color-text-1: var(--ant-color-text);
@@ -19,7 +20,7 @@ html {
--font-size-large: 16px; --font-size-large: 16px;
--font-size-middle: 14px; --font-size-middle: 14px;
--ant-color-fill-secondary: rgba(0, 0, 0, 6%); --ant-color-fill-secondary: rgba(0, 0, 0, 6%);
--table-td-radius: 24px; --table-td-radius: 20px;
--checkbox-border-radius: 4px; --checkbox-border-radius: 4px;
--ant-table-cell-padding-inline: 16px; --ant-table-cell-padding-inline: 16px;
--ant-table-cell-padding-block: 16px; --ant-table-cell-padding-block: 16px;
@@ -27,7 +28,7 @@ html {
--ant-table-header-split-color: #f0f0f0; --ant-table-header-split-color: #f0f0f0;
--ant-table-row-selected-bg: #f0fff6; --ant-table-row-selected-bg: #f0fff6;
--ant-table-row-selected-hover-bg: #d8f2e4; --ant-table-row-selected-hover-bg: #d8f2e4;
--ant-table-row-hover-bg: #e6e6e6; --ant-table-row-hover-bg: rgba(230, 230, 230, 70%);
--color-chart-red: #ff7875; --color-chart-red: #ff7875;
--color-chart-green: #54cc98; --color-chart-green: #54cc98;
--color-chart-glod: #ffd666; --color-chart-glod: #ffd666;
@@ -36,6 +37,7 @@ html {
--ant-input-active-shadow: 0 0 0 2px rgba(5, 255, 105, 6%); --ant-input-active-shadow: 0 0 0 2px rgba(5, 255, 105, 6%);
--ant-input-active-border-color: #2fbf85; --ant-input-active-border-color: #2fbf85;
--ant-input-hover-border-color: #54cc98; --ant-input-hover-border-color: #54cc98;
--box-shadow-base: 0 4px 12px rgb(227, 232, 240);
.css-var-rf { .css-var-rf {
--ant-font-size: var(--font-size-base); --ant-font-size: var(--font-size-base);
@@ -49,7 +51,7 @@ html {
&.ant-menu-css-var { &.ant-menu-css-var {
--ant-menu-item-height: 46px; --ant-menu-item-height: 46px;
--ant-menu-item-selected-bg: var(--color-white-1); --ant-menu-item-selected-bg: var(--color-white-1);
--ant-menu-item-border-radius: 24px; --ant-menu-item-border-radius: 16px;
--ant-menu-item-selected-color: var(--ant-color-primary); --ant-menu-item-selected-color: var(--ant-color-primary);
--ant-menu-item-hover-bg: var(--color-white-1); --ant-menu-item-hover-bg: var(--color-white-1);
--ant-menu-item-color: var(--color-text-1); --ant-menu-item-color: var(--color-text-1);
@@ -67,6 +69,7 @@ html {
--ant-border-radius-lg: 16px; --ant-border-radius-lg: 16px;
--ant-color-error: #ff4d4f; --ant-color-error: #ff4d4f;
--ant-color-bg-mask: rgba(0, 0, 0, 35%); --ant-color-bg-mask: rgba(0, 0, 0, 35%);
--ant-color-border: rgb(217 217 217 / 50%);
&.ant-popover { &.ant-popover {
--ant-popover-inner-padding: 26px; --ant-popover-inner-padding: 26px;
@@ -95,7 +98,7 @@ html {
.css-var-rh.ant-menu-css-var { .css-var-rh.ant-menu-css-var {
--ant-menu-item-height: 46px; --ant-menu-item-height: 46px;
--ant-menu-item-selected-bg: var(--color-white-1); --ant-menu-item-selected-bg: var(--color-white-1);
--ant-menu-item-border-radius: 24px; --ant-menu-item-border-radius: 20px;
--ant-menu-item-selected-color: var(--ant-color-primary); --ant-menu-item-selected-color: var(--ant-color-primary);
--ant-menu-item-hover-bg: var(--color-white-1); --ant-menu-item-hover-bg: var(--color-white-1);
--ant-menu-item-color: var(--color-text-1); --ant-menu-item-color: var(--color-text-1);
@@ -161,7 +164,7 @@ body {
} }
tr > td { tr > td {
background-color: var(--color-fill-1); // background-color: var(--color-fill-1);
border-bottom: none; border-bottom: none;
height: 70px; height: 70px;
@@ -222,12 +225,43 @@ body {
font-size: 16px; font-size: 16px;
} }
} }
// ============== new theme style start ===============
.ant-pro-layout {
background-color: var(--color-fill-1);
height: 100%;
.ant-pro-sider .ant-layout-sider-children {
background-color: var(--color-white-1);
box-shadow: var(--box-shadow-base);
}
}
.ant-table-wrapper .ant-table {
background-color: unset;
}
.ant-table-content table {
.ant-table-thead > tr > th {
background-color: unset;
}
.ant-table-tbody {
.ant-table-row {
border-radius: var(--table-td-radius);
background-color: var(--color-white-1);
box-shadow: var(--box-shadow-base);
> td {
background-color: unset;
}
}
}
}
// ============== new theme style end =================
} }
// ======== basic layout style start============
// ======== basic layout style end ============
// ======== menu style start ============ // ======== menu style start ============
.ant-pro-sider-collapsed-button { .ant-pro-sider-collapsed-button {
display: none; display: none;
@@ -250,11 +284,19 @@ body {
} }
.ant-pro-layout-container { .ant-pro-layout-container {
background-color: var(--color-fill-2); // background-color: var(--color-fill-2);
} }
.ant-pro-sider { .ant-pro-sider {
.ant-menu { .ant-menu {
.ant-menu-item {
border-radius: 16px;
&:active {
background-color: var(--ant-menu-item-selected-bg);
}
}
.ant-menu-item:not(.ant-menu-item-selected) { .ant-menu-item:not(.ant-menu-item-selected) {
color: var(--ant-color-text); color: var(--ant-color-text);
} }
@@ -266,6 +308,11 @@ body {
.ant-menu-item.ant-menu-item-selected:hover { .ant-menu-item.ant-menu-item-selected:hover {
color: var(--ant-color-primary); color: var(--ant-color-primary);
} }
.ant-menu-item.ant-menu-item-selected {
color: var(--ant-color-primary);
background-color: unset;
}
} }
} }
} }
@@ -275,20 +322,35 @@ body {
.monaco-editor { .monaco-editor {
border-radius: 16px; border-radius: 16px;
} }
// .background {
// position: fixed; .background {
// top: 0; position: fixed;
// left: 0; top: 0;
// bottom: 0; left: 0;
// right: 0; bottom: 0;
// background: radial-gradient( right: 0;
// circle, // background: radial-gradient(
// rgba(47, 191, 133, 0.02), // circle,
// rgba(55, 125, 184, 0.2) // rgba(47, 191, 133, 0.02),
// ); // rgba(55, 125, 184, 0.2)
// filter: blur(100px); // );
// height: 100%; background: url('./assets/images/bg.png') center center no-repeat;
// } background-size: cover;
&::after {
content: '';
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1;
backdrop-filter: blur(100px);
filter: blur(100px);
height: 100%;
}
}
.ant-pro-page-container { .ant-pro-page-container {
background: transparent; background: transparent;
} }
@@ -308,7 +370,7 @@ body {
background: linear-gradient( background: linear-gradient(
90deg, 90deg,
rgba(255, 255, 255, 4%) 25%, rgba(255, 255, 255, 4%) 25%,
rgba(0, 0, 0, 10%) 37%, rgba(0, 0, 0, 8%) 37%,
rgba(255, 255, 255, 4%) 63% rgba(255, 255, 255, 4%) 63%
); );
background-size: 400% 100%; background-size: 400% 100%;
+60
View File
@@ -0,0 +1,60 @@
import breakpoints from '@/config/breakpoints';
import { useEffect, useState } from 'react';
export default function useWindowResize() {
const [size, setSize] = useState<{ width: number; height: number }>({
width: window.innerWidth,
height: window.innerHeight
});
const [isMobile, setIsMobile] = useState<boolean>(false);
const [isTablet, setIsTablet] = useState<boolean>(false);
const [isDesktop, setIsDesktop] = useState<boolean>(false);
const [currentPoint, setCurrentPoint] = useState<string>('');
const checkBreakpoint = (width: number) => {
if (width < breakpoints.sm) {
setIsMobile(true);
setIsTablet(false);
setIsDesktop(false);
setCurrentPoint('sm');
return;
}
if (width < breakpoints.md) {
setIsMobile(false);
setIsTablet(true);
setIsDesktop(false);
setCurrentPoint('md');
return;
}
if (width < breakpoints.lg) {
setIsMobile(false);
setIsTablet(false);
setIsDesktop(true);
setCurrentPoint('lg');
return;
}
setIsMobile(false);
setIsTablet(false);
setIsDesktop(true);
setCurrentPoint('xl');
};
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
useEffect(() => {
checkBreakpoint(size.width);
}, [size.width]);
return { size, isMobile, isTablet, isDesktop, currentPoint };
}
+1 -1
View File
@@ -144,7 +144,7 @@ export default (props: any) => {
location={location} location={location}
title={userConfig.title} title={userConfig.title}
navTheme="light" navTheme="light"
siderWidth={270} siderWidth={220}
onMenuHeaderClick={(e) => { onMenuHeaderClick={(e) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
+7
View File
@@ -0,0 +1,7 @@
import { request } from '@umijs/max';
export const DASHBOARD_API = '/dashboard';
export async function queryDashboardData() {
return request(DASHBOARD_API);
}
+28 -76
View File
@@ -1,6 +1,9 @@
import ContentWrapper from '@/components/content-wrapper';
import PageTools from '@/components/page-tools'; import PageTools from '@/components/page-tools';
import ProgressBar from '@/components/progress-bar';
import { Col, Row, Table } from 'antd'; import { Col, Row, Table } from 'antd';
import _ from 'lodash';
import { useContext } from 'react';
import { DashboardContext } from '../config/dashboard-context';
const modelColumns = [ const modelColumns = [
{ {
@@ -9,28 +12,30 @@ const modelColumns = [
key: 'name' key: 'name'
}, },
{ {
title: 'Allocated GPUs', title: 'GPU Utilization',
dataIndex: 'allocated', dataIndex: 'gpu_utilization',
key: 'allocated', key: 'gpu_utilization',
render: (text: any, record: any) => <span>{record.gpu.allocated}</span> render: (text: any, record: any) => (
<ProgressBar percent={_.round(text, 2)}></ProgressBar>
)
}, },
{ {
title: 'GPU Utilization', title: 'VRAM Utilization',
dataIndex: 'utilization', dataIndex: 'gpu_memory_utilization',
key: 'utilization', key: 'gpu_memory_utilization',
render: (text: any, record: any) => <span>{record.gpu.utilization}</span> render: (text: any, record: any) => (
<ProgressBar percent={_.round(text, 2)}></ProgressBar>
)
}, },
{ {
title: 'Running Instances', title: 'Running Instances',
dataIndex: 'running', dataIndex: 'instance_count',
key: 'running', key: 'instance_count'
render: (text: any, record: any) => <span>{record.instances.running}</span>
}, },
{ {
title: 'Pending Instances', title: 'Tokens',
dataIndex: 'pending', dataIndex: 'token_count',
key: 'pending', key: 'token_count'
render: (text: any, record: any) => <span>{record.instances.pending}</span>
} }
]; ];
@@ -59,39 +64,6 @@ const projectColumns = [
} }
]; ];
const modelData = [
{
id: 1,
name: 'qwen2',
gpu: { allocated: 4, utilization: '50%' },
instances: { running: 1, pending: 0 }
},
{
id: 2,
name: 'llama3:70b',
gpu: { allocated: 3, utilization: '70%' },
instances: { running: 1, pending: 0 }
},
{
id: 3,
name: 'llama3',
gpu: { allocated: 5, utilization: '20%' },
instances: { running: 1, pending: 0 }
},
{
id: 4,
name: 'gemma',
gpu: { allocated: 1, utilization: '25%' },
instances: { running: 1, pending: 0 }
},
{
id: 5,
name: 'phi3',
gpu: { allocated: 2, utilization: '46%' },
instances: { running: 1, pending: 0 }
}
];
const projectData = [ const projectData = [
{ {
id: 1, id: 1,
@@ -130,11 +102,12 @@ const projectData = [
} }
]; ];
const ActiveTable = () => { const ActiveTable = () => {
const data = useContext(DashboardContext).active_models || [];
return ( return (
<Row> <Row gutter={[20, 0]}>
<Col span={12}> <Col xs={24} sm={24} md={24} lg={24} xl={24}>
<PageTools <PageTools
style={{ margin: '32px 40px' }} style={{ margin: '32px 8px' }}
left={ left={
<span <span
style={{ fontSize: 'var(--font-size-large)', padding: '9px 0' }} style={{ fontSize: 'var(--font-size-large)', padding: '9px 0' }}
@@ -144,35 +117,14 @@ const ActiveTable = () => {
} }
right={false} right={false}
/> />
<ContentWrapper contentStyle={{ paddingRight: 0 }} title={false}> <div>
<Table <Table
columns={modelColumns} columns={modelColumns}
dataSource={modelData} dataSource={data}
pagination={false} pagination={false}
rowKey="id" rowKey="id"
/> />
</ContentWrapper> </div>
</Col>
<Col span={12}>
<PageTools
style={{ margin: '32px 40px' }}
left={
<span
style={{ fontSize: 'var(--font-size-large)', padding: '9px 0' }}
>
Active Projects
</span>
}
right={false}
/>
<ContentWrapper title={false}>
<Table
columns={projectColumns}
dataSource={projectData}
pagination={false}
rowKey="id"
/>
</ContentWrapper>
</Col> </Col>
</Row> </Row>
); );
@@ -3,6 +3,8 @@
height: 110px; height: 110px;
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
box-shadow: var(--box-shadow-base);
border-radius: var(--ant-border-radius-lg);
} }
} }
+16 -23
View File
@@ -1,7 +1,8 @@
import ContentWrapper from '@/components/content-wrapper';
import { Card, Col, Row, Space } from 'antd'; import { Card, Col, Row, Space } from 'antd';
import React from 'react'; import _ from 'lodash';
import React, { useContext } from 'react';
import { overviewConfigs } from '../config'; import { overviewConfigs } from '../config';
import { DashboardContext } from '../config/dashboard-context';
import '../styles/index.less'; import '../styles/index.less';
import styles from './over-view.less'; import styles from './over-view.less';
@@ -24,23 +25,8 @@ const renderCardItem = (data: {
</Card> </Card>
); );
}; };
const Overview: React.FC = (props) => { const Overview: React.FC = () => {
// const { data = {} } = props; const data = useContext(DashboardContext).resource_counts || {};
const data = {
workers: 8,
models: {
healthy: 10,
warning: 2,
error: 1
},
gpus: 30,
allocatedGpus: 12,
instances: {
healthy: 32,
warning: 3,
error: 2
}
};
const renderValue = ( const renderValue = (
value: value:
@@ -63,19 +49,26 @@ const Overview: React.FC = (props) => {
); );
}; };
return ( return (
<ContentWrapper contentStyle={{ paddingBlockStart: '32px' }} title={false}> <div>
<Row gutter={[24, 20]} className={styles.row}> <Row gutter={[24, 20]} className={styles.row}>
{overviewConfigs.map((config, index) => ( {overviewConfigs.map((config, index) => (
<Col span={5} key={config.key}> <Col
xs={{ flex: '100%' }}
sm={{ flex: '50%' }}
md={{ flex: '30%' }}
lg={{ flex: '20%' }}
xl={{ flex: '20%' }}
key={config.key}
>
{renderCardItem({ {renderCardItem({
label: config.label, label: config.label,
value: renderValue(data[config.key] || 0), value: renderValue(_.get(data, config.key, 0)),
bgColor: config.backgroundColor bgColor: config.backgroundColor
})} })}
</Col> </Col>
))} ))}
</Row> </Row>
</ContentWrapper> </div>
); );
}; };
@@ -1,68 +1,68 @@
import LineChart from '@/components/charts/line-chart'; import LineChart from '@/components/charts/line-chart';
import { generateFluctuatingData } from '@/utils'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
import { useContext, useEffect, useState } from 'react';
import { DashboardContext } from '../config/dashboard-context';
const mockData = { const TypeKeyMap = {
GPU: generateFluctuatingData({ cpu: 'CPU',
total: 17, memory: 'Memory',
max: 80, gpu: 'GPU',
min: 20 gpu_memory: 'VRAM'
}),
CPU: generateFluctuatingData({
total: 17,
max: 70,
min: 10
}),
Memory: generateFluctuatingData({
total: 17,
max: 60,
min: 20
}),
VRAM: generateFluctuatingData({
total: 17,
max: 90,
min: 15
})
}; };
const UtilizationOvertime: React.FC = () => { const UtilizationOvertime: React.FC = () => {
const timeList = [ const data = useContext(DashboardContext)?.system_load?.history || {};
'08:00:00', const [result, setResult] = useState<
'09:00:00', { time: string; value: number; type: string }[]
'10:00:00', >([]);
'11:00:00',
'12:00:00', const typeList = ['gpu', 'cpu', 'memory', 'gpu_memory'];
'13:00:00', const sliderConfig = {
'14:00:00', y: false,
'15:00:00', x: {
'16:00:00', style: {
'17:00:00', selectionFill: 'rgb(84, 204, 152)',
'18:00:00', selectionFillOpacity: 0.1,
'19:00:00', handleIconFill: 'rgb(84, 204, 152)',
'20:00:00', handleIconFillOpacity: 0.15,
'21:00:00', handleIconStrokeOpacity: 0,
'22:00:00', sparklineType: 'line',
'23:00:00', sparkline: true
'24:00:00' },
]; sparkline: true
const typeList = ['GPU', 'CPU', 'Memory', 'VRAM'];
const generateData = () => {
const data = [];
for (let i = 0; i < timeList.length; i++) {
for (let j = 0; j < typeList.length; j++) {
data.push({
time: timeList[i],
type: typeList[j],
value: _.get(mockData, typeList[j])[i]
});
}
} }
return data;
}; };
const data = generateData();
const labelFormatter = (value: any) => {
return `${value}%`;
};
const generateData = () => {
const list: { value: number; time: string; type: string }[] = [];
_.each(typeList, (type: any) => {
const dataList = _.map(_.get(data, type, []), (item: any) => {
return {
value: _.round(item.value, 2) || 0,
time: dayjs(item.timestamp * 1000).format('HH:mm:ss'),
type: _.get(TypeKeyMap, type, '')
};
});
list.push(...dataList);
});
setResult(list);
};
useEffect(() => {
generateData();
}, [data]);
return ( return (
<> <>
<LineChart height={400} data={data} /> <LineChart
data={result}
labelFormatter={labelFormatter}
slider={sliderConfig}
/>
</> </>
); );
}; };
+85 -49
View File
@@ -1,20 +1,43 @@
import GaugeChart from '@/components/charts/gauge'; import CardWrapper from '@/components/card-wrapper';
import LiquidChart from '@/components/charts/liquid';
import PageTools from '@/components/page-tools'; import PageTools from '@/components/page-tools';
import { PageContainer } from '@ant-design/pro-components'; import breakpoints from '@/config/breakpoints';
import useWindowResize from '@/hooks/use-window-resize';
import { Col, DatePicker, Row } from 'antd'; import { Col, DatePicker, Row } from 'antd';
import _ from 'lodash';
import { useContext, useEffect, useState } from 'react';
import { DashboardContext } from '../config/dashboard-context';
import ResourceUtilization from './resource-utilization'; import ResourceUtilization from './resource-utilization';
const SystemLoad = () => { const SystemLoad = () => {
const handleSelectDate = (date: string) => { const colors = [
console.log('dateString============', date); 'linear-gradient(90deg, rgba(84, 204, 152,.8) 0%, rgba(84, 204, 152,0.5) 50%, rgba(84, 204, 152,.8) 100%)',
}; 'linear-gradient(90deg, rgba(255, 214, 102,.8) 0%, rgba(255, 214, 102,0.5) 50%, rgba(255, 214, 102,.8) 100%)',
'linear-gradient(90deg, rgba(255, 120, 117,.8) 0%, rgba(255, 120, 117,0.5) 50%, rgba(255, 120, 117,.8) 100%)'
];
const data = useContext(DashboardContext)?.system_load?.current || {};
const { size } = useWindowResize();
const [paddingRight, setPaddingRight] = useState<string>('20px');
const [smallChartHeight, setSmallChartHeight] = useState<number>(190);
const [largeChartHeight, setLargeChartHeight] = useState<number>(400);
const thresholds = [0.5, 0.7, 1];
const height = 400;
const handleSelectDate = (date: string) => {};
useEffect(() => {
if (size.width < breakpoints.xl) {
setPaddingRight('0');
} else {
setPaddingRight('20px');
}
}, [size.width]);
return ( return (
<PageContainer ghost title={false}> <div>
<div className="system-load"> <div className="system-load">
<PageTools <PageTools
marginBottom={10} style={{ margin: '32px 8px' }}
marginTop={0}
left={ left={
<span style={{ fontSize: 'var(--font-size-large)' }}> <span style={{ fontSize: 'var(--font-size-large)' }}>
System Load System Load
@@ -24,51 +47,64 @@ const SystemLoad = () => {
<DatePicker onChange={handleSelectDate} style={{ width: 300 }} /> <DatePicker onChange={handleSelectDate} style={{ width: 300 }} />
} }
/> />
<ResourceUtilization /> <Row style={{ width: '100%' }} gutter={[0, 20]}>
<Row style={{ width: '100%', marginTop: '32px' }}> <Col
<Col xs={24} sm={24} md={12} lg={6} xl={6}> xs={24}
<GaugeChart sm={24}
title="GPU Compute Utilization" md={24}
total={100} lg={24}
target={20} xl={16}
// height={320} style={{ paddingRight: paddingRight }}
thresholds={[50, 70, 100]} >
rangColor={['#54cc98', '#ffd666', '#ff7875']} <CardWrapper style={{ height: height, width: '100%' }}>
></GaugeChart> <ResourceUtilization />
</CardWrapper>
</Col> </Col>
<Col xs={24} sm={24} md={12} lg={6} xl={6}> <Col xs={24} sm={24} md={24} lg={24} xl={8}>
<GaugeChart <CardWrapper style={{ height: largeChartHeight, width: '100%' }}>
title="GPU Memory Utilization" <Row style={{ height: largeChartHeight, width: '100%' }}>
total={100} <Col span={12} style={{ height: smallChartHeight }}>
target={30} <LiquidChart
// height={320} title="GPU Compute Utilization"
thresholds={[50, 70, 100]} percent={_.round(data.gpu?.utilization_rate || 0, 2) / 100}
rangColor={['#54cc98', '#ffd666', '#ff7875']} thresholds={thresholds}
></GaugeChart> rangColor={colors}
</Col> ></LiquidChart>
<Col xs={24} sm={24} md={12} lg={6} xl={6}> </Col>
<GaugeChart <Col span={12} style={{ height: smallChartHeight }}>
title="CPU Compute Utilization" <LiquidChart
total={100} title="GPU Memory Utilization"
target={40} percent={
// height={320} _.round(data.gpu_memory?.utilization_rate || 0, 2) / 100
thresholds={[50, 70, 100]} }
rangColor={['#54cc98', '#ffd666', '#ff7875']} thresholds={thresholds}
></GaugeChart> rangColor={colors}
</Col> ></LiquidChart>
<Col xs={24} sm={24} md={12} lg={6} xl={6}> </Col>
<GaugeChart <Col span={12} style={{ height: smallChartHeight }}>
title="CPU Memory Utilization" <LiquidChart
total={100} title="CPU Compute Utilization"
target={70} percent={_.round(data.cpu?.utilization_rate || 0, 2) / 100}
// height={320} thresholds={thresholds}
thresholds={[50, 70, 100]} rangColor={colors}
rangColor={['#54cc98', '#ffd666', '#ff7875']} ></LiquidChart>
></GaugeChart> </Col>
<Col span={12} style={{ height: smallChartHeight }}>
<LiquidChart
title="CPU Memory Utilization"
percent={
_.round(data.memory?.utilization_rate || 0, 2) / 100
}
thresholds={thresholds}
rangColor={colors}
></LiquidChart>
</Col>
</Row>
</CardWrapper>
</Col> </Col>
</Row> </Row>
</div> </div>
</PageContainer> </div>
); );
}; };
+143 -46
View File
@@ -1,10 +1,17 @@
import CardWrapper from '@/components/card-wrapper';
import ColumnBar from '@/components/charts/column-bar'; import ColumnBar from '@/components/charts/column-bar';
import HBar from '@/components/charts/h-bar'; import HBar from '@/components/charts/h-bar';
import ContentWrapper from '@/components/content-wrapper';
import PageTools from '@/components/page-tools'; import PageTools from '@/components/page-tools';
import breakpoints from '@/config/breakpoints';
import useWindowResize from '@/hooks/use-window-resize';
import { generateRandomArray } from '@/utils'; import { generateRandomArray } from '@/utils';
import { Col, DatePicker, Row } from 'antd'; import { Col, DatePicker, Row } from 'antd';
import dayjs from 'dayjs';
import _ from 'lodash';
import { useContext, useEffect, useState } from 'react';
import { DashboardContext } from '../config/dashboard-context';
const { RangePicker } = DatePicker;
const times = [ const times = [
'june 1', 'june 1',
'june 2', 'june 2',
@@ -86,64 +93,154 @@ const tokenUsage = TokensData.map((val, i) => {
}); });
const Usage = () => { const Usage = () => {
const handleSelectDate = (dateString: string) => { const { size } = useWindowResize();
console.log('dateString============', dateString); const [paddingRight, setPaddingRight] = useState<string>('20px');
const [requestData, setRequestData] = useState<
{ time: string; value: number }[]
>([]);
const [tokenData, setTokenData] = useState<{ time: string; value: number }[]>(
[]
);
const [userData, setUserData] = useState<{ name: string; value: number }[]>(
[]
);
const data = useContext(DashboardContext)?.model_usage || {};
const handleSelectDate = (dateString: string) => {};
const generateData = () => {
const requestList: { time: string; value: number }[] = [];
const tokenList: {
time: string;
value: number;
name: string;
color: string;
}[] = [];
const userList: {
name: string;
value: number;
type: string;
color: string;
}[] = [];
_.each(data.api_request_history, (item: any) => {
requestList.push({
time: dayjs(item.timestamp * 1000).format('YYYY-MM-DD'),
value: item.value
});
});
_.each(data.completion_token_history, (item: any) => {
tokenList.push({
time: dayjs(item.timestamp * 1000).format('YYYY-MM-DD'),
name: 'completion_token',
color:
'linear-gradient(90deg,rgba(84, 204, 152,0.8) 0%,rgb(0, 168, 143,.7) 100%)',
value: item.value
});
});
_.each(data.prompt_token_history, (item: any) => {
tokenList.push({
time: dayjs(item.timestamp * 1000).format('YYYY-MM-DD'),
name: 'prompt_token',
color:
'linear-gradient(90deg,rgba(0, 170, 173, 0.8) 0%,rgba(0, 109, 193, 0.7) 100%)',
value: item.value
});
});
_.each(data.top_users, (item: any) => {
userList.push({
name: item.username,
type: 'completion_token',
color:
'linear-gradient(90deg,rgba(84, 204, 152,0.8) 0%,rgb(0, 168, 143,.7) 100%)',
value: item.completion_token_count
});
userList.push({
name: item.username,
type: 'prompt_token',
color:
'linear-gradient(90deg,rgba(0, 170, 173, 0.8) 0%,rgba(0, 109, 193, 0.7) 100%)',
value: item.prompt_token_count
});
});
setRequestData(requestList);
setTokenData(tokenList);
setUserData(userList);
}; };
useEffect(() => {
if (size.width < breakpoints.xl) {
setPaddingRight('0');
} else {
setPaddingRight('20px');
}
}, [size.width]);
useEffect(() => {
generateData();
}, [data]);
return ( return (
<> <>
<PageTools <PageTools
style={{ margin: '32px 40px' }} style={{ margin: '32px 8px' }}
left={<span style={{ fontSize: 'var(--font-size-large)' }}>Usage</span>} left={<span style={{ fontSize: 'var(--font-size-large)' }}>Usage</span>}
right={ right={
<DatePicker onChange={handleSelectDate} style={{ width: 300 }} /> <RangePicker onChange={handleSelectDate} style={{ width: 300 }} />
} }
/> />
<Row style={{ width: '100%' }} gutter={[20, 0]}> <Row style={{ width: '100%' }} gutter={[0, 20]}>
<Col span={12}> <Col
<ContentWrapper title={false} contentStyle={{ paddingRight: 0 }}> xs={24}
<ColumnBar sm={24}
title="API Request" md={24}
data={dataList} lg={24}
xField="time" xl={16}
yField="value" style={{ paddingRight: paddingRight }}
height={360} >
></ColumnBar> <CardWrapper style={{ width: '100%' }}>
</ContentWrapper> <Row style={{ width: '100%' }}>
<Col span={12}>
<ColumnBar
title="API Request"
data={requestData}
xField="time"
yField="value"
height={360}
></ColumnBar>
</Col>
<Col span={12}>
<ColumnBar
title="Tokens"
data={tokenData}
group={false}
colorField="name"
stack={true}
xField="time"
legend={false}
yField="value"
height={360}
></ColumnBar>
</Col>
</Row>
</CardWrapper>
</Col> </Col>
<Col span={12}> <Col xs={24} sm={24} md={24} lg={24} xl={8}>
<ContentWrapper title={false} contentStyle={{ paddingLeft: 0 }}> <CardWrapper>
<ColumnBar
title="Tokens"
data={tokenUsage}
xField="time"
yField="value"
height={360}
></ColumnBar>
</ContentWrapper>
</Col>
</Row>
<Row style={{ width: '100%' }} gutter={[20, 0]}>
<Col span={12}>
<ContentWrapper title={false} contentStyle={{ paddingRight: 0 }}>
<HBar <HBar
title="Top Users" title="Top Users"
data={userDataList} data={userData}
xField="time" colorField="type"
stack={true}
legend={false}
xField="name"
yField="value" yField="value"
height={400} height={360}
></HBar> ></HBar>
</ContentWrapper> </CardWrapper>
</Col>
<Col span={12}>
<ContentWrapper title={false} contentStyle={{ paddingLeft: 0 }}>
<HBar
title="Top Projects"
data={projectDataList}
xField="time"
yField="value"
height={400}
></HBar>
</ContentWrapper>
</Col> </Col>
</Row> </Row>
</> </>
@@ -0,0 +1,8 @@
import { createContext } from 'react';
import { DashboardProps } from './types';
export const DashboardContext = createContext<DashboardProps>(
{} as DashboardProps
);
export default DashboardContext;
+14 -14
View File
@@ -1,32 +1,32 @@
export const overviewConfigs = [ export const overviewConfigs = [
{ {
key: 'workers', key: 'workworker_count',
label: 'Workers', label: 'Workers',
backgroundColor: // backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
'linear-gradient(180deg, rgb(0 139 188 / 20%) 0%, rgba(40,207,181,.2) 100%)' backgroundColor: 'var(--color-white-1)'
}, },
{ {
key: 'gpus', key: 'gpu_count',
label: 'Total GPUs', label: 'Total GPUs',
backgroundColor: backgroundColor: 'var(--color-white-1)'
'linear-gradient(180deg, rgb(0 139 188 / 20%) 0%, rgba(40,207,181,.2) 100%)' // backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
}, },
{ {
key: 'allocatedGpus', key: 'allocatedGpus',
label: 'Allocated GPUs', label: 'Allocated GPUs',
backgroundColor: backgroundColor: 'var(--color-white-1)'
'linear-gradient(180deg, rgb(0 139 188 / 20%) 0%, rgba(40,207,181,.2) 100%)' // backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
}, },
{ {
key: 'models', key: 'model_count',
label: 'Models', label: 'Models',
backgroundColor: backgroundColor: 'var(--color-white-1)'
'linear-gradient(180deg, rgb(0 139 188 / 20%) 0%, rgba(40,207,181,.2) 100%)' // backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
}, },
{ {
key: 'instances', key: 'model_instance_count',
label: 'Instances', label: 'Instances',
backgroundColor: backgroundColor: 'var(--color-white-1)'
'linear-gradient(180deg, rgb(0 139 188 / 20%) 0%, rgba(40,207,181,.2) 100%)' // backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
} }
]; ];
+62
View File
@@ -0,0 +1,62 @@
export interface DashboardProps {
resource_counts: {
worker_count: number;
gpu_count: number;
model_count: number;
model_instance_count: number;
};
system_load: {
current: {
cpu: {
total: number;
used: number;
utilization_rate: number;
};
memory: {
total: number;
used: number;
utilization_rate: number;
};
gpu: {
total: number;
used: number;
utilization_rate: number;
};
gpu_memory: {
total: number;
used: number;
utilization_rate: number;
};
};
history: {
cpu: {
timestamp: number;
value: number;
}[];
memory: {
timestamp: number;
value: number;
}[];
gpu: {
timestamp: number;
value: number;
}[];
gpu_memory: {
timestamp: number;
value: number;
}[];
};
};
model_usage: {
api_request_history: any[];
completion_token_history: any[];
prompt_token_history: any[];
top_users: {
user_id: number;
username: string;
prompt_token_count: number;
completion_token_count: number;
}[];
};
active_models: any[];
}
+30 -10
View File
@@ -1,20 +1,40 @@
import DividerLine from '@/components/divider-line'; import { Spin } from 'antd';
import { useEffect, useState } from 'react';
import { queryDashboardData } from './apis';
import ActiveTable from './components/active-table'; import ActiveTable from './components/active-table';
import Overview from './components/over-view'; import Overview from './components/over-view';
import SystemLoad from './components/system-load'; import SystemLoad from './components/system-load';
import Usage from './components/usage'; import Usage from './components/usage';
import DashboardContext from './config/dashboard-context';
import { DashboardProps } from './config/types';
const Dashboard: React.FC = () => { const Dashboard: React.FC = () => {
const [data, setData] = useState<DashboardProps>({} as DashboardProps);
const [loading, setLoading] = useState(false);
const getDashboardData = async () => {
try {
setLoading(true);
const res = await queryDashboardData();
setData(res);
setLoading(false);
} catch (error) {
setLoading(false);
setData({} as DashboardProps);
}
};
useEffect(() => {
getDashboardData();
}, []);
return ( return (
<> <Spin spinning={loading}>
<Overview></Overview> <DashboardContext.Provider value={{ ...data }}>
<DividerLine></DividerLine> <Overview></Overview>
<SystemLoad></SystemLoad> <SystemLoad></SystemLoad>
<DividerLine></DividerLine> <Usage></Usage>
<Usage></Usage> <ActiveTable></ActiveTable>
<DividerLine></DividerLine> </DashboardContext.Provider>
<ActiveTable></ActiveTable> </Spin>
</>
); );
}; };
+1 -2
View File
@@ -26,8 +26,7 @@ type AddModalProps = {
const sourceOptions = [ const sourceOptions = [
{ label: 'Huggingface', value: 'huggingface', key: 'huggingface' }, { label: 'Huggingface', value: 'huggingface', key: 'huggingface' },
{ label: 'Ollama', value: 'ollama_library', key: 'ollama_library' }, { label: 'Ollama Library', value: 'ollama_library', key: 'ollama_library' }
{ label: 'S3', value: 's3', key: 's3' }
]; ];
const AddModal: React.FC<AddModalProps> = (props) => { const AddModal: React.FC<AddModalProps> = (props) => {
+1 -1
View File
@@ -114,7 +114,7 @@ const Models: React.FC = () => {
clearInterval(timer.current); clearInterval(timer.current);
timer.current = setInterval(() => { timer.current = setInterval(() => {
fetchData(true); fetchData(true);
}, 3000); }, 5000);
}; };
const handleShowSizeChange = (page: number, size: number) => { const handleShowSizeChange = (page: number, size: number) => {
@@ -18,11 +18,20 @@ interface ChatFooterProps {
onView: () => void; onView: () => void;
disabled?: boolean; disabled?: boolean;
feedback?: React.ReactNode; feedback?: React.ReactNode;
hasTokenResult?: boolean;
} }
const ChatFooter: React.FC<ChatFooterProps> = (props) => { const ChatFooter: React.FC<ChatFooterProps> = (props) => {
const intl = useIntl(); const intl = useIntl();
const { onSubmit, onClear, onNewMessage, onView, feedback, disabled } = props; const {
onSubmit,
onClear,
onNewMessage,
onView,
feedback,
disabled,
hasTokenResult
} = props;
useHotkeys( useHotkeys(
HotKeys.SUBMIT.join(','), HotKeys.SUBMIT.join(','),
() => { () => {
@@ -34,7 +43,7 @@ const ChatFooter: React.FC<ChatFooterProps> = (props) => {
return ( return (
<div className="chat-footer"> <div className="chat-footer">
<Row style={{ width: '100%' }}> <Row style={{ width: '100%' }}>
<Col span={8}> <Col span={hasTokenResult ? 8 : 12}>
<Space size={20}> <Space size={20}>
<Button <Button
disabled={disabled} disabled={disabled}
@@ -53,8 +62,8 @@ const ChatFooter: React.FC<ChatFooterProps> = (props) => {
</Button> </Button>
</Space> </Space>
</Col> </Col>
<Col span={8}>{feedback}</Col> <Col span={hasTokenResult ? 8 : 0}>{feedback}</Col>
<Col span={8} style={{ textAlign: 'right' }}> <Col span={hasTokenResult ? 8 : 12} style={{ textAlign: 'right' }}>
<Space size={20}> <Space size={20}>
<Button <Button
icon={<CodeOutlined></CodeOutlined>} icon={<CodeOutlined></CodeOutlined>}
@@ -235,6 +235,7 @@ const MessageList: React.FC<MessageProps> = (props) => {
onSubmit={handleSubmit} onSubmit={handleSubmit}
onView={handleView} onView={handleView}
disabled={loading} disabled={loading}
hasTokenResult={!!tokenResult}
feedback={<ReferenceParams usage={tokenResult}></ReferenceParams>} feedback={<ReferenceParams usage={tokenResult}></ReferenceParams>}
></ChatFooter> ></ChatFooter>
</div> </div>
+20 -14
View File
@@ -1,3 +1,4 @@
import CardWrapper from '@/components/card-wrapper';
import { useSearchParams } from '@umijs/max'; import { useSearchParams } from '@umijs/max';
import { Divider } from 'antd'; import { Divider } from 'antd';
import { useState } from 'react'; import { useState } from 'react';
@@ -26,20 +27,25 @@ const Playground: React.FC = () => {
}; };
return ( return (
<div className="play-ground"> <div style={{ padding: '32px 40px' }}>
<div className="chat"> <CardWrapper>
<GroundLeft parameters={params}></GroundLeft> <div className="play-ground">
</div> <div className="chat">
<div className="divider-line"> <GroundLeft parameters={params}></GroundLeft>
<Divider type="vertical" /> </div>
</div> <div className="divider-line">
<div className="params"> <Divider type="vertical" />
<ParamsSettings </div>
onClose={handleClosePopover}
setParams={setParams} <div className="params">
selectedModel={selectModel} <ParamsSettings
/> onClose={handleClosePopover}
</div> setParams={setParams}
selectedModel={selectModel}
/>
</div>
</div>
</CardWrapper>
</div> </div>
); );
}; };
+7 -1
View File
@@ -1,11 +1,17 @@
.ground-left { .ground-left {
position: relative; position: relative;
height: 100vh; height: calc(100vh - 84px);
padding-bottom: 120px; padding-bottom: 120px;
.message-list-wrap { .message-list-wrap {
max-height: calc(100vh - 152px); max-height: calc(100vh - 152px);
overflow-y: auto; overflow-y: auto;
.ant-pro-page-container-children-container {
padding-inline: 26px;
}
} }
.ground-left-footer { .ground-left-footer {
position: absolute; position: absolute;
bottom: 20px; bottom: 20px;
+5 -1
View File
@@ -1,20 +1,24 @@
.play-ground { .play-ground {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
.chat { .chat {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.params { .params {
// width: 465px; // width: 465px;
padding: 20px; padding: 20px;
} }
.divider-line { .divider-line {
width: 1px; width: 1px;
.ant-divider { .ant-divider {
margin: 0; margin: 0;
min-height: 100vh; min-height: calc(100vh - 84px);
} }
} }
} }
+58 -51
View File
@@ -1,3 +1,4 @@
import CardWrapper from '@/components/card-wrapper';
import FormButtons from '@/components/form-buttons'; import FormButtons from '@/components/form-buttons';
import SealInput from '@/components/seal-form/seal-input'; import SealInput from '@/components/seal-form/seal-input';
import { PasswordReg } from '@/config'; import { PasswordReg } from '@/config';
@@ -42,14 +43,17 @@ const Profile: React.FC = () => {
}} }}
extra={[]} extra={[]}
> >
<Form <CardWrapper
style={{ width: '524px' }} style={{ padding: '32px', marginTop: '70px', width: 'max-content' }}
name="profileForm"
form={form}
onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed}
> >
{/* <Form.Item<ProfileProps> <Form
style={{ width: '524px' }}
name="profileForm"
form={form}
onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed}
>
{/* <Form.Item<ProfileProps>
name="username" name="username"
rules={[ rules={[
{ {
@@ -69,50 +73,53 @@ const Profile: React.FC = () => {
style={{ width: INPUT_WIDTH.default }} style={{ width: INPUT_WIDTH.default }}
></SealInput.Input> ></SealInput.Input>
</Form.Item> */} </Form.Item> */}
<Form.Item<ProfileProps> <Form.Item<ProfileProps>
name="current_password" name="current_password"
rules={[ rules={[
{ {
required: true, required: true,
message: intl.formatMessage( message: intl.formatMessage(
{ id: 'common.form.rule.input' }, { id: 'common.form.rule.input' },
{ {
name: intl.formatMessage({ name: intl.formatMessage({
id: 'users.form.currentpassword' id: 'users.form.currentpassword'
}) })
} }
) )
} }
]} ]}
> >
<SealInput.Password <SealInput.Password
label={intl.formatMessage({ id: 'users.form.currentpassword' })} label={intl.formatMessage({ id: 'users.form.currentpassword' })}
required required
style={{ width: INPUT_WIDTH.default }} style={{ width: INPUT_WIDTH.default }}
></SealInput.Password> ></SealInput.Password>
</Form.Item> </Form.Item>
<Form.Item<ProfileProps> <Form.Item<ProfileProps>
name="new_password" name="new_password"
rules={[ rules={[
{ {
required: true, required: true,
pattern: PasswordReg, pattern: PasswordReg,
message: intl.formatMessage({ id: 'users.form.rule.password' }) message: intl.formatMessage({
} id: 'users.form.rule.password'
]} })
> }
<SealInput.Password ]}
label={intl.formatMessage({ id: 'users.form.newpassword' })} >
required <SealInput.Password
style={{ width: INPUT_WIDTH.default }} label={intl.formatMessage({ id: 'users.form.newpassword' })}
></SealInput.Password> required
</Form.Item> style={{ width: INPUT_WIDTH.default }}
<FormButtons ></SealInput.Password>
htmlType="submit" </Form.Item>
onCancel={handleCancel} <FormButtons
showCancel={false} htmlType="submit"
></FormButtons> onCancel={handleCancel}
</Form> showCancel={false}
></FormButtons>
</Form>
</CardWrapper>
</PageContainer> </PageContainer>
</StrictMode> </StrictMode>
); );
+9 -8
View File
@@ -201,14 +201,7 @@ const Models: React.FC = () => {
); );
}} }}
/> />
<Column
title={intl.formatMessage({ id: 'resources.table.disk' })}
dataIndex="storage"
key="storage"
render={(text, record: ListItem) => {
return <ProgressBar percent={0}></ProgressBar>;
}}
/>
<Column <Column
title={intl.formatMessage({ id: 'resources.table.vram' })} title={intl.formatMessage({ id: 'resources.table.vram' })}
dataIndex="VRAM" dataIndex="VRAM"
@@ -240,6 +233,14 @@ const Models: React.FC = () => {
); );
}} }}
/> />
<Column
title={intl.formatMessage({ id: 'resources.table.disk' })}
dataIndex="storage"
key="storage"
render={(text, record: ListItem) => {
return <ProgressBar percent={0}></ProgressBar>;
}}
/>
</Table> </Table>
</> </>
); );
+1
View File
@@ -1,3 +1,4 @@
import _ from 'lodash';
export const isNotEmptyValue = (value: any) => { export const isNotEmptyValue = (value: any) => {
if (Array.isArray(value)) { if (Array.isArray(value)) {
return value.length > 0; return value.length > 0;