diff --git a/config/theme.ts b/config/theme.ts index 4802ffa5..ee6229c7 100644 --- a/config/theme.ts +++ b/config/theme.ts @@ -8,7 +8,8 @@ export default { }, Table: { cellFontSize: 12, - rowSelectedHoverBg: '#e6f6ff' + rowSelectedHoverBg: 'rgba(230, 230, 230, 0.88)', + rowSelectedBg: 'rgb(244, 245, 244)' }, Menu: { iconSize: 16, @@ -20,10 +21,14 @@ export default { }, Slider: { handleSize: 8, - handleSizeHover: 10 - // handleColor: '#007BFF' - // trackBg: '#007BFF', - // trackHoverBg: '#007BFF' + handleSizeHover: 8, + trackBg: 'rgba(0,0,0,0.15)', + handleColor: 'rgba(0,0,0,0.2)', + trackHoverBg: 'rgba(0,0,0,0.25)', + handleActiveColor: 'rgba(0,0,0,0.3)', + dotActiveBorderColor: 'rgba(0,0,0,0.25)', + handleActiveOutlineColor: 'rgba(0,0,0,0.25)', + dotBorderColor: 'rgba(0,0,0,0.25)' } }, token: { diff --git a/package.json b/package.json index 24f87db9..a5d3a95b 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "classnames": "^2.5.1", "crypto-js": "^4.2.0", "dayjs": "^1.11.11", + "echarts": "^5.5.1", "has-ansi": "^5.0.1", "jotai": "^2.8.4", "localforage": "^1.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b87d360..28feedd4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ dependencies: dayjs: specifier: ^1.11.11 version: 1.11.11 + echarts: + specifier: ^5.5.1 + version: 5.5.1 has-ansi: specifier: ^5.0.1 version: 5.0.1 @@ -8238,6 +8241,13 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: false + /echarts@5.5.1: + resolution: {integrity: sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==, tarball: https://registry.npmjs.org/echarts/-/echarts-5.5.1.tgz} + dependencies: + tslib: 2.3.0 + zrender: 5.6.0 + dev: false + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false @@ -16286,7 +16296,11 @@ packages: dev: false /tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==, tarball: https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz} + dev: false + + /tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==, tarball: https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz} dev: false /tslib@2.6.2: @@ -17187,6 +17201,12 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + /zrender@5.6.0: + resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==, tarball: https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz} + dependencies: + tslib: 2.3.0 + dev: false + /zscroller@0.4.8: resolution: {integrity: sha512-G5NiNLKx2+QhhvZi2yV1jjVXY50otktxkseX2hG2N/eixohOUk0AY8ZpbAxNqS9oJS/NxItCsowupy2tsXxAMw==, tarball: https://registry.npmjs.org/zscroller/-/zscroller-0.4.8.tgz} dependencies: diff --git a/src/assets/images/bg-12.png b/src/assets/images/bg-12.png deleted file mode 100644 index a550b46a..00000000 Binary files a/src/assets/images/bg-12.png and /dev/null differ diff --git a/src/assets/images/bg-2.png b/src/assets/images/bg-2.png deleted file mode 100644 index 91e22ef0..00000000 Binary files a/src/assets/images/bg-2.png and /dev/null differ diff --git a/src/assets/images/bg-6.png b/src/assets/images/bg-6.png deleted file mode 100644 index d76880ef..00000000 Binary files a/src/assets/images/bg-6.png and /dev/null differ diff --git a/src/assets/images/bg.png b/src/assets/images/bg.png deleted file mode 100644 index de7f0623..00000000 Binary files a/src/assets/images/bg.png and /dev/null differ diff --git a/src/assets/images/gpustack-logo.png b/src/assets/images/gpustack-logo.png new file mode 100644 index 00000000..df995fc9 Binary files /dev/null and b/src/assets/images/gpustack-logo.png differ diff --git a/src/assets/styles/common.less b/src/assets/styles/common.less index 21f495fe..dd10243c 100644 --- a/src/assets/styles/common.less +++ b/src/assets/styles/common.less @@ -45,6 +45,11 @@ align-items: center; } +.flex-gap-2 { + display: flex; + gap: 2px; +} + .opct-7 { opacity: 0.7; } diff --git a/src/components/card-wrapper/index.less b/src/components/card-wrapper/index.less index edd203f9..95fffd55 100644 --- a/src/components/card-wrapper/index.less +++ b/src/components/card-wrapper/index.less @@ -1,8 +1,7 @@ .cardWrapper { border-radius: var(--border-radius-small); background-color: var(--color-white-1); - // box-shadow: var(--box-shadow-base); box-shadow: none; padding: 10px; - border: 1px solid var(--ant-color-border); + border: 1px solid var(--color-border-1); } diff --git a/src/components/charts/area.tsx b/src/components/charts/area.tsx index 5f00f439..5f9834bf 100644 --- a/src/components/charts/area.tsx +++ b/src/components/charts/area.tsx @@ -5,7 +5,7 @@ interface LineChartProps { title?: string; height?: number; data: any[]; - color?: string[]; + color?: string; xField?: string; yField?: string; locale?: string; @@ -47,7 +47,14 @@ const LineChart: React.FC = (props) => { } }, style: { - fill: 'rgba(84, 204, 152,0.8)' + fill: (params: any) => { + console.log('area param=========', params); + return color || 'rgba(84, 204, 152,0.8)'; + }, + stroke: (params: any) => { + console.log('area param========2=', params); + return color || 'rgba(84, 204, 152,0.8)'; + } }, title: { title, diff --git a/src/components/charts/column-bar.tsx b/src/components/charts/column-bar.tsx index 672e0211..dd0b878c 100644 --- a/src/components/charts/column-bar.tsx +++ b/src/components/charts/column-bar.tsx @@ -89,6 +89,7 @@ const BarChart: React.FC = (props) => { style: { fill: (params: any) => { + console.log('colbar=====', params); return params.color || 'rgba(84, 204, 152,0.8)'; }, // radiusTopLeft: 12, diff --git a/src/components/charts/line-chart.tsx b/src/components/charts/line-chart.tsx index f184ce0a..2228a6bc 100644 --- a/src/components/charts/line-chart.tsx +++ b/src/components/charts/line-chart.tsx @@ -55,19 +55,19 @@ const LineChart: React.FC = (props) => { // }, style: { lineWidth: 1.5, - opacity: 0.8 + opacity: 0.7 + // fill: (params: any) => { + // return params.color; + // } // stroke: (params: any) => { - // return ( - // params.color || - // 'linear-gradient(90deg,rgba(84, 204, 152,0.8) 0%,rgba(0, 168, 143,.7) 100%)' - // ); + // console.log('line param=========', params.color); + // return params.color; // } }, legend: { color: { layout: { justifyContent: 'center' } }, - size: { itemLabelFontSize: 14, itemLabelFontWeight: 500 diff --git a/src/components/echarts/bar-chart.tsx b/src/components/echarts/bar-chart.tsx new file mode 100644 index 00000000..82b9fb4b --- /dev/null +++ b/src/components/echarts/bar-chart.tsx @@ -0,0 +1,96 @@ +import Chart from '@/components/echarts/chart'; +import { + barItemConfig, + grid, + legend, + tooltip, + xAxis, + yAxis +} from '@/components/echarts/config'; +import _ from 'lodash'; +import { useEffect, useState } from 'react'; +import { ChartProps } from './types'; + +const BarChart: React.FC = (props) => { + const { + seriesData, + xAxisData, + height, + width, + labelFormatter, + legendData, + title + } = props; + const [dataOptions, setDataOptions] = useState({}); + + const options = { + title: { + text: '' + }, + grid, + tooltip: { + ...tooltip + // axisPointer: { + // type: 'shadow' + // } + }, + xAxis: { + ...xAxis, + axisLabel: { + ...xAxis.axisLabel, + formatter: labelFormatter + }, + data: [] + }, + yAxis, + legend: { + ...legend, + data: [] + }, + + series: [] + }; + + useEffect(() => { + const data = _.map(seriesData, (item: any) => { + return { + ...item, + ...barItemConfig, + stack: 'total', + itemStyle: { + color: item.color + } + }; + }); + const optionsConfig = { + ...options, + title: { + show: true, + left: 'center', + textStyle: { + fontSize: 14 + }, + text: title + }, + yAxis: { + ...options.yAxis + }, + xAxis: { + ...options.xAxis, + data: xAxisData + }, + series: data + }; + console.log('optionsConfig=========', optionsConfig); + setDataOptions(optionsConfig); + }, [seriesData, xAxisData, title]); + return ( + + ); +}; + +export default BarChart; diff --git a/src/components/echarts/chart.tsx b/src/components/echarts/chart.tsx new file mode 100644 index 00000000..cf8c5eb2 --- /dev/null +++ b/src/components/echarts/chart.tsx @@ -0,0 +1,58 @@ +import { throttle } from 'echarts/core'; +import { useCallback, useEffect, useRef } from 'react'; +import echarts, { ECOption } from '.'; + +const Chart: React.FC<{ + options: ECOption; + height: number | string; + width: number | string; +}> = ({ options, width, height }) => { + const container = useRef(null); + const chart = useRef(); + + const init = useCallback(() => { + if (container.current) { + chart.current?.clear(); + chart.current = echarts.init(container.current); + } + }, []); + + const resize = useCallback(() => { + chart.current?.resize(); + }, []); + + const setOption = useCallback((options: ECOption) => { + chart.current?.clear(); + chart.current?.setOption(options, { + notMerge: true, + lazyUpdate: true + }); + }, []); + + useEffect(() => { + init(); + return () => { + chart.current?.dispose(); + }; + }, [init]); + + useEffect(() => { + resize(); + setOption(options); + }, [options]); + + // resize on window resize + useEffect(() => { + const handleResize = throttle(() => { + resize(); + }, 100); + window.addEventListener('resize', handleResize); + return () => { + window.removeEventListener('resize', handleResize); + }; + }, [resize, chart]); + + return
; +}; + +export default Chart; diff --git a/src/components/echarts/config.ts b/src/components/echarts/config.ts new file mode 100644 index 00000000..01f0ba9d --- /dev/null +++ b/src/components/echarts/config.ts @@ -0,0 +1,102 @@ +const chartColorMap = { + tickLineColor: 'rgba(217,217,217,0.5)', + axislabelColor: 'rgba(0, 0, 0, 0.4)' +}; + +export const tooltip = { + trigger: 'axis', + // axisPointer: { + // type: 'shadow' + // } + formatter(params: any) { + let result = `${params[0].axisValue}`; + params.forEach((item: any) => { + result += ` + + + ${item.seriesName}: + + ${item.data.value} + `; + }); + return `
${result}
`; + } +}; + +export const grid = { + left: 0, + right: 20, + bottom: 20, + containLabel: true +}; + +export const legend = { + itemWidth: 8, + itemHeight: 8 +}; + +export const xAxis = { + type: 'category', + boundaryGap: true, + axisTick: { + show: true, + lineStyle: { + color: chartColorMap.tickLineColor + } + }, + axisLabel: { + color: chartColorMap.axislabelColor, + fontSize: 12 + }, + axisLine: { + show: false + } +}; + +export const yAxis = { + // max: 100, + // min: 0, + splitLine: { + show: true, + lineStyle: { + type: 'dashed' + } + }, + axisLabel: { + color: chartColorMap.axislabelColor, + fontSize: 12 + }, + axisTick: { + show: false + }, + type: 'value' +}; + +export const title = { + show: true, + left: 'center', + textStyle: { + fontSize: 14 + }, + text: '' +}; + +export const barItemConfig = { + type: 'bar', + barMaxWidth: 20, + barMinWidth: 8, + // stack: 'total', + barGap: '30%', + barCategoryGap: '50%' +}; + +export const lineItemConfig = { + type: 'line', + smooth: true, + showSymbol: false, + itemStyle: {}, + lineStyle: { + width: 1.5, + opacity: 0.7 + } +}; diff --git a/src/components/echarts/h-bar.tsx b/src/components/echarts/h-bar.tsx new file mode 100644 index 00000000..0140f078 --- /dev/null +++ b/src/components/echarts/h-bar.tsx @@ -0,0 +1,116 @@ +import Chart from '@/components/echarts/chart'; +import { + grid, + legend, + title as titleConfig, + tooltip, + xAxis, + yAxis +} from '@/components/echarts/config'; +import _ from 'lodash'; +import { useEffect, useState } from 'react'; +import { ChartProps } from './types'; + +const BarChart: React.FC = (props) => { + const { + seriesData, + xAxisData, + height, + width, + labelFormatter, + legendData, + title + } = props; + const [dataOptions, setDataOptions] = useState({}); + + const options = { + title: titleConfig, + grid, + tooltip: { + ...tooltip + }, + xAxis: { + ...xAxis, + axisLabel: { + ...xAxis.axisLabel, + formatter: labelFormatter + } + }, + yAxis, + legend: { + ...legend, + data: [] + }, + + series: [] + }; + + useEffect(() => { + const data = _.map(seriesData, (item: any) => { + return { + ...item, + type: 'bar', + barWidth: 20, + stack: 'Ad', + barGap: '30%', + label: { + show: false + }, + itemStyle: { + color: item.color + } + }; + return item; + }); + const optionsConfig = { + ...options, + title: { + ...options.title, + left: 'start', + text: title + }, + yAxis: { + ...options.yAxis, + inverse: true, + type: 'category', + + splitLine: { + show: false + }, + data: xAxisData, + axisLine: { + show: false + }, + axisTick: { + show: false + } + }, + xAxis: { + ...options.xAxis, + boundaryGap: true, + type: 'value', + splitLine: { + show: false + }, + axisLabel: { + show: false + }, + axisTick: { + show: false + } + }, + series: data + }; + console.log('optionsConfig========h=', optionsConfig); + setDataOptions(optionsConfig); + }, [seriesData, xAxisData, title]); + return ( + + ); +}; + +export default BarChart; diff --git a/src/components/echarts/index.ts b/src/components/echarts/index.ts new file mode 100644 index 00000000..f46df571 --- /dev/null +++ b/src/components/echarts/index.ts @@ -0,0 +1,56 @@ +import type { + // 系列类型的定义后缀都为 SeriesOption + BarSeriesOption, + LineSeriesOption +} from 'echarts/charts'; +import { BarChart, LineChart } from 'echarts/charts'; +import type { + DatasetComponentOption, + GridComponentOption, + // 组件类型的定义后缀都为 ComponentOption + TitleComponentOption, + TooltipComponentOption +} from 'echarts/components'; +import { + // 数据集组件 + DatasetComponent, + GridComponent, + LegendComponent, + TitleComponent, + TooltipComponent, + // 内置数据转换器组件 (filter, sort) + TransformComponent +} from 'echarts/components'; +import type { ComposeOption } from 'echarts/core'; +import * as echarts from 'echarts/core'; +import { LabelLayout, UniversalTransition } from 'echarts/features'; +import { CanvasRenderer } from 'echarts/renderers'; + +// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型 +type ECOption = ComposeOption< + | BarSeriesOption + | LineSeriesOption + | TitleComponentOption + | TooltipComponentOption + | GridComponentOption + | DatasetComponentOption +>; + +// 注册必须的组件 +echarts.use([ + LegendComponent, + TitleComponent, + TooltipComponent, + GridComponent, + DatasetComponent, + TransformComponent, + BarChart, + LineChart, + LabelLayout, + UniversalTransition, + CanvasRenderer +]); + +export type { ECOption }; + +export default echarts; diff --git a/src/components/echarts/line-chart.tsx b/src/components/echarts/line-chart.tsx new file mode 100644 index 00000000..552d5dad --- /dev/null +++ b/src/components/echarts/line-chart.tsx @@ -0,0 +1,98 @@ +import Chart from '@/components/echarts/chart'; +import { + grid, + legend, + lineItemConfig, + tooltip, + xAxis, + yAxis +} from '@/components/echarts/config'; +import _ from 'lodash'; +import { useEffect, useState } from 'react'; +import { ChartProps } from './types'; + +const LineChart: React.FC = (props) => { + const { + seriesData, + xAxisData, + height, + width, + labelFormatter, + legendData, + smooth, + title + } = props; + const [dataOptions, setDataOptions] = useState({}); + + const options = { + title: { + text: '' + }, + grid, + tooltip: { + ...tooltip + }, + xAxis: { + ...xAxis, + axisLabel: { + ...xAxis.axisLabel, + formatter: labelFormatter + } + }, + yAxis, + legend: { + ...legend, + data: [] + }, + + series: [] + }; + + useEffect(() => { + const data = _.map(seriesData, (item: any) => { + return { + ...item, + ...lineItemConfig, + smooth: smooth, + itemStyle: { + ...lineItemConfig.itemStyle, + color: item.color + }, + lineStyle: { + ...lineItemConfig.lineStyle, + color: item.color + } + }; + }); + const optionsConfig = { + ...options, + title: { + show: true, + left: 'center', + textStyle: { + fontSize: 14 + }, + text: title + }, + yAxis: { + ...options.yAxis + }, + xAxis: { + ...options.xAxis, + data: xAxisData + }, + series: data + }; + console.log('optionsConfig========line=', optionsConfig); + setDataOptions(optionsConfig); + }, [seriesData, xAxisData, title]); + return ( + + ); +}; + +export default LineChart; diff --git a/src/components/echarts/types.ts b/src/components/echarts/types.ts new file mode 100644 index 00000000..30308460 --- /dev/null +++ b/src/components/echarts/types.ts @@ -0,0 +1,17 @@ +export interface ChartProps { + seriesData: any[]; + xAxisData: string[]; + legendData?: string[]; + labelFormatter?: (val?: any) => string; + height: string | number; + width?: string | number; + title?: string; + smooth?: boolean; +} + +export interface AreaChartItemProps { + name: string; + color: string; + areaStyle: any; + data: { time: string; value: number }[]; +} diff --git a/src/components/logs-viewer/index.tsx b/src/components/logs-viewer/index.tsx index f69a7dee..7a27d881 100644 --- a/src/components/logs-viewer/index.tsx +++ b/src/components/logs-viewer/index.tsx @@ -1,4 +1,5 @@ import useSetChunkRequest from '@/hooks/use-chunk-request'; +import useContainerScroll from '@/hooks/use-container-scorll'; import Convert from 'ansi-to-html'; import classNames from 'classnames'; import hasAnsi from 'has-ansi'; @@ -17,9 +18,18 @@ const LogsViewer: React.FC = (props) => { const [logsContent, setLogsContent] = useState(content); const { setChunkRequest } = useSetChunkRequest(); const chunkRequedtRef = useRef(null); + const scroller = useRef(null); + const { updateScrollerPosition, handleContentWheel } = useContainerScroll( + scroller, + { toBottom: true } + ); const convert = new Convert(); + useEffect(() => { + updateScrollerPosition(); + }, [logsContent]); + const updateContent = (newVal: string) => { if (hasAnsi(newVal)) { const htmlStr = `${convert.toHtml(newVal)}`; @@ -51,7 +61,12 @@ const LogsViewer: React.FC = (props) => { return (
-
+
{logsContent}
diff --git a/src/components/page-tools/index.tsx b/src/components/page-tools/index.tsx index 2aacf26c..553b9cba 100644 --- a/src/components/page-tools/index.tsx +++ b/src/components/page-tools/index.tsx @@ -14,7 +14,7 @@ const PageTools: React.FC = (props) => { left, right, marginBottom = 0, - marginTop = 60, + marginTop = 30, style: pageStyle } = props; diff --git a/src/components/progress-bar/index.tsx b/src/components/progress-bar/index.tsx index 8f177af3..e30b2d83 100644 --- a/src/components/progress-bar/index.tsx +++ b/src/components/progress-bar/index.tsx @@ -15,7 +15,7 @@ const RenderProgress = memo( return 'var(--ant-color-primary)'; } if (percent <= 50) { - return 'var(--ant-color-primary)'; + return 'var(--color-progress-green)'; } if (percent <= 80) { return 'var(--ant-color-warning)'; diff --git a/src/components/seal-table/index.tsx b/src/components/seal-table/index.tsx index c6fc2527..01d83020 100644 --- a/src/components/seal-table/index.tsx +++ b/src/components/seal-table/index.tsx @@ -104,6 +104,7 @@ const SealTable: React.FC = (props) => { title={title} style={headerStyle} firstCell={i === 0} + align={align} lastCell={i === props.children.length - 1} > diff --git a/src/components/seal-table/styles/index.less b/src/components/seal-table/styles/index.less index 014cdd69..2dc2e6a9 100644 --- a/src/components/seal-table/styles/index.less +++ b/src/components/seal-table/styles/index.less @@ -48,11 +48,10 @@ } &-selected { - background-color: var(--ant-table-row-selected-bg); transition: all 0.2s ease; &:hover { - background-color: var(--ant-table-row-selected-hover-bg); + background-color: var(--ant-table-row-hover-bg); transition: all 0.2s ease; } } diff --git a/src/config/index.ts b/src/config/index.ts index 90f05993..79940db3 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -21,7 +21,7 @@ export const StatusColorMap: Record = bg: `var(--ant-blue-1)` }, success: { - text: `var(--ant-color-primary)`, + text: `var(--color-progress-green)`, bg: `var(--ant-green-1)` }, inactive: { diff --git a/src/global.less b/src/global.less index e78b3131..f137c665 100644 --- a/src/global.less +++ b/src/global.less @@ -40,6 +40,8 @@ html { --color-chart-green: rgb(84, 204, 152, 80%); --color-chart-glod: rgba(250, 173, 20, 80%); --seal-transition-func: cubic-bezier(0, 0, 1, 1); + --color-progress-green: rgba(84, 204, 152, 100%); + --color-border-1: rgba(217, 217, 217, 100%); // ======== input ============ --ant-input-active-shadow: 0 0 0 2px rgba(5, 255, 105, 6%); --ant-input-active-border-color: #007bff; @@ -77,7 +79,7 @@ html { --ant-color-text: #000; --ant-color-error: #ff4d4f; --ant-color-bg-mask: rgba(0, 0, 0, 35%); - --ant-color-border: rgb(217 217 217 / 50%); + --ant-color-border: rgb(217 217 217 / 100%); &.ant-popover { --ant-popover-inner-padding: 26px; @@ -131,6 +133,10 @@ body * { } body { + // .ant-slider:hover .ant-slider-handle::after { + // box-shadow: 0 0 0 var(--ant-slider-handle-line-width) + // var(--ant-slider-handle-color); + // } .ant-modal-confirm-title { display: none; } @@ -406,3 +412,33 @@ body { ); background-size: 400% 100%; } + +.tooltip-wrapper { + display: flex; + flex-direction: column; + gap: 4px; + font-size: 11px; + background-color: rgba(255, 255, 255, 80%); + min-width: 100px; + + .tooltip-x-name { + font-size: var(--font-size-base); + color: var(--ant-color-text-tertiary); + } + + .tooltip-item { + color: var(--ant-color-text-secondary); + display: flex; + justify-content: space-between; + align-items: center; + + .tooltip-item-title { + margin-right: 2px; + } + + .tooltip-value { + margin-left: 10px; + color: var(--ant-color-text); + } + } +} diff --git a/src/hooks/use-container-scorll.ts b/src/hooks/use-container-scorll.ts new file mode 100644 index 00000000..675aee68 --- /dev/null +++ b/src/hooks/use-container-scorll.ts @@ -0,0 +1,48 @@ +import { useRef } from 'react'; + +export default function useContainerScroll( + container: any, + options?: { toBottom?: boolean } +) { + const isWheeled = useRef(false); + const scroller = useRef(container); + const optionsRef = useRef(options); + const toBottomFlag = useRef(options?.toBottom); + const handleContentWheel = (e: any) => { + isWheeled.current = true; + }; + + const scrollerRun = () => { + const scrollerContainer = scroller.current?.current || {}; + const { scrollHeight, clientHeight, scrollTop } = scrollerContainer; + if ( + optionsRef.current?.toBottom && + toBottomFlag.current && + scrollHeight > clientHeight + scrollTop + ) { + scroller.current.current.scrollTop = scrollHeight; + toBottomFlag.current = false; + isWheeled.current = false; + } + if ( + !isWheeled.current && + scrollHeight > clientHeight + scrollTop && + scroller.current?.current + ) { + scroller.current.current.scrollTop += 5; + window.requestAnimationFrame(scrollerRun); + } + }; + + const updateScrollerPosition = () => { + if (!isWheeled.current) { + window.requestAnimationFrame(scrollerRun); + } + }; + + return { + handleContentWheel, + updateScrollerPosition, + scroller + }; +} diff --git a/src/layouts/Logo.tsx b/src/layouts/Logo.tsx index b4ef0d95..8ebad26e 100644 --- a/src/layouts/Logo.tsx +++ b/src/layouts/Logo.tsx @@ -1,16 +1,11 @@ // @ts-nocheck // This file is generated by Umi automatically // DO NOT CHANGE IT MANUALLY! +import GpustackLogo from '@/assets/images/gpustack-logo.png'; import React from 'react'; const LogoIcon: React.FC = () => { - return ( - logo - ); + return logo; }; export default LogoIcon; diff --git a/src/locales/en-US/dashboard.ts b/src/locales/en-US/dashboard.ts index ecd78080..6bad47bd 100644 --- a/src/locales/en-US/dashboard.ts +++ b/src/locales/en-US/dashboard.ts @@ -2,7 +2,7 @@ export default { 'dashboard.title': 'Dashboard', 'dashboard.workers': 'Workers', 'dashboard.models': 'Models', - 'dashboard.totalgpus': 'Total GPUs', + 'dashboard.totalgpus': 'GPUs', 'dashboard.allocategpus': 'Allocated GPUs', 'dashboard.instances': 'Instances', 'dashboard.systemload': 'System Load', @@ -21,5 +21,5 @@ export default { 'dashboard.activeModels': 'Active Models', 'dashboard.runninginstances': 'Running Instances', 'dashboard.activeModels.name': 'Model Name', - 'dashboard.allocatevram': 'Allocated VRAM' + 'dashboard.allocatevram': 'Allocated VRAM / RAM' }; diff --git a/src/locales/zh-CN/dashboard.ts b/src/locales/zh-CN/dashboard.ts index e4b1c011..d5dc3f51 100644 --- a/src/locales/zh-CN/dashboard.ts +++ b/src/locales/zh-CN/dashboard.ts @@ -1,8 +1,8 @@ export default { 'dashboard.title': '概览', - 'dashboard.workers': '节点', + 'dashboard.workers': 'Workers', 'dashboard.models': '模型', - 'dashboard.totalgpus': '总 GPU 数量', + 'dashboard.totalgpus': 'GPUs', 'dashboard.allocategpus': '已分配 GPU 数量', 'dashboard.instances': '实例', 'dashboard.systemload': '系统负载', @@ -21,5 +21,5 @@ export default { 'dashboard.activeModels': '活跃模型', 'dashboard.activeModels.name': '模型名称', 'dashboard.runninginstances': '运行实例', - 'dashboard.allocatevram': '已分配显存' + 'dashboard.allocatevram': '已分配显存 / 内存' }; diff --git a/src/pages/api-keys/index.tsx b/src/pages/api-keys/index.tsx index 9f4e561a..c387d249 100644 --- a/src/pages/api-keys/index.tsx +++ b/src/pages/api-keys/index.tsx @@ -240,7 +240,7 @@ const Models: React.FC = () => { }} > { }} /> + { + return dayjs(text).format('YYYY-MM-DD HH:mm:ss'); + }} + /> { return dayjs(text).format('YYYY-MM-DD HH:mm:ss'); }} /> - { - return dayjs(text).format('YYYY-MM-DD HH:mm:ss'); - }} - /> { diff --git a/src/pages/dashboard/components/active-table.tsx b/src/pages/dashboard/components/active-table.tsx index d6d1cbab..5e39f979 100644 --- a/src/pages/dashboard/components/active-table.tsx +++ b/src/pages/dashboard/components/active-table.tsx @@ -72,7 +72,7 @@ const ActiveTable = () => { const data = useContext(DashboardContext).active_models || []; const modelColumns = [ { - title: intl.formatMessage({ id: 'dashboard.activeModels.name' }), + title: intl.formatMessage({ id: 'common.table.name' }), dataIndex: 'name', key: 'name' }, @@ -86,9 +86,16 @@ const ActiveTable = () => { // }, { title: intl.formatMessage({ id: 'dashboard.allocatevram' }), - dataIndex: 'allocate_gpu_memory_utilization', - key: 'gpu_memory_utilization', - render: (text: any, record: any) => {convertFileSize(text)} + dataIndex: 'resource_claim.memory', + key: 'gpu_memory', + render: (text: any, record: any) => { + return ( + + {convertFileSize(record.resource_claim?.gpu_memory || 0)} /{' '} + {convertFileSize(record.resource_claim?.memory || 0)} + + ); + } }, { title: intl.formatMessage({ id: 'dashboard.runninginstances' }), diff --git a/src/pages/dashboard/components/over-view.less b/src/pages/dashboard/components/over-view.less index 0285c757..e3b421ef 100644 --- a/src/pages/dashboard/components/over-view.less +++ b/src/pages/dashboard/components/over-view.less @@ -1,11 +1,12 @@ :local(.card-body) { + box-shadow: none !important; + :global(.ant-card-body) { height: 110px; display: flex; justify-content: space-around; - // box-shadow: var(--box-shadow-base); border-radius: var(--ant-border-radius-lg); - // border: 1px solid var(--ant-color-border); + border: 1px solid var(--color-border-1); } } diff --git a/src/pages/dashboard/components/over-view.tsx b/src/pages/dashboard/components/over-view.tsx index edde0489..ac4a01d4 100644 --- a/src/pages/dashboard/components/over-view.tsx +++ b/src/pages/dashboard/components/over-view.tsx @@ -15,7 +15,7 @@ const renderCardItem = (data: { const { label, value, bgColor } = data; return ( diff --git a/src/pages/dashboard/components/resource-utilization.tsx b/src/pages/dashboard/components/resource-utilization.tsx index fcd06b82..a06d9250 100644 --- a/src/pages/dashboard/components/resource-utilization.tsx +++ b/src/pages/dashboard/components/resource-utilization.tsx @@ -1,89 +1,174 @@ -import LineChart from '@/components/charts/line-chart'; +import Chart from '@/components/echarts/chart'; import { getLocale, useIntl } from '@umijs/max'; import dayjs from 'dayjs'; import _ from 'lodash'; import { useCallback, useContext, useEffect, useState } from 'react'; import { DashboardContext } from '../config/dashboard-context'; +const chartColorMap = { + tickLineColor: 'rgba(217,217,217,0.5)', + axislabelColor: 'rgba(0, 0, 0, 0.4)' +}; + const TypeKeyMap = { cpu: { label: 'CPU', type: 'CPU', intl: false, - color: - 'linear-gradient(90deg,rgba(84, 204, 152,0.8) 0%,rgba(0, 168, 143,.7) 100%)' + color: 'rgba(250, 173, 20,.8)' }, memory: { label: 'dashboard.memory', type: 'Memory', intl: true, - color: - 'linear-gradient(90deg,rgba(249, 248, 113,.8) 0%,rgba(255, 199, 92,0.7) 100%)' + color: 'rgba(114, 46, 209,.8)' }, gpu: { label: 'GPU', type: 'GPU', intl: false, - color: 'rgba(84, 204, 152,0.8)' + color: 'rgba(84, 204, 152,.8)' }, gpu_memory: { label: 'dashboard.vram', type: 'VRAM', intl: true, - color: - 'linear-gradient(90deg,rgba(84, 204, 152,0.8) 0%,rgba(0, 168, 143,.7) 100%)' + color: 'rgba(255, 107, 179, 80%)' } }; +const option = { + title: { + text: '' + }, + legend: { + itemWidth: 8, + itemHeight: 8, + data: [] + }, + grid: { + left: 0, + right: 20, + bottom: 20, + containLabel: true + }, + tooltip: { + trigger: 'axis', + formatter(params: any) { + let result = `${params[0].axisValue}`; + params.forEach((item: any) => { + result += ` + + + ${item.seriesName}: + + ${item.data.value} + `; + }); + return `
${result}
`; + } + }, + xAxis: { + type: 'category', + boundaryGap: true, + axisTick: { + show: true, + lineStyle: { + color: chartColorMap.tickLineColor + } + }, + axisLabel: { + color: chartColorMap.axislabelColor, + // fontFamily: 'unset', + fontSize: 12 + }, + axisLine: { + show: false + }, + data: [] + }, + yAxis: { + max: 100, + min: 0, + splitLine: { + show: true, + lineStyle: { + type: 'dashed' + } + }, + axisLabel: { + color: chartColorMap.axislabelColor, + // fontFamily: 'unset', + fontSize: 12 + }, + axisTick: { + show: false + }, + type: 'value' + }, + series: [] +}; + const UtilizationOvertime: React.FC = () => { const intl = useIntl(); const locale = getLocale(); + const [dataOptions, setDataOptions] = useState(option); const data = useContext(DashboardContext)?.system_load?.history || {}; - const [result, setResult] = useState< - { time: string; value: number; type: string }[] - >([]); const typeList = ['gpu', 'cpu', 'memory', 'gpu_memory']; - const sliderConfig = { - y: false, - x: { - style: { - selectionFill: 'rgb(84, 204, 152)', - selectionFillOpacity: 0.1, - handleIconFill: 'rgb(84, 204, 152)', - handleIconFillOpacity: 0.15, - handleIconStrokeOpacity: 0, - sparklineType: 'line', - sparkline: true - }, - sparkline: true - } - }; const labelFormatter = (value: any) => { return `${value}%`; }; + const generateData = useCallback(() => { - const list: { value: number; time: string; type: string }[] = []; - _.each(typeList, (type: any) => { - const dataList = _.map(_.get(data, type, []), (item: any) => { - const value = _.round(_.get(item, 'value', 0), 1); - const time = dayjs(item.timestamp * 1000).format('HH:mm:ss'); - const itemtype = _.get(TypeKeyMap, [type, 'intl'], false) - ? intl.formatMessage({ - id: _.get(TypeKeyMap, [type, 'label'], '') - }) - : _.get(TypeKeyMap, [type, 'label'], ''); - return { - value, - time, - type: itemtype, - color: 'red' - }; - }); - list.push(...dataList); + const legendData: string[] = []; + const xAxisData: string[] = []; + let list: { value: number; time: string; type: string }[] = []; + + list = _.map(typeList, (item: string) => { + const itemConfig = _.get(TypeKeyMap, item, {}); + console.log('itemConfig', itemConfig); + const name = itemConfig.intl + ? intl.formatMessage({ id: itemConfig.label }) + : itemConfig.label; + legendData.push(name); + const itemDataList = _.get(data, item, []); + return { + type: 'line', + smooth: true, + showSymbol: false, + itemStyle: { + color: itemConfig.color + }, + lineStyle: { + width: 1.5, + opacity: 0.7, + color: itemConfig.color + }, + name: name, + data: _.map(itemDataList, (item: any) => { + xAxisData.push(dayjs(item.timestamp * 1000).format('HH:mm:ss')); + return { + time: dayjs(item.timestamp * 1000).format('HH:mm:ss'), + value: _.round(_.get(item, 'value', 0), 1) + }; + }) + }; + }); + + setDataOptions({ + ...option, + legend: { + ...option.legend, + data: legendData + }, + xAxis: { + ...option.xAxis, + data: _.uniq(xAxisData) + }, + series: list }); - setResult(list); }, [data, locale]); useEffect(() => { @@ -92,13 +177,7 @@ const UtilizationOvertime: React.FC = () => { return ( <> - + ); }; diff --git a/src/pages/dashboard/components/usage.tsx b/src/pages/dashboard/components/usage.tsx index a320d094..00189d58 100644 --- a/src/pages/dashboard/components/usage.tsx +++ b/src/pages/dashboard/components/usage.tsx @@ -1,7 +1,7 @@ import CardWrapper from '@/components/card-wrapper'; -import AreaChart from '@/components/charts/area'; -import ColumnBar from '@/components/charts/column-bar'; -import HBar from '@/components/charts/h-bar'; +import BarChart from '@/components/echarts/bar-chart'; +import HBar from '@/components/echarts/h-bar'; +import LineChart from '@/components/echarts/line-chart'; import PageTools from '@/components/page-tools'; import breakpoints from '@/config/breakpoints'; import useWindowResize from '@/hooks/use-window-resize'; @@ -13,7 +13,14 @@ import _ from 'lodash'; import { useContext, useEffect, useState } from 'react'; import { DashboardContext } from '../config/dashboard-context'; -const { RangePicker } = DatePicker; +const baseColorMap = { + baseL2: 'rgba(13,171,219,0.8)', + baseL1: 'rgba(0,34,255,0.8)', + base: 'rgba(0,123,255,0.8)', + baseR1: 'rgba(0,255,233,0.8)', + baseR2: 'rgba(48,0,255,0.8)', + baseR3: 'rgba(85,167,255,0.8)' +}; const times = [ 'june 1', 'june 2', @@ -94,15 +101,15 @@ const tokenUsage = TokensData.map((val, i) => { const getCurrentMonthDays = () => { const now = dayjs(); - const firstDayOfMonth = now.startOf('month'); - const dateRange = []; - let currentDate = firstDayOfMonth; + const daysInMonth = now.daysInMonth(); + const year = dayjs().year(); + const month = dayjs().month() + 1; - while (currentDate.isBefore(now) || currentDate.isSame(now, 'day')) { - dateRange.push(currentDate.format('YYYY-MM-DD')); - currentDate = currentDate.add(1, 'day'); + const dates = []; + for (let day = 1; day <= daysInMonth; day++) { + dates.push(dayjs(`${year}-${month}-${day}`).format('YYYY-MM-DD')); } - return dateRange; + return dates; }; const Usage = () => { @@ -110,42 +117,64 @@ const Usage = () => { const { size } = useWindowResize(); const [paddingRight, setPaddingRight] = useState('20px'); const [requestData, setRequestData] = useState< - { time: string; value: number }[] + { + name: string; + color: string; + areaStyle: any; + data: { time: string; value: number }[]; + }[] >([]); + + const currentMonthDays = getCurrentMonthDays(); const [tokenData, setTokenData] = useState<{ time: string; value: number }[]>( [] ); const [userData, setUserData] = useState<{ name: string; value: number }[]>( [] ); - const [dateRange, setDateRange] = useState(getCurrentMonthDays()); + const [xAxisData, setXAxisData] = useState(currentMonthDays); + const [dateRange, setDateRange] = useState(currentMonthDays); + const [topUserList, setTopUseList] = useState([]); const data = useContext(DashboardContext)?.model_usage || {}; - console.log('dateRange', dateRange); const handleSelectDate = (dateString: string) => {}; const generateData = () => { - const requestList: { time: string; value: number }[] = []; - const tokenList: { - time: string; - value: number; + const requestList: { name: string; color: string; - }[] = []; - const userList: { - name: string; - value: number; - type: string; - color: string; - }[] = []; + areaStyle: any; + data: { time: string; value: number }[]; + } = { + name: 'API Request', + areaStyle: {}, + color: baseColorMap.base, + data: [] + }; - _.each(data.api_request_history, (item: any) => { - requestList.push({ - time: dayjs(item.timestamp * 1000).format('YYYY-MM-DD'), - value: item.value - }); - }); + const completionData: any = { + name: 'completion_token', + color: baseColorMap.base, + data: [] + }; + const prompData: any = { + name: 'prompt_token', + color: baseColorMap.baseR3, + data: [] + }; + + const topUserPrompt: any = { + name: 'prompt_token', + color: baseColorMap.baseR3, + data: [] + }; + const topUserCompletion: any = { + name: 'completion_token', + color: baseColorMap.base, + data: [] + }; + const users: string[] = []; _.each(dateRange, (date: string) => { // tokens data @@ -153,18 +182,14 @@ const Usage = () => { return dayjs(item.timestamp * 1000).format('YYYY-MM-DD') === date; }); if (!item) { - tokenList.push({ - time: date, - name: 'completion_token', - color: 'rgba(84, 204, 152,0.8)', + completionData.data.push({ + titme: date, value: 0 }); } else { - tokenList.push({ - time: dayjs(item.timestamp * 1000).format('YYYY-MM-DD'), - name: 'completion_token', - color: 'rgba(84, 204, 152,0.8)', - value: item.value + completionData.data.push({ + value: item.value, + time: dayjs(item.timestamp * 1000).format('YYYY-MM-DD') }); } @@ -172,57 +197,58 @@ const Usage = () => { return dayjs(item.timestamp * 1000).format('YYYY-MM-DD') === date; }); if (!promptItem) { - tokenList.push({ - time: date, - name: 'prompt_token', - color: 'rgba(0, 170, 173, 0.8)', - value: 0 + prompData.data.push({ + value: 0, + time: date }); } else { - tokenList.push({ - time: dayjs(promptItem.timestamp * 1000).format('YYYY-MM-DD'), - name: 'prompt_token', - color: 'rgba(0, 170, 173, 0.8)', - value: promptItem.value + prompData.data.push({ + value: promptItem.value, + time: dayjs(promptItem.timestamp * 1000).format('YYYY-MM-DD') }); } - // api request data + // ============== api request data ================= const requestItem = _.find(data.api_request_history, (item: any) => { return dayjs(item.timestamp * 1000).format('YYYY-MM-DD') === date; }); if (!requestItem) { - requestList.push({ + requestList.data.push({ time: date, value: 0 }); } else { - requestList.push({ + requestList.data.push({ time: dayjs(requestItem.timestamp * 1000).format('YYYY-MM-DD'), value: requestItem.value }); } }); + // ========== top users ============ + _.each(data.top_users, (item: any) => { - userList.push({ + users.push(item.username); + topUserPrompt.data.push({ name: item.username, - type: 'completion_token', - color: 'rgba(84, 204, 152,0.8)', - value: item.completion_token_count - }); - userList.push({ - name: item.username, - type: 'prompt_token', - color: 'rgba(0, 170, 173, 0.8)', value: item.prompt_token_count }); + topUserCompletion.data.push({ + name: item.username, + value: item.completion_token_count + }); }); - setRequestData(requestList); - setTokenData(tokenList); - setUserData(userList); + console.log('usage=============', { + requestList, + xAxisData + }); + + setRequestData([requestList]); + setUserData([topUserCompletion, topUserPrompt]); + setTopUseList(users); + setTokenData([completionData, prompData]); }; const labelFormatter = (v: any) => { @@ -270,28 +296,25 @@ const Usage = () => { - + > - + > @@ -300,13 +323,8 @@ const Usage = () => { diff --git a/src/pages/llmodels/components/add-modal.tsx b/src/pages/llmodels/components/add-modal.tsx index 4c3cc2e6..b67cb6e0 100644 --- a/src/pages/llmodels/components/add-modal.tsx +++ b/src/pages/llmodels/components/add-modal.tsx @@ -100,7 +100,7 @@ const AddModal: React.FC = (props) => { const handleOnSearchRepo = async (text: string) => { try { const params = { - q: `${text} gguf`, + q: `${text}`, type: 'model' }; const res = await callHuggingfaceQuickSearch(params); diff --git a/src/pages/llmodels/index.tsx b/src/pages/llmodels/index.tsx index fa06e047..3d6830cf 100644 --- a/src/pages/llmodels/index.tsx +++ b/src/pages/llmodels/index.tsx @@ -162,9 +162,6 @@ const Models: React.FC = () => { _.each(list, (data: any) => { updateChunkedList(data); }); - if (!dataSource.length) { - handleFilter(); - } }; const createModelsChunkRequest = () => { @@ -367,7 +364,10 @@ const Models: React.FC = () => { - + {item.state && ( { }} > { title={intl.formatMessage({ id: 'models.form.replicas' })} dataIndex="replicas" key="replicas" + align="center" span={4} /> = (props) => { }, { enabled: !disabled } ); - useHotkeys( - HotKeys.CREATE.join(','), - (e) => { - onNewMessage(); - }, - { enabled: !disabled } - ); return (
@@ -59,7 +52,6 @@ const ChatFooter: React.FC = (props) => { onClick={onNewMessage} > {intl.formatMessage({ id: 'playground.newMessage' })} - CTRL+SHIFT+ N name="temperature" diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx index cf980e0d..b6eeddb2 100644 --- a/src/pages/profile/index.tsx +++ b/src/pages/profile/index.tsx @@ -1,4 +1,3 @@ -import CardWrapper from '@/components/card-wrapper'; import FormButtons from '@/components/form-buttons'; import SealInput from '@/components/seal-form/seal-input'; import { PasswordReg } from '@/config'; @@ -18,11 +17,12 @@ const Profile: React.FC = () => { const [form] = Form.useForm(); const intl = useIntl(); - const newpasswordValue = Form.useWatch('new_password', form); - const handleOnFinish = async (values: any) => { try { - await updatePassword(values); + await updatePassword({ + new_password: values.new_password, + current_password: values.current_password + }); message.success(intl.formatMessage({ id: 'common.message.success' })); } catch (error) {} }; @@ -43,20 +43,14 @@ const Profile: React.FC = () => { }} extra={[]} > - -
- {/* + {/* name="username" rules={[ { @@ -76,53 +70,80 @@ const Profile: React.FC = () => { style={{ width: INPUT_WIDTH.default }} > */} - - name="current_password" - rules={[ - { - required: true, - message: intl.formatMessage( - { id: 'common.form.rule.input' }, - { - name: intl.formatMessage({ - id: 'users.form.currentpassword' - }) - } - ) + + name="current_password" + rules={[ + { + required: true, + message: intl.formatMessage( + { id: 'common.form.rule.input' }, + { + name: intl.formatMessage({ + id: 'users.form.currentpassword' + }) + } + ) + } + ]} + > + + + + name="new_password" + rules={[ + { + required: true, + pattern: PasswordReg, + message: intl.formatMessage({ + id: 'users.form.rule.password' + }) + } + ]} + > + + + ({ + validator(_, value) { + if (!value || getFieldValue('new_password') === value) { + return Promise.resolve(); + } + return Promise.reject( + new Error( + intl.formatMessage({ id: 'users.password.confirm.error' }) + ) + ); } - ]} - > - - - - name="new_password" - rules={[ - { - required: true, - pattern: PasswordReg, - message: intl.formatMessage({ - id: 'users.form.rule.password' - }) - } - ]} - > - - - - -
+ }) + ]} + > + + + + ); diff --git a/src/pages/resources/components/workers.tsx b/src/pages/resources/components/workers.tsx index c0d636e7..12addb7e 100644 --- a/src/pages/resources/components/workers.tsx +++ b/src/pages/resources/components/workers.tsx @@ -146,7 +146,7 @@ const Models: React.FC = () => { }} > @@ -211,7 +211,7 @@ const Models: React.FC = () => { key="GPU" render={(text, record: ListItem) => { return ( - + {record?.status?.gpu_devices.map((item, index) => { return ( diff --git a/src/pages/users/components/add-modal.tsx b/src/pages/users/components/add-modal.tsx index 0ae0c31a..eb37da84 100644 --- a/src/pages/users/components/add-modal.tsx +++ b/src/pages/users/components/add-modal.tsx @@ -3,11 +3,7 @@ import SealInput from '@/components/seal-form/seal-input'; import SealSelect from '@/components/seal-form/seal-select'; import { PageAction, PasswordReg } from '@/config'; import { PageActionType } from '@/config/types'; -import { - SyncOutlined, - UserOutlined, - UserSwitchOutlined -} from '@ant-design/icons'; +import { UserOutlined, UserSwitchOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Form, Modal, Select } from 'antd'; import { useEffect } from 'react'; @@ -32,14 +28,7 @@ const AddModal: React.FC = ({ }) => { const [form] = Form.useForm(); const intl = useIntl(); - const suffix = ( - - ); + const initFormValue = () => { if (action === PageAction.EDIT && open) { form.setFieldsValue({ diff --git a/src/pages/users/index.tsx b/src/pages/users/index.tsx index 67d90ad2..ba419b88 100644 --- a/src/pages/users/index.tsx +++ b/src/pages/users/index.tsx @@ -113,7 +113,6 @@ const Models: React.FC = () => { }; const handleModalOk = async (data: FormData) => { - console.log('handleModalOk'); const params = { ...data, is_admin: data.is_admin === 'admin' @@ -131,7 +130,7 @@ const Models: React.FC = () => { } fetchData(); setOpenAddModal(false); - message.success(''); + message.success(intl.formatMessage({ id: 'common.message.success' })); } catch (error) { setOpenAddModal(false); } @@ -261,22 +260,9 @@ const Models: React.FC = () => { }} > - { - return dayjs(text).format('YYYY-MM-DD HH:mm:ss'); - }} /> { }} />