From 4c24faafe921791c9d44a4348f3a79e254864c31 Mon Sep 17 00:00:00 2001 From: cp3hnu Date: Sat, 19 Oct 2024 16:45:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=8C=87=E6=A0=87=E5=AF=B9=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/assets/img/metrics-title-icon.png | Bin 0 -> 536 bytes react-ui/src/assets/img/model-metrics.png | Bin 0 -> 1224 bytes react-ui/src/components/BasicInfo/index.less | 80 +++--- react-ui/src/components/BasicInfo/index.tsx | 106 ++++--- .../src/components/BasicTableInfo/index.less | 64 +++++ .../src/components/BasicTableInfo/index.tsx | 43 +++ .../components/ResourceInfo/index.less | 10 +- .../Dataset/components/ResourceInfo/index.tsx | 11 +- .../components/ResourceIntro/index.less | 27 +- .../components/ResourceIntro/index.tsx | 134 ++++----- .../components/ResourceVersion/index.less | 5 + .../components/ResourceVersion/index.tsx | 2 +- .../src/pages/Experiment/Comparison/index.tsx | 6 +- .../Experiment/components/LogGroup/index.tsx | 8 +- .../Experiment/components/LogList/index.tsx | 3 +- .../Model/components/MetricsChart/index.less | 29 ++ .../Model/components/MetricsChart/index.tsx | 174 ++++++++++++ .../Model/components/MetricsChart/tooltip.css | 33 +++ .../components/ModelEvolution/index.less | 7 +- .../Model/components/ModelMetrics/index.less | 35 +++ .../Model/components/ModelMetrics/index.tsx | 259 ++++++++++++++++++ react-ui/src/services/dataset/index.js | 16 ++ react-ui/src/services/experiment/index.js | 7 +- 23 files changed, 897 insertions(+), 162 deletions(-) create mode 100644 react-ui/src/assets/img/metrics-title-icon.png create mode 100644 react-ui/src/assets/img/model-metrics.png create mode 100644 react-ui/src/components/BasicTableInfo/index.less create mode 100644 react-ui/src/components/BasicTableInfo/index.tsx create mode 100644 react-ui/src/pages/Model/components/MetricsChart/index.less create mode 100644 react-ui/src/pages/Model/components/MetricsChart/index.tsx create mode 100644 react-ui/src/pages/Model/components/MetricsChart/tooltip.css create mode 100644 react-ui/src/pages/Model/components/ModelMetrics/index.less create mode 100644 react-ui/src/pages/Model/components/ModelMetrics/index.tsx diff --git a/react-ui/src/assets/img/metrics-title-icon.png b/react-ui/src/assets/img/metrics-title-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..66cd461fa4ccfbd764bcf2d147d02ee2e091b62c GIT binary patch literal 536 zcmV+z0_XjSP)?=hv?0&#*I3M%G z*kb9l0@XTg(i2;(n2$bAmPgae$rMoIDwJHy;{t9IG-UvkNCdcxT7nZ$`!Yl2%z1D{{Lde&D|?9ww+&vy!@7$PGj714eu_KObX#c|-KQdTF1>|MHCRKJBg= zFteK7l+xS%JdA%2J9Wa;^_wo}E$`Kl5!c4;Q>1N?c(Mv^;5h$|DS#fvE<`ZLFtlJ8 zMqw9*VZaC*f!&!E{^6hhlKltwS`TAOZ;`gCiOh06VRL&h) zzP?T&)&tq9i7yEw`h+jEu92q)Yhfg>{h%ucKB$FpC(uRP+d+TEzZ9wMtYaEvfhHI> zB;bP}!yJh#pM$Nhx1+4oGxY||>%e`c!MqW~{}0dzk9T3r^_F6~i9sI*kx{tFG-s1M zpKeyrMS*M&#AXG}8KkF&M#1zPn7)|Dv`k+ASSl~pR4X(hd*J25n2s>x$nE>vDUX?G z1k+{nbhElgS}Ecou}=u*&R=L)Qa_V zJ5Tc|!#pF_a@;CBxfInN&H8fTJdG=c2##8urz7n=-KacmGCgWCeYPb^({?@WbZX!6 zm&;D&>BJiSLEnn>cCRYRmZn*vx53o!+f`N#(@3jJ4=2QYC2N%RX(${L9dxY{g+Ln^Hcu{R zu!Gzk7|R1KV$rdsOGo)wQ}t*B(`5s(H9`M<;q=mvZyG^V?Wi}?=B4@_;4!)c#{IPa z^{b1r??Au+scmJ*-Svsc+*icvOs>;JuFfsF>xG-LGmzxR3g!0hRw>X0-K%6OMU%;7 z%+Tl5`P$f^DV7&DkMT{vboxPd`9i>3qPtbpy)uiR{R|iZcG+P) ztBI78Tiq_BCFgtN)=7HixlbzwYM+oz$>nIT%Lz1qjRV+df&MjQ0;N>D4glv+I~%*r z-7OUV3Ytn$W2Y@e=(MU{H-+(^Af@jQh{r4byhXZv3$oLfFeYupRLZ7y+2vww?kkQ5 zC(x!}jsu8l&B6CDceYIT;EZK&eC9e|PDJK)Y-?iLoXo~|c!&dsgXV1foCw!LZyrJQ mR>=AoI4)PzTHmgvu>AvCc%NYt4@D9H0000 string | BasicInfoLink | BasicInfoLink[] | undefined; }; @@ -18,45 +20,73 @@ type BasicInfoProps = { datas: BasicInfoData[]; className?: string; style?: React.CSSProperties; - labelWidth?: number; + labelWidth: number; }; -function BasicInfo({ datas, className, style, labelWidth = 100 }: BasicInfoProps) { +type BasicInfoItemProps = { + data: BasicInfoData; + labelWidth: number; + classPrefix: string; +}; + +type BasicInfoItemValueProps = BasicInfoLink & { + ellipsis?: boolean; + classPrefix: string; +}; + +export default function BasicInfo({ datas, className, style, labelWidth }: BasicInfoProps) { return (
{datas.map((item) => ( - + ))}
); } -type BasicInfoItemProps = { - data: BasicInfoData; - labelWidth?: number; -}; -function BasicInfoItem({ data, labelWidth = 100 }: BasicInfoItemProps) { - const { label, value, format } = data; +export function BasicInfoItem({ data, labelWidth, classPrefix }: BasicInfoItemProps) { + const { label, value, format, ellipsis } = data; const formatValue = format ? format(value) : value; + const myClassName = `${classPrefix}__item`; let valueComponent = undefined; if (Array.isArray(formatValue)) { valueComponent = ( -
+
{formatValue.map((item: BasicInfoLink) => ( - + ))}
); } else if (typeof formatValue === 'object' && formatValue) { valueComponent = ( - + ); } else { - valueComponent = ; + valueComponent = ( + + ); } return ( -
-
+
+
{label}
{valueComponent} @@ -64,35 +94,39 @@ function BasicInfoItem({ data, labelWidth = 100 }: BasicInfoItemProps) { ); } -type BasicInfoItemValueProps = { - value: string; - link?: string; - url?: string; -}; - -function BasicInfoItemValue({ value, link, url }: BasicInfoItemValueProps) { +export function BasicInfoItemValue({ + value, + link, + url, + ellipsis, + classPrefix, +}: BasicInfoItemValueProps) { + const myClassName = `${classPrefix}__item__value`; + let component = undefined; if (url && value) { - return ( - + component = ( + {value} ); } else if (link && value) { - return ( - + component = ( + {value} ); } else { - return ( -
{value ?? '--'}
- ); + component = {value ?? '--'}; } -} -export default BasicInfo; + return ( + + {component} + + ); +} diff --git a/react-ui/src/components/BasicTableInfo/index.less b/react-ui/src/components/BasicTableInfo/index.less new file mode 100644 index 00000000..cc3d0984 --- /dev/null +++ b/react-ui/src/components/BasicTableInfo/index.less @@ -0,0 +1,64 @@ +.kf-basic-table-info { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: stretch; + width: 100%; + border: 1px solid @border-color-base; + border-bottom: none; + border-radius: 4px; + + &__item { + display: flex; + align-items: stretch; + width: 25%; + border-bottom: 1px solid @border-color-base; + + &__label { + flex: none; + padding: 12px 20px; + color: @text-color-secondary; + font-size: 14px; + text-align: left; + background-color: .addAlpha(#606b7a, 0.05) []; + } + + &__value-container { + display: flex; + flex: 1; + flex-direction: column; + align-items: flex-start; + min-width: 0; + } + + &__value { + flex: 1; + margin: 0 !important; + padding: 12px 20px 4px; + font-size: @font-size; + white-space: pre-line; + word-break: break-all; + + & + & { + padding-top: 0; + } + + &:last-child { + padding-bottom: 12px; + } + + &--ellipsis { + .singleLine(); + } + + &__text { + color: @text-color; + } + + &__link:hover { + text-decoration: underline @underline-color; + text-underline-offset: 3px; + } + } + } +} diff --git a/react-ui/src/components/BasicTableInfo/index.tsx b/react-ui/src/components/BasicTableInfo/index.tsx new file mode 100644 index 00000000..df167ae2 --- /dev/null +++ b/react-ui/src/components/BasicTableInfo/index.tsx @@ -0,0 +1,43 @@ +import classNames from 'classnames'; +import { BasicInfoItem, type BasicInfoData, type BasicInfoLink } from '../BasicInfo'; +import './index.less'; +export type { BasicInfoData, BasicInfoLink }; + +type BasicTableInfoProps = { + datas: BasicInfoData[]; + className?: string; + style?: React.CSSProperties; + labelWidth: number; +}; + +export default function BasicTableInfo({ + datas, + className, + style, + labelWidth, +}: BasicTableInfoProps) { + const remainder = datas.length % 4; + const array = []; + if (remainder > 0) { + for (let i = 0; i < 4 - remainder; i++) { + array.push({ + label: '', + value: '', + }); + } + } + const showDatas = [...datas, ...array]; + + return ( +
+ {showDatas.map((item) => ( + + ))} +
+ ); +} diff --git a/react-ui/src/pages/Dataset/components/ResourceInfo/index.less b/react-ui/src/pages/Dataset/components/ResourceInfo/index.less index 6103b602..8cbba3d6 100644 --- a/react-ui/src/pages/Dataset/components/ResourceInfo/index.less +++ b/react-ui/src/pages/Dataset/components/ResourceInfo/index.less @@ -38,10 +38,6 @@ &__bottom { position: relative; height: calc(100% - 135px); - padding: 8px 30px 20px; - background: #ffffff; - border-radius: 10px; - box-shadow: 0px 2px 12px rgba(180, 182, 191, 0.09); &__legend { position: absolute; @@ -52,6 +48,12 @@ :global { .ant-tabs { height: 100%; + .ant-tabs-nav-wrap { + padding-top: 8px; + padding-left: 30px; + background-color: white; + border-radius: 10px 10px 0 0; + } .ant-tabs-content-holder { height: 100%; .ant-tabs-content { diff --git a/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx b/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx index fe1e9a86..159ba9f1 100644 --- a/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx +++ b/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx @@ -164,7 +164,16 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { key: ResourceInfoTabKeys.Introduction, label: `${typeName}简介`, icon: , - children: , + children: ( + + ), }, { key: ResourceInfoTabKeys.Version, diff --git a/react-ui/src/pages/Dataset/components/ResourceIntro/index.less b/react-ui/src/pages/Dataset/components/ResourceIntro/index.less index 57d40216..6ac9223b 100644 --- a/react-ui/src/pages/Dataset/components/ResourceIntro/index.less +++ b/react-ui/src/pages/Dataset/components/ResourceIntro/index.less @@ -1,10 +1,25 @@ .resource-intro { width: 100%; - margin-top: 24px; - &__basic { - width: 100%; - } - &__usage { - width: 100%; + + &__top { + padding: 20px 30px; + background: white; + border-radius: 0 0 10px 10px; + box-shadow: 0px 2px 12px rgba(180, 182, 191, 0.09); + + pre { + margin-bottom: 0 !important; + } + + &__title { + margin: 15px 0; + color: @text-color-secondary; + font-size: 14px; + } + + &__desc { + color: @text-color; + font-size: @font-size; + } } } diff --git a/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx b/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx index 2ee7fb24..0aa3b7e3 100644 --- a/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx +++ b/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx @@ -1,4 +1,4 @@ -import BasicInfo, { BasicInfoData } from '@/components/BasicInfo'; +import BasicTableInfo, { BasicInfoData } from '@/components/BasicTableInfo'; import SubAreaTitle from '@/components/SubAreaTitle'; import { ResourceInfoTabKeys } from '@/pages/Dataset/components/ResourceInfo'; import { @@ -8,13 +8,19 @@ import { ProjectDependency, ResourceType, TrainTask, + resourceConfig, } from '@/pages/Dataset/config'; +import ModelMetrics from '@/pages/Model/components/ModelMetrics'; import { getGitUrl } from '@/utils'; import styles from './index.less'; type ResourceIntroProps = { resourceType: ResourceType; info: DatasetData | ModelData; + resourceId: number; + identifier: string; + owner: string; + version?: string; }; const formatDataset = (datasets?: DatasetData[]) => { @@ -27,29 +33,6 @@ const formatDataset = (datasets?: DatasetData[]) => { })); }; -const formatParams = (map?: Record, space: string = '') => { - if (!map || Object.keys(map).length === 0) { - return undefined; - } - return Object.entries(map) - .map(([key, value]) => `${space}${key} : ${value}`) - .join('\n'); -}; - -const formatMetrics = (map?: Record) => { - if (!map || Object.keys(map).length === 0) { - return undefined; - } - return Object.entries(map) - .map(([key, value]) => { - if (typeof value === 'object' && value !== null) { - return `${key} : \n${formatParams(value, ' ')}`; - } - return `${key} : ${value}`; - }) - .join('\n'); -}; - const getProjectUrl = (project?: ProjectDependency) => { if (!project || !project.url || !project.branch) { return undefined; @@ -93,49 +76,50 @@ const getDatasetDatas = (data: DatasetData): BasicInfoData[] => [ { label: '数据集名称', value: data.name, + ellipsis: true, }, { label: '版本', value: data.version, + ellipsis: true, }, { label: '创建人', value: data.create_by, + ellipsis: true, }, { label: '更新时间', value: data.update_time, + ellipsis: true, }, { label: '数据来源', value: data.dataset_source, format: formatSource, + ellipsis: true, }, { label: '训练任务', value: data.train_task, format: formatTrainTask, + ellipsis: true, }, { label: '处理代码', value: data.processing_code, format: formatProject, + ellipsis: true, }, { label: '数据集分类', value: data.data_type, + ellipsis: true, }, { label: '研究方向', value: data.data_tag, - }, - { - label: '数据集描述', - value: data.description, - }, - { - label: '版本描述', - value: data.version_desc, + ellipsis: true, }, ]; @@ -143,77 +127,79 @@ const getModelDatas = (data: ModelData): BasicInfoData[] => [ { label: '模型名称', value: data.name, + ellipsis: true, }, { label: '版本', value: data.version, + ellipsis: true, }, { label: '创建人', value: data.create_by, + ellipsis: true, }, { label: '更新时间', value: data.update_time, + ellipsis: true, }, { label: '训练镜像', value: data.image, + ellipsis: true, }, { label: '训练代码', value: data.project_depency, format: formatProject, + ellipsis: true, }, { label: '训练数据集', value: data.train_datasets, format: formatDataset, + ellipsis: true, }, { label: '测试数据集', value: data.test_datasets, format: formatDataset, - }, - { - label: '参数', - value: data.params, - format: formatParams, - }, - { - label: '指标', - value: data.metrics, - format: formatMetrics, + ellipsis: true, }, { label: '模型来源', value: data.model_source, format: formatSource, + ellipsis: true, }, { label: '训练任务', value: data.train_task, format: formatTrainTask, + ellipsis: true, }, { label: '模型框架', value: data.model_type, + ellipsis: true, }, { label: '模型能力', value: data.model_tag, - }, - { - label: '模型描述', - value: data.description, - }, - { - label: '版本描述', - value: data.version_desc, + ellipsis: true, }, ]; -function ResourceIntro({ resourceType, info }: ResourceIntroProps) { +function ResourceIntro({ + resourceType, + info, + resourceId, + identifier, + owner, + version, +}: ResourceIntroProps) { + const config = resourceConfig[resourceType]; const basicDatas: BasicInfoData[] = resourceType === ResourceType.Dataset ? getDatasetDatas(info as DatasetData) @@ -221,23 +207,37 @@ function ResourceIntro({ resourceType, info }: ResourceIntroProps) { return (
- -
- +
+ +
+ +
+
{`${config.name}描述`}
+
{info.description ?? '暂无描述'}
+
版本描述
+
{info.version_desc ?? '暂无描述'}
+ +
- -
+ {resourceType === ResourceType.Model && version && ( + + )}
); } diff --git a/react-ui/src/pages/Dataset/components/ResourceVersion/index.less b/react-ui/src/pages/Dataset/components/ResourceVersion/index.less index d36c5ab6..e40591bd 100644 --- a/react-ui/src/pages/Dataset/components/ResourceVersion/index.less +++ b/react-ui/src/pages/Dataset/components/ResourceVersion/index.less @@ -1,4 +1,9 @@ .resource-version { + min-height: 100%; + padding: 20px 30px; color: @text-color; font-size: @font-size-content; + background: white; + border-radius: 0 0 10px 10px; + box-shadow: 0px 2px 12px rgba(180, 182, 191, 0.09); } diff --git a/react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx b/react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx index 4a6d190c..44c54320 100644 --- a/react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx +++ b/react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx @@ -86,7 +86,7 @@ function ResourceVersion({ resourceType, info }: ResourceVersionProps) { return (
- +