From 42ce67062f23239a235f714d7f6b2c50fcc38444 Mon Sep 17 00:00:00 2001 From: cp3hnu Date: Tue, 22 Oct 2024 09:47:17 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=E5=AE=9E=E9=AA=8C=E5=AF=B9?= =?UTF-8?q?=E6=AF=94=E6=B7=BB=E5=8A=A0=E4=B8=8A=E6=8B=89=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E6=9B=B4=E5=A4=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- react-ui/.npmrc | 1 + react-ui/package.json | 4 +- react-ui/src/assets/img/robot.png | Bin 1777 -> 15171 bytes react-ui/src/components/KFModal/index.less | 2 +- react-ui/src/overrides.less | 2 +- .../pages/Experiment/Comparison/index.less | 29 ++- .../src/pages/Experiment/Comparison/index.tsx | 102 ++++---- .../components/AddExperimentModal/index.tsx | 2 +- react-ui/src/pages/Workspace/index.less | 10 +- react-ui/src/services/typings.d.ts | 223 ++++++++++++++++++ 10 files changed, 319 insertions(+), 56 deletions(-) create mode 100644 react-ui/.npmrc diff --git a/react-ui/.npmrc b/react-ui/.npmrc new file mode 100644 index 00000000..dd026c83 --- /dev/null +++ b/react-ui/.npmrc @@ -0,0 +1 @@ +save-prefix=~ diff --git a/react-ui/package.json b/react-ui/package.json index d5c09dbd..aebf21ca 100644 --- a/react-ui/package.json +++ b/react-ui/package.json @@ -60,7 +60,7 @@ "@antv/hierarchy": "^0.6.12", "@types/crypto-js": "^4.2.2", "@umijs/route-utils": "^4.0.1", - "antd": "^5.4.4", + "antd": "~5.21.4", "classnames": "^2.3.2", "crypto-js": "^4.2.0", "echarts": "^5.5.0", @@ -111,7 +111,7 @@ "umi-presets-pro": "^2.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.14.0" }, "create-umi": { "ignoreScript": [ diff --git a/react-ui/src/assets/img/robot.png b/react-ui/src/assets/img/robot.png index c1379ef35055ce861d41a5ae240da54982d6dac2..9e179741cce50d8e436f887cd6ac25a777e99ae5 100644 GIT binary patch literal 15171 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 literal 1777 zcmah}>p#>9AN~Hu%ot?zWXZVH)FXtXafwJnD7g=w2{VjHt)-#C} zY0@f*R1CISw;{aRt#X0A5-ki_*oEInA#p#5sw6Zh+$l9H>rfwWj&dB0fhW_@_lId`ZzW=4-) zzwpvPOZiv%Ra7Hnk6qMCdJkQt@9?p{zTCd6t;Npin{A)vcCE`a6eRRg!U=G=eiBJS{q@|Ix=*5(eUrsb8u~9ar~^#{tII%rC9Mk?R5_%?Ka5AGkUDT$u z$ilFZ#+q$F=T4hwBLBw(E24f`W$}3q)%9)PgZjXR+=W+;yj?V%qjhaN;4!Plq3%Kbh4x?q|*FO9;o`e3;?xFvm?g#aCN051hR?@@JJ=AX#WV zG^{qsd{_UB%ZmgM9pKaOFgdMAC%iac$crRN=*R}JQK!9o>CASP&W1`qax$chj(?jH>ef8)FrM9)diCLX36k=>r(E#C8ft!f z?(z^t0jii|N8_`d1q$i1>JLp~FoTV?Z};5d_uj>$?z*BkzKf|vbZ_dd7Ypc!sbe=} zjJRn%?R&1ZReDWY|E=`krIVAbO%LY8SV@NsCri_!}`_AfQ;s7)&`VLY4 z`pyaGh@ohwY6c@93sg&c#PA=}voa96w@Ay)Ok!p@du($uvh+Xze7Bnl`Ib-Qi#tP0`l{ zr9WJZ6f*;m_~9TM_S_X9{d}z6d8d!9yE(^5-v{dtnPcCikZkmx+&4NO!x zDpS(OrKHz9$Tpc{epP7A{u7MXSvd6YQMSglUt4;$4ticc^6D6bW{m)a6wY&&1PV|@ zDK~^qPPuLsds?Rpz=a3KO8rH%quJwQq|o=l25c-)@)0jgODnf0_sf8RuUFxR#93eb-l0wrtZpcS$Gk-5U<)!?wlJW=x`(^buIS@KMKN@8;GvU|! zqXilLWm)6yHgMot_DDfV<K1i~e*l%IaL76%zP>40!Aqb7ZW00=-TUAs6NA%$Zq@CRls=!n2{^xviu;E$Xu z?F!{Y5vI1G3z@xN?r9KbZFAzJ$7VB{BtXuvv*WMjRSy75wp?(N$~`F{oyRA<+36pN z0CsNA$tHyyER%d4ij|&Sx3|L-iBI_i*HEwAl;(ykrZ!hXRxzzvc7A@ GO!zOCIu|$q diff --git a/react-ui/src/components/KFModal/index.less b/react-ui/src/components/KFModal/index.less index b034672d..fafc6f7d 100644 --- a/react-ui/src/components/KFModal/index.less +++ b/react-ui/src/components/KFModal/index.less @@ -20,7 +20,7 @@ height: 40px; padding: 0 30px; font-size: @font-size-content; - border-radius: 10px; + border-radius: 6px; } .ant-btn-default { border-color: transparent; diff --git a/react-ui/src/overrides.less b/react-ui/src/overrides.less index 9e4b34cc..f676890e 100644 --- a/react-ui/src/overrides.less +++ b/react-ui/src/overrides.less @@ -168,7 +168,7 @@ height: 40px; padding: 0 30px; font-size: @font-size-content; - border-radius: 10px; + border-radius: 6px; } .ant-btn-default { border-color: transparent; diff --git a/react-ui/src/pages/Experiment/Comparison/index.less b/react-ui/src/pages/Experiment/Comparison/index.less index b9198e74..273791c4 100644 --- a/react-ui/src/pages/Experiment/Comparison/index.less +++ b/react-ui/src/pages/Experiment/Comparison/index.less @@ -14,10 +14,30 @@ &__table { height: calc(100% - 60px); - padding: 20px 30px 0; + padding: 20px 30px; background-color: white; border-radius: 10px; + &__footer { + display: flex; + align-items: center; + padding-top: 20px; + color: @text-color-secondary; + font-size: 12px; + background-color: white; + + div { + flex: 1; + height: 1px; + background-color: @border-color-base; + } + + p { + flex: none; + margin: 0 8px; + } + } + :global { .ant-table-container { border: none !important; @@ -34,6 +54,13 @@ border-left: none !important; } } + .ant-table-tbody-virtual::after { + border-bottom: none !important; + } + .ant-table-footer { + padding: 0; + border: none !important; + } } } } diff --git a/react-ui/src/pages/Experiment/Comparison/index.tsx b/react-ui/src/pages/Experiment/Comparison/index.tsx index fe09198b..1667a064 100644 --- a/react-ui/src/pages/Experiment/Comparison/index.tsx +++ b/react-ui/src/pages/Experiment/Comparison/index.tsx @@ -1,4 +1,10 @@ -// import { useCacheState } from '@/hooks/pageCacheState'; +/* + * @Author: 赵伟 + * @Date: 2024-10-10 09:55:12 + * @Description: 实验对比 + */ + +import { useDomSize } from '@/hooks'; import { getExpEvaluateInfosReq, getExpMetricsReq, @@ -8,7 +14,6 @@ import { to } from '@/utils/promise'; import tableCellRender, { TableCellValueType } from '@/utils/table'; import { useSearchParams } from '@umijs/max'; import { App, Button, Table, /* TablePaginationConfig,*/ TableProps, Tooltip } from 'antd'; -import classNames from 'classnames'; import { useEffect, useMemo, useState } from 'react'; import ExperimentStatusCell from '../components/ExperimentStatusCell'; import { ComparisonType, comparisonConfig } from './config'; @@ -26,40 +31,57 @@ type TableData = { params: Record; }; +const pageSize = 30; + +// function Footer() { +// return ( +//
+//
+//

我是有底线的

+//
+//
+// ); +// } + function ExperimentComparison() { const [searchParams] = useSearchParams(); const comparisonType = searchParams.get('type') as ComparisonType; const experimentId = searchParams.get('id'); const [tableData, setTableData] = useState([]); - // const [cacheState, setCacheState] = useCacheState(); - // const [total, setTotal] = useState(0); const [selectedRowKeys, setSelectedRowKeys] = useState([]); - // const [loading, setLoading] = useState(false); const { message } = App.useApp(); const config = useMemo(() => comparisonConfig[comparisonType], [comparisonType]); - // const [pagination, setPagination] = useState( - // cacheState?.pagination ?? { - // current: 1, - // pageSize: 10, - // }, - // ); + const [tableRef, { width: tableWidth, height: tableHeight }] = useDomSize( + 0, + 0, + [], + ); + const [loadCompleted, setLoadCompleted] = useState(false); + const [loading, setLoading] = useState(false); // 避免误触发加载更多 useEffect(() => { getComparisonData(); }, [experimentId]); // 获取对比数据列表 - const getComparisonData = async () => { - // setLoading(true); + const getComparisonData = async (offset: string = '') => { const request = comparisonType === ComparisonType.Train ? getExpTrainInfosReq : getExpEvaluateInfosReq; - const [res] = await to(request(experimentId, { offset: '', limit: 50 })); - // setLoading(false); + const [res] = await to(request(experimentId, { offset: offset, limit: pageSize })); if (res && res.data) { - // const { content = [], totalElements = 0 } = res.data; - setTableData(res.data); - // setTotal(totalElements); + setTableData((prev) => [...prev, ...res.data]); + if (res.data.length === 0) { + setLoadCompleted(true); + const ele = document.getElementsByClassName('ant-table-body')[0]; + if (ele) { + const div = document.createElement('div'); + div.className = styles['experiment-comparison__table__footer']; + div.innerHTML = '

我是有底线的

'; + ele.appendChild(div); + } + } } + setLoading(false); }; // 获取对比 url @@ -80,17 +102,10 @@ function ExperimentComparison() { getExpMetrics(); }; - // 分页切换 - // const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter, { action }) => { - // if (action === 'paginate') { - // setPagination(pagination); - // } - // // console.log(pagination, filters, sorter, action); - // }; - // 选择行 const rowSelection: TableProps['rowSelection'] = { type: 'checkbox', + columnWidth: 48, fixed: 'left', selectedRowKeys, onChange: (selectedRowKeys: React.Key[]) => { @@ -98,7 +113,20 @@ function ExperimentComparison() { }, }; - const columns: TableProps['columns'] = useMemo(() => { + const handleTableScroll = (e: React.UIEvent) => { + const target = e.target as HTMLDivElement; + + const { scrollTop, scrollHeight, clientHeight } = target; + + // 实现自动加载更多 + if (!loadCompleted && !loading && scrollHeight - scrollTop - clientHeight <= 0) { + const last = tableData[tableData.length - 1]; + setLoading(true); + getComparisonData(last?.run_id); + } + }; + + const columns: TableProps['columns'] = useMemo(() => { const first: TableData | undefined = tableData[0]; return [ { @@ -192,29 +220,15 @@ function ExperimentComparison() { 可视化对比 -
+
diff --git a/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx b/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx index 4758a165..2b7e80c5 100644 --- a/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx +++ b/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx @@ -126,7 +126,7 @@ function AddExperimentModal({ , - , ]; diff --git a/react-ui/src/pages/Workspace/index.less b/react-ui/src/pages/Workspace/index.less index 300887e9..3fbcc8e8 100644 --- a/react-ui/src/pages/Workspace/index.less +++ b/react-ui/src/pages/Workspace/index.less @@ -47,13 +47,11 @@ &__robot-img { position: fixed; - right: 30px; - bottom: 20px; + right: 20px; + bottom: 90px; z-index: 99; - width: 64px; - height: 64px; - background-color: white; - border-radius: 10px; + width: 56px; + height: 56px; cursor: pointer; } } diff --git a/react-ui/src/services/typings.d.ts b/react-ui/src/services/typings.d.ts index 1e0b8b5f..5720efd7 100644 --- a/react-ui/src/services/typings.d.ts +++ b/react-ui/src/services/typings.d.ts @@ -188,4 +188,227 @@ declare namespace API { filter?: string; sorter?: string; }; + type CurrentUser = UserInfo & { + signature?: string; + title?: string; + group?: string; + tags?: { key?: string; label?: string }[]; + notifyCount?: number; + unreadCount?: number; + country?: string; + access?: string; + geographic?: { + province?: { label?: string; key?: string }; + city?: { label?: string; key?: string }; + }; + address?: string; + phone?: string; + roleNames?: { + roleName?: string; + }[]; + }; + + type ErrorResponse = { + /** 业务约定的错误码 */ + errorCode: string; + /** 业务上的错误信息 */ + errorMessage?: string; + /** 业务上的请求是否成功 */ + success?: boolean; + }; + + type FakeCaptcha = { + code?: number; + status?: string; + }; + + type getFakeCaptchaParams = { + /** 手机号 */ + phone?: string; + }; + + type LoginParams = { + username?: string; + password?: string; + uuid?: string; + autoLogin?: boolean; + type?: string; + }; + + type LoginResult = { + code: number; + msg?: string; + type?: string; + data?: { + access_token?: string; + expires_in?: number; + }; + }; + + type NoticeIconItem = { + id?: string; + extra?: string; + key?: string; + read?: boolean; + avatar?: string; + title?: string; + status?: string; + datetime?: string; + description?: string; + type?: NoticeIconItemType; + }; + + type NoticeIconItemType = 'notification' | 'message' | 'event'; + + type NoticeIconList = { + data?: NoticeIconItem[]; + /** 列表的内容总数 */ + total?: number; + success?: boolean; + }; + + type PageParams = { + current?: number; + pageSize?: number; + }; + + type RuleList = { + data?: RuleListItem[]; + /** 列表的内容总数 */ + total?: number; + success?: boolean; + }; + + type RuleListItem = { + key?: number; + disabled?: boolean; + href?: string; + avatar?: string; + name?: string; + owner?: string; + desc?: string; + callNo?: number; + status?: number; + updatedAt?: string; + createdAt?: string; + progress?: number; + }; + + type ruleParams = { + /** 当前的页码 */ + current?: number; + /** 页面的容量 */ + pageSize?: number; + }; + + type ApiResponse = { + code?: number; + type?: string; + message?: string; + }; + + type Category = { + id?: number; + name?: string; + }; + + type deleteOrderParams = { + /** ID of the order that needs to be deleted */ + orderId: number; + }; + + type deletePetParams = { + api_key?: string; + /** Pet id to delete */ + petId: number; + }; + + type deleteUserParams = { + /** The name that needs to be deleted */ + username: string; + }; + + type findPetsByStatusParams = { + /** Status values that need to be considered for filter */ + status: ('available' | 'pending' | 'sold')[]; + }; + + type findPetsByTagsParams = { + /** Tags to filter by */ + tags: string[]; + }; + + type getOrderByIdParams = { + /** ID of pet that needs to be fetched */ + orderId: number; + }; + + type getPetByIdParams = { + /** ID of pet to return */ + petId: number; + }; + + type getUserByNameParams = { + /** The name that needs to be fetched. Use user1 for testing. */ + username: string; + }; + + type loginUserParams = { + /** The user name for login */ + username: string; + /** The password for login in clear text */ + password: string; + }; + + type Order = { + id?: number; + petId?: number; + quantity?: number; + shipDate?: string; + /** Order Status */ + status?: 'placed' | 'approved' | 'delivered'; + complete?: boolean; + }; + + type Pet = { + id?: number; + category?: Category; + name: string; + photoUrls: string[]; + tags?: Tag[]; + /** pet status in the store */ + status?: 'available' | 'pending' | 'sold'; + }; + + type Tag = { + id?: number; + name?: string; + }; + + type updatePetWithFormParams = { + /** ID of pet that needs to be updated */ + petId: number; + }; + + type updateUserParams = { + /** name that need to be updated */ + username: string; + }; + + type uploadFileParams = { + /** ID of pet to update */ + petId: number; + }; + + type User = { + id?: number; + username?: string; + firstName?: string; + lastName?: string; + email?: string; + password?: string; + phone?: string; + /** User Status */ + userStatus?: number; + }; } From 5d99e0481c5fa2fcb09af79e11bfe731e8fe5870 Mon Sep 17 00:00:00 2001 From: cp3hnu Date: Wed, 23 Oct 2024 09:15:29 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=AF=B9=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- react-ui/src/components/BasicInfo/index.less | 5 - react-ui/src/components/BasicInfo/index.tsx | 13 +- .../src/components/BasicTableInfo/index.less | 6 +- react-ui/src/enums/pagesEnums.ts | 1 + react-ui/src/hooks/index.ts | 2 +- .../src/pages/Experiment/Comparison/index.tsx | 15 +- .../Model/components/ModelMetrics/index.tsx | 26 ++- .../ModelDeployment/ServiceInfo/index.tsx | 41 +++- .../components/VersionCompareModal/index.less | 117 +++++++++++ .../components/VersionCompareModal/index.tsx | 193 ++++++++++++++++++ .../src/services/modelDeployment/index.ts | 8 + react-ui/src/utils/index.ts | 23 +++ 12 files changed, 413 insertions(+), 37 deletions(-) create mode 100644 react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.less create mode 100644 react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx diff --git a/react-ui/src/components/BasicInfo/index.less b/react-ui/src/components/BasicInfo/index.less index ffeb3d3d..e4570868 100644 --- a/react-ui/src/components/BasicInfo/index.less +++ b/react-ui/src/components/BasicInfo/index.less @@ -38,13 +38,8 @@ margin-left: 16px; font-size: @font-size-content; line-height: 1.6; - white-space: pre-line; word-break: break-all; - &--ellipsis { - .singleLine(); - } - &__text { color: @text-color; } diff --git a/react-ui/src/components/BasicInfo/index.tsx b/react-ui/src/components/BasicInfo/index.tsx index 60cd5b57..bd07db34 100644 --- a/react-ui/src/components/BasicInfo/index.tsx +++ b/react-ui/src/components/BasicInfo/index.tsx @@ -120,13 +120,10 @@ export function BasicInfoItemValue({ } return ( - - {component} - +
+ + {component} + +
); } diff --git a/react-ui/src/components/BasicTableInfo/index.less b/react-ui/src/components/BasicTableInfo/index.less index cc3d0984..314b05ca 100644 --- a/react-ui/src/components/BasicTableInfo/index.less +++ b/react-ui/src/components/BasicTableInfo/index.less @@ -33,10 +33,10 @@ &__value { flex: 1; + min-width: 0; margin: 0 !important; padding: 12px 20px 4px; font-size: @font-size; - white-space: pre-line; word-break: break-all; & + & { @@ -47,10 +47,6 @@ padding-bottom: 12px; } - &--ellipsis { - .singleLine(); - } - &__text { color: @text-color; } diff --git a/react-ui/src/enums/pagesEnums.ts b/react-ui/src/enums/pagesEnums.ts index 756d3325..d11eb8b5 100644 --- a/react-ui/src/enums/pagesEnums.ts +++ b/react-ui/src/enums/pagesEnums.ts @@ -1,3 +1,4 @@ export enum PageEnum { LOGIN = '/user/login', + Authorize = '/authorize', } diff --git a/react-ui/src/hooks/index.ts b/react-ui/src/hooks/index.ts index 6f5bdbbc..f5ef64af 100644 --- a/react-ui/src/hooks/index.ts +++ b/react-ui/src/hooks/index.ts @@ -162,7 +162,7 @@ export const useCheck = (list: T[]) => { const [selected, setSelected] = useState([]); const checked = useMemo(() => { - return selected.length === list.length; + return selected.length === list.length && selected.length > 0; }, [selected, list]); const indeterminate = useMemo(() => { diff --git a/react-ui/src/pages/Experiment/Comparison/index.tsx b/react-ui/src/pages/Experiment/Comparison/index.tsx index 1667a064..1edfd0a3 100644 --- a/react-ui/src/pages/Experiment/Comparison/index.tsx +++ b/react-ui/src/pages/Experiment/Comparison/index.tsx @@ -10,6 +10,7 @@ import { getExpMetricsReq, getExpTrainInfosReq, } from '@/services/experiment'; +import { tableSorter } from '@/utils'; import { to } from '@/utils/promise'; import tableCellRender, { TableCellValueType } from '@/utils/table'; import { useSearchParams } from '@umijs/max'; @@ -103,7 +104,7 @@ function ExperimentComparison() { }; // 选择行 - const rowSelection: TableProps['rowSelection'] = { + const rowSelection: TableProps['rowSelection'] = { type: 'checkbox', columnWidth: 48, fixed: 'left', @@ -126,8 +127,10 @@ function ExperimentComparison() { } }; - const columns: TableProps['columns'] = useMemo(() => { + const columns: TableProps['columns'] = useMemo(() => { const first: TableData | undefined = tableData[0]; + const metricsNames = first?.metrics_names ?? []; + const paramsNames = first?.params_names ?? []; return [ { title: '基本信息', @@ -175,7 +178,7 @@ function ExperimentComparison() { { title: `${config.title}参数`, align: 'center', - children: first?.params_names.map((name) => ({ + children: paramsNames.map((name) => ({ title: ( {name} @@ -187,14 +190,14 @@ function ExperimentComparison() { align: 'center', render: tableCellRender(true), ellipsis: { showTitle: false }, - sorter: (a, b) => a.params[name] - b.params[name], + sorter: (a, b) => tableSorter(a.params[name], b.params[name]), showSorterTooltip: false, })), }, { title: `${config.title}指标`, align: 'center', - children: first?.metrics_names.map((name) => ({ + children: metricsNames.map((name) => ({ title: ( {name} @@ -206,7 +209,7 @@ function ExperimentComparison() { align: 'center', render: tableCellRender(true), ellipsis: { showTitle: false }, - sorter: (a, b) => a.metrics[name] - b.metrics[name], + sorter: (a, b) => tableSorter(a.metrics[name], b.metrics[name]), showSorterTooltip: false, })), }, diff --git a/react-ui/src/pages/Model/components/ModelMetrics/index.tsx b/react-ui/src/pages/Model/components/ModelMetrics/index.tsx index 7ffd7fbb..d1f8d2d6 100644 --- a/react-ui/src/pages/Model/components/ModelMetrics/index.tsx +++ b/react-ui/src/pages/Model/components/ModelMetrics/index.tsx @@ -1,6 +1,7 @@ import SubAreaTitle from '@/components/SubAreaTitle'; import { useCheck } from '@/hooks'; import { getModelPageVersionsReq, getModelVersionsMetricsReq } from '@/services/dataset'; +import { tableSorter } from '@/utils'; import { to } from '@/utils/promise'; import tableCellRender from '@/utils/table'; import { Checkbox, Table, Tooltip, type TablePaginationConfig, type TableProps } from 'antd'; @@ -18,7 +19,7 @@ type TableData = { metrics_names?: string[]; metrics?: Record; params_names?: string[]; - params?: Record; + params?: Record; }; type ModelMetricsProps = { @@ -113,14 +114,18 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr }; // 分页切换 - const handleTableChange: TableProps['onChange'] = (pagination, _filters, _sorter, { action }) => { + const handleTableChange: TableProps['onChange'] = ( + pagination, + _filters, + _sorter, + { action }, + ) => { if (action === 'paginate') { setPagination(pagination); } - // console.log(pagination, filters, sorter, action); }; - const rowSelection: TableProps['rowSelection'] = { + const rowSelection: TableProps['rowSelection'] = { type: 'checkbox', fixed: 'left', selectedRowKeys, @@ -142,10 +147,12 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr }, [version, tableData]); // 表头 - const columns: TableProps['columns'] = useMemo(() => { + const columns: TableProps['columns'] = useMemo(() => { const first: TableData | undefined = tableData.find( (item) => item.metrics_names && item.metrics_names.length > 0, ); + const metricsNames = first?.metrics_names ?? []; + const paramsNames = first?.params_names ?? []; return [ { title: '基本信息', @@ -165,7 +172,7 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr { title: `训练参数`, align: 'center', - children: first?.params_names?.map((name) => ({ + children: paramsNames.map((name) => ({ title: ( {name} @@ -177,7 +184,7 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr align: 'center', render: tableCellRender(true), ellipsis: { showTitle: false }, - sorter: (a, b) => a.params?.[name] ?? 0 - b.params?.[name] ?? 0, + sorter: (a, b) => tableSorter(a.params?.[name], b.params?.[name]), showSorterTooltip: false, })), }, @@ -188,12 +195,13 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr checked={metricsChecked} indeterminate={metricsIndeterminate} onChange={checkAllMetrics} + disabled={metricsNames.length === 0} >{' '} 训练指标 ), align: 'center', - children: first?.metrics_names?.map((name) => ({ + children: metricsNames.map((name) => ({ title: (
a.metrics?.[name] ?? 0 - b.metrics?.[name] ?? 0, + sorter: (a, b) => tableSorter(a.metrics?.[name], b.metrics?.[name]), showSorterTooltip: false, })), }, diff --git a/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx b/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx index 42b80e87..07d40de5 100644 --- a/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx +++ b/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx @@ -18,6 +18,7 @@ import { } from '@/services/modelDeployment'; import themes from '@/styles/theme.less'; import { formatDate } from '@/utils/date'; +import { openAntdModal } from '@/utils/modal'; import { to } from '@/utils/promise'; import SessionStorage from '@/utils/sessionStorage'; import tableCellRender, { TableCellValueType } from '@/utils/table'; @@ -37,6 +38,7 @@ import { type SearchProps } from 'antd/es/input'; import classNames from 'classnames'; import { useEffect, useState } from 'react'; import ServiceRunStatusCell from '../components/ModelDeployStatusCell'; +import VersionCompareModal from '../components/VersionCompareModal'; import { CreateServiceVersionFrom, ServiceData, @@ -56,6 +58,7 @@ function ServiceInfo() { const [inputText, setInputText] = useState(cacheState?.searchText); const [tableData, setTableData] = useState([]); const [total, setTotal] = useState(0); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [pagination, setPagination] = useState( cacheState?.pagination ?? { current: 1, @@ -208,11 +211,39 @@ function ServiceInfo() { }; // 分页切换 - const handleTableChange: TableProps['onChange'] = (pagination, _filters, _sorter, { action }) => { + const handleTableChange: TableProps['onChange'] = ( + pagination, + _filters, + _sorter, + { action }, + ) => { if (action === 'paginate') { setPagination(pagination); } - // console.log(pagination, filters, sorter, action); + }; + + // 版本对比 + const handleVersionCompare = () => { + if (selectedRowKeys.length !== 2) { + message.error('请选择两个版本进行对比'); + return; + } + + openAntdModal(VersionCompareModal, { + version1: selectedRowKeys[0] as string, + version2: selectedRowKeys[1] as string, + }); + }; + + // 选择行 + const rowSelection: TableProps['rowSelection'] = { + type: 'checkbox', + columnWidth: 48, + fixed: 'left', + selectedRowKeys, + onChange: (selectedRowKeys: React.Key[]) => { + setSelectedRowKeys(selectedRowKeys); + }, }; const columns: TableProps['columns'] = [ @@ -379,13 +410,16 @@ function ServiceInfo() { allowClear > +
diff --git a/react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.less b/react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.less new file mode 100644 index 00000000..f0d5449e --- /dev/null +++ b/react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.less @@ -0,0 +1,117 @@ +@purple-color: #6516ff; + +.title(@color, @background) { + width: 100%; + margin-bottom: 20px; + color: @color; + font-weight: 500; + font-size: @font-size; + line-height: 42px; + text-align: center; + background: @background; + .singleLine(); +} + +.text() { + margin-bottom: 20px !important; + color: @text-color-secondary; + font-size: 13px; + word-break: break-all; + .singleLine(); +} + +.version-container(@background) { + flex: 1; + min-width: 0; + background: @background; + border-radius: 4px; +} + +.version-compare { + :global { + .ant-modal-content { + padding: 40px 40px 25px !important; + } + .ant-modal-header { + margin-bottom: 20px !important; + } + .kf-modal-title { + color: @text-color; + font-weight: 500; + font-size: 20px; + } + } + + &__container { + display: flex; + flex-wrap: nowrap; + gap: 0 5px; + align-items: stretch; + height: 100%; + } + + &__fields { + flex: none; + width: 117px; + padding: 0 15px; + background: white; + border: 1px solid .addAlpha(@primary-color, 0.2) []; + border-radius: 4px; + + &__title { + margin-bottom: 20px; + color: @text-color; + font-size: @font-size; + line-height: 42px; + } + &__text { + .text(); + + &--different { + color: @error-color; + } + } + } + + &__left { + .version-container(.addAlpha(@primary-color, 0.04) []); + + &__title { + .title(@primary-color, linear-gradient( + 159.9deg,rgba(138, 177, 255, 0.5) 0%, + rgba(22, 100, 255, 0.5) 100% + )); + } + + &__text { + padding: 0 15px; + text-align: center; + .text(); + + &--different { + color: @primary-color; + } + } + } + + &__right { + .version-container(rgba(100, 30, 237, 0.04)); + &__title { + .title(@purple-color, linear-gradient( + 159.9deg, + rgba(193, 138, 255, 0.5) 0%, + rgba(146, 22, 255, 0.5) 100% + )); + } + + &__text { + padding: 0 15px; + text-align: center; + .text(); + + &--different { + color: @purple-color; + } + } + } +} diff --git a/react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx b/react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx new file mode 100644 index 00000000..be28ba3b --- /dev/null +++ b/react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx @@ -0,0 +1,193 @@ +import KFModal from '@/components/KFModal'; +import { ServiceRunStatus } from '@/enums'; +import { useComputingResource } from '@/hooks/resource'; +import { type ServiceVersionData } from '@/pages/ModelDeployment/types'; +import { getServiceVersionCompareReq } from '@/services/modelDeployment'; +import { to } from '@/utils/promise'; +import { Typography, type ModalProps } from 'antd'; +import classNames from 'classnames'; +import { useEffect, useMemo, useState } from 'react'; +import { statusInfo } from '../ModelDeployStatusCell'; +import styles from './index.less'; + +type CompareData = { + differences: Record; + version1: ServiceVersionData; + version2: ServiceVersionData; +}; + +type ServiceVersionDataKey = keyof ServiceVersionData; + +type FiledType = { + key: ServiceVersionDataKey; + text: string; + format?: (data: any) => any; +}; + +interface CreateMirrorModalProps extends Omit { + version1: string; + version2: string; +} + +// 格式化环境变量 +const formatEnvText = (env: Record) => { + if (!env || Object.keys(env).length === 0) { + return '--'; + } + return Object.entries(env) + .map(([key, value]) => `${key} = ${value}`) + .join(','); +}; + +function VersionCompareModal({ version1, version2, ...rest }: CreateMirrorModalProps) { + const [compareData, setCompareData] = useState(undefined); + const getResourceDescription = useComputingResource()[2]; + + const fields: FiledType[] = useMemo( + () => [ + { + key: 'service_name', + text: '服务名称', + }, + { + key: 'run_state', + text: '状态', + format: (data: any) => { + return data ? statusInfo[data as ServiceRunStatus].text : '--'; + }, + }, + { + key: 'image', + text: '镜像', + }, + { + key: 'code_config', + text: '代码配置', + format: (data: any) => { + return data?.show_value; + }, + }, + { + key: 'model', + text: '模型', + format: (data: any) => { + return data?.show_value; + }, + }, + { + key: 'resource', + text: '资源规格', + format: getResourceDescription, + }, + { + key: 'replicas', + text: '副本数', + }, + { + key: 'mount_path', + text: '挂载路径', + }, + { + key: 'url', + text: '服务URL', + }, + { + key: 'env_variables', + text: '环境变量', + format: formatEnvText, + }, + { + key: 'description', + text: '描述', + }, + ], + [getResourceDescription], + ); + + useEffect(() => { + getServiceVersionCompare(); + }, []); + + // 获取对比数据 + const getServiceVersionCompare = async () => { + const params = { + id1: version1, + id2: version2, + }; + const [res] = await to(getServiceVersionCompareReq(params)); + if (res && res.data) { + setCompareData(res.data); + } + }; + + const { + version1: v1 = {} as ServiceVersionData, + version2: v2 = {} as ServiceVersionData, + differences = {}, + } = compareData || {}; + + const isDifferent = (key: ServiceVersionDataKey) => { + const keys = Object.keys(differences); + return keys.includes(key); + }; + + return ( + +
+
+
基础版本号
+ {fields.map(({ key, text }) => ( +
+ {text} +
+ ))} +
+
+
{v1.version}
+ {fields.map(({ key, format }) => { + const text = format ? format(v1[key]) : v1[key]; + return ( +
+ {text} +
+ ); + })} +
+
+
{v2.version}
+ {fields.map(({ key, format }) => { + const text = format ? format(v2[key]) : v2[key]; + return ( +
+ {text} +
+ ); + })} +
+
+
+ ); +} + +export default VersionCompareModal; diff --git a/react-ui/src/services/modelDeployment/index.ts b/react-ui/src/services/modelDeployment/index.ts index eccf841e..9a33687f 100644 --- a/react-ui/src/services/modelDeployment/index.ts +++ b/react-ui/src/services/modelDeployment/index.ts @@ -104,3 +104,11 @@ export function getServiceVersionLogReq(params: any) { params, }); } + +// 获取服务版本对比 +export function getServiceVersionCompareReq(params: any) { + return request(`/api/mmp/service/serviceVersionCompare`, { + method: 'GET', + params, + }); +} diff --git a/react-ui/src/utils/index.ts b/react-ui/src/utils/index.ts index 0af6f5aa..67ca10d2 100644 --- a/react-ui/src/utils/index.ts +++ b/react-ui/src/utils/index.ts @@ -4,6 +4,7 @@ * @Description: 工具类 */ +import { PageEnum } from '@/enums/pagesEnums'; import G6 from '@antv/g6'; // 生成 8 位随机数 @@ -218,3 +219,25 @@ export const getGitUrl = (url: string, branch: string): string => { const gitUrl = url.replace(/\.git$/, ''); return branch ? `${gitUrl}/tree/${branch}` : gitUrl; }; + +// 判断是否需要登录 +export const needAuth = (pathname: string) => { + return pathname !== PageEnum.LOGIN && pathname !== PageEnum.Authorize; +}; + +// 表格排序 +export const tableSorter = (a: any, b: any) => { + if (b === null || b === undefined) { + return -1; + } + if (a === null || a === undefined) { + return 1; + } + if (typeof a === 'number' && typeof b === 'number') { + return a - b; + } + if (typeof a === 'string' && typeof b === 'string') { + return a.localeCompare(b); + } + return 0; +}; From ca16078569b245a94f0aa888e83501bbeceddf03 Mon Sep 17 00:00:00 2001 From: cp3hnu Date: Wed, 23 Oct 2024 14:45:29 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=85=8D=E7=BD=AE=E7=BB=93=E6=9E=84=E5=92=8C=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- react-ui/src/assets/img/code-name-icon.png | Bin 0 -> 2158 bytes react-ui/src/assets/img/creatBy.png | Bin 1566 -> 1656 bytes react-ui/src/assets/img/total-icon.png | Bin 0 -> 4078 bytes react-ui/src/pages/CodeConfig/List/index.less | 75 +++++++-------- react-ui/src/pages/CodeConfig/List/index.tsx | 90 ++++++++++-------- .../components/CodeConfigItem/index.less | 75 +++++++++++---- .../components/CodeConfigItem/index.tsx | 23 +++-- .../components/ResourceItem/index.less | 44 +++++---- .../components/ResourceList/index.less | 1 - .../Dataset/components/ResourceList/index.tsx | 1 + .../DevelopmentEnvironment/List/index.tsx | 7 +- .../components/ExperimentInstance/index.less | 2 +- .../components/ExperimentInstance/index.tsx | 3 +- .../components/TensorBoardStatus/index.less | 2 +- react-ui/src/pages/Mirror/Info/index.tsx | 7 +- react-ui/src/pages/Mirror/List/index.tsx | 7 +- .../src/pages/ModelDeployment/List/index.tsx | 7 +- .../components/VersionCompareModal/index.tsx | 8 +- .../components/CodeSelectorModal/index.less | 4 - .../components/CodeSelectorModal/index.tsx | 1 + 20 files changed, 218 insertions(+), 139 deletions(-) create mode 100644 react-ui/src/assets/img/code-name-icon.png create mode 100644 react-ui/src/assets/img/total-icon.png diff --git a/react-ui/src/assets/img/code-name-icon.png b/react-ui/src/assets/img/code-name-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1e7991a97645ada3a9f925b57ab5c9d912fd19ca GIT binary patch literal 2158 zcmV-!2$A=RP)C4?CJU?_KYK0GsbXYcHG=ic4h zZgR8TcJG-vzjMy-{AQ*YS+4fSMGN8dd*YKWj1gi@eC+&p-b1$cv;MyLod1)<-3H4lc}Fg%6K5Q+(ZRZ&xEPJ&%93HG`2}Y zJ~?^y97c$Gbo{r^?d(+CQ66+e;f*BvdK~El!~Q#y$5-vm?LdreG9a&N!ia=I(Bbr# zFYWA9+zIq4+zn;~H1hgRMKC_~Ng+2yd0%NN8-ubn&C zbxg==g>Fa)(H(vzBLQ`=IC;!jlRE;%$NdqnTw#Y~?x&`W96?6PY(@oOk<;lc($*7f zTE*RSYGPr8)#MY;9G=T%)Os;z9Viy+coIS!IGq54n+0(lC5b%q=#RF^OWW-)_$Prr zcgB!6BBAO)M?nOqem_>|&JzWDA9zd%jhM=L-N?Sl*9VF(_|p#d?uzBS5ebRBc1|ER z^ZQYE{mMA-1UQhA^Alg^bD*<_1&}*#b&^#uq*F@N>2VOJQ^WbBiai3-fkdv$sZV^B z-2s{Z1qAtT&T2ObeA4MRH1f_KI>@jaFYZUBa?kUc(94ZIfBnNHZ75wBNccVht zNEMy)W*>-ZgwxU=!`=hScA)qSXcro?Qy6AegKf+K+k5;zg2+CMoo0=+c0eI+Xe_e^ zqDTq0z&YPPJ6vDj)l`-p&jcmHft}#3dTb--B~u2H{WF4`^~sz!(N0A^872A5*fio| z`|OAIl&a}K)-Xz~g9ttCU}vk!a7)hD>I1P9Go1lVi8vd*IFzopmhC{XyF5s+JAg3= zK?}cYhcY9dW=C*}eR^>2NL(*9>_A(0`RM|7j$`38N61Q60WUPWXtv3Nyk;rgsbef9 z`w?Xe!e_mI9a;5F=x5eMslHe2uCPngkrRlSix$oGN$nG*nW>G!zL0!3DLdpdp|o`A zJ@0|s5h!;1B?oyMlsmekHi~?rBU}4JiiQFOiipp~b34%44gEnb%M+|&YMRtsH_P%Ci3VHZMHIqoIII9V% z{sDhwN#mB?hhh)(22|ZITGC}LUDs9vaV-?rMo9q}Aup#-Fmi4L`BO-`3KF4&F|4pc zO_K%bROCY~l*NwBd5yfD{QSFxjk#+;$Es~t`Dw>-k3;@6*X8#*Id5%ZVJcNsD-_jK$t%bs&Zz^9?DG37)X0)mbI&XO zh`{a^qzl|YFX|Ub#z~1xq@nb?z%))k&t85neaUJ~A*pY$OTWj6bSW{1_}Jv}+WSB2 zUJ5o5N1Vn^xB^S^d{X}>`8|O=3HS`B;`#YQ4Tw3-HYPtaM8~H-uD?6I{#CzEILG)Z#urGy_Fp=<3_0D=Q{nr#7UA^5N7-GdC;R5B>*o{aQFe(S*m*3C6 zKV(gzj$T!cm5yiR>f}e+mz3*v2b)B}J(m@EIgYzXH(#oeGRfMrI#84j7&)n(m^_-@ z0Xf*(0Tu`nN^MId)W#OT1nr&9AFywbrL|kT124*z1YC)>K9$pf*4OB?aGkqq?F(t1 zCw?&V_q&CB+bSm8$VEv~e=ph3iSaye1mk1V!*+IB+wY&`tGt~tJDOrcFl)jWzoWJMsum(3&)d_mrjuXQ0tXL z`L@`xWSdy?303A2H#&gJHGYL3?FlvO<|=uGaqYAYD^;o?vxU64l+a2!%dbeQu zaK1jE=XA$j`Y-e3PF*GEHS*F0LdC+hw;DX;vpLn zfxNWuEz;4egDZT@dN(*AS;OoLl67VdHWq<92mHSBP2P}+ysnAWi$VXD!KRfx=w9Df z`L_U53qd?;vQ51dzV%JmvW5R&(Cc8mv#{`{qmK>inOO37YYl)T9uZ!god~PVHuXlo zeXn0CH&vRZss+hD*Zt9JZ#O+y@4L3Y*Q|Ughq2>ysslX^Y*T|Ovrgc)z5YmfwX;Rm klXruiz4mrvxv$6j9}f6iLj1QKM@L7$y1Tn; zOG--CIq40loYJUp2Jw6?ZBySKOZ zDww|l#oc*?uz!vP-(B2ycXxM(VW?deIwk=1_4Utz*?YL@^UN>I$V1#;mzI|5#Ds|g zsH>}c5zGp~$U}^ooWElxHsMm=R905b+v95&Kx1R$H89Bn11C9^W90?I9YX#Oh>Krr z(bxoVqI?dku(KTJ6L-+4%;T z`)~+2kD(g?@dZFUG8bk60IACzIldGFo_ACuwOXwT3=C8fpM!&gz3J)c#nsi-O-J}5 zCjRvG^{p=~EHqVc3jnpXwQ2CT*Bud(kdP1_8ylr6O(w9p;>5Y zY5AUq#(b=?HW&b)T`0!1P+KhNV)1<;#zR9x12Z!-D^i5Q-DgmtTNRW#;R0Ve(i-RG z<)sA#1YF`(#1R10W^8S3eV{<$dKrc59bVz)3(W*)Rt3IwnQ{$#iKum};#L~x^x z?d|OirBZp>oCjGS3;Hg&7%e3wU$myC8GrZ;9AsH(rKhLIhlPbj+tY}Yhvg+#Vk_Q% z5r7uFlx?*|ONz<%X?}iwhJ}liYhq$zY;tmPP6{mw znO5OQ8!fekuvS(o2&DfJ$*qKx4%C*FsWWYFZ|ANHXsN$bG&MDO;GvPXnJX$P$`(qM zQe1&(v|8;HV&Xn4{Yf*YkUooYXX04r_@5A=w2x)brt|PyVGM7$d8JPi=UVj=4q>`yUfka z&E~PN*w~eomBr4^&O2h`=y&t26%zmoW>|r?ReV{PSoilVG`7Z&^}D*dSbujZo}k11 z)Odbi;WDVo*Ad;Btjf?<3IbXsWjRSQXw|m9zOEY{9-d_FlMuW;SPPFBb+iD0%;Jo_ zoe$+$AFvvMXB?EDN4jn5?d^Rk2!LK<7JLBUV?18uVSWx{>2$N&`V37l>mZ=7AWc<3 zssF_GIvoN+pL&f(^T66r|9^`E4p_b&me&f+vgi>Lh~MIEGvsn7Y8TMvbYlzgXInf) z3m2W%0KofTc4~k`Jx` delta 1533 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 literal 0 HcmV?d00001 diff --git a/react-ui/src/pages/CodeConfig/List/index.less b/react-ui/src/pages/CodeConfig/List/index.less index 4108b486..d4cb1b4a 100644 --- a/react-ui/src/pages/CodeConfig/List/index.less +++ b/react-ui/src/pages/CodeConfig/List/index.less @@ -1,47 +1,46 @@ -.code-config-list { - display: flex; - flex: 1; - flex-direction: column; +.code-config { height: 100%; - height: 100%; - padding: 20px 0; - background: white; - box-shadow: 0px 3px 6px rgba(146, 146, 146, 0.09); - &__header { + &__list { display: flex; - align-items: center; - justify-content: space-between; - height: 32px; - margin-bottom: 30px; - padding: 0 30px; - color: @text-color; - font-size: 15px; - } + flex-direction: column; + height: calc(100% - 60px); + margin-top: 10px; + padding: 30px 30px 0; + background: white; + border-radius: 10px; + box-shadow: 0px 2px 12px rgba(180, 182, 191, 0.09); - &__content { - display: flex; - flex: 1; - flex-wrap: wrap; - gap: 20px; - align-content: flex-start; - width: 100%; - margin-bottom: 30px; - padding: 0 30px; - overflow-y: auto; - } + &__header { + display: flex; + align-items: center; + height: 32px; + color: @text-color; + font-size: 15px; + } - &__empty { - display: flex; - flex: 1; - align-items: center; - justify-content: center; - } + &__content { + display: flex; + flex: 1 1 0%; + flex-wrap: wrap; + gap: 20px; + align-content: flex-start; + width: 100%; + margin: 25px 0; + overflow-y: auto; + } + + &__empty { + display: flex; + flex: 1; + align-items: center; + justify-content: center; + } - :global { - .ant-pagination { - margin-right: 30px; - text-align: right; + :global { + .ant-pagination { + margin-bottom: 25px; + } } } } diff --git a/react-ui/src/pages/CodeConfig/List/index.tsx b/react-ui/src/pages/CodeConfig/List/index.tsx index 847d30ec..e8a2557f 100644 --- a/react-ui/src/pages/CodeConfig/List/index.tsx +++ b/react-ui/src/pages/CodeConfig/List/index.tsx @@ -1,5 +1,12 @@ +/* + * @Author: 赵伟 + * @Date: 2024-10-10 09:55:12 + * @Description: 代码配置 + */ + import KFEmpty, { EmptyType } from '@/components/KFEmpty'; import KFIcon from '@/components/KFIcon'; +import PageTitle from '@/components/PageTitle'; import { deleteCodeConfigReq, getCodeConfigListReq } from '@/services/codeConfig'; import { getGitUrl } from '@/utils'; import { openAntdModal } from '@/utils/modal'; @@ -127,64 +134,69 @@ function CodeConfigList() { }; return ( -
-
- 数据总数:{total} 个 -
+
+ +
+
+ 数据总数:{total} 个 setInputText(e.target.value)} value={inputText} />
-
- {dataList && dataList.length !== 0 && ( - <> -
- {dataList.map((item) => ( - - ))} -
- +
+ {dataList.map((item) => ( + + ))} +
+ + + )} + {dataList && dataList.length === 0 && ( + - - )} - {dataList && dataList.length === 0 && ( - - )} + )} +
); } diff --git a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less index c5d4abaa..c2855954 100644 --- a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less +++ b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less @@ -2,50 +2,96 @@ position: relative; width: calc(25% - 15px); padding: 20px; - background: white; - border: 1px solid #eaeaea; + background: linear-gradient(180deg, #f7faff 0%, #ffffff 100%); + border: 2px solid white; border-radius: 4px; + box-shadow: 0px 3px 10px rgba(164, 169, 181, 0.13); cursor: pointer; + &:hover { + border-color: @primary-color; + } + @media screen and (max-width: 1860px) { & { width: calc(33.33% - 13.33px); } } - &__name { + &__icon { + flex: none; + width: 16px; + height: 16px; margin-right: 10px; + } + + &__name { + position: relative; + margin-right: 20px; margin-bottom: 0 !important; color: @text-color; + font-weight: 500; font-size: 16px; + + &::after { + position: absolute; + top: 14px; + left: 0; + width: 100%; + height: 6px; + background: linear-gradient( + to right, + .addAlpha(@primary-color, 0.4) [] 0, + .addAlpha(@primary-color, 0) [] 100% + ); + content: ''; + } + } + + &:hover &__name { + color: @primary-color; } &__tag { - padding: 2px 11px; - font-size: 12px; - border-radius: 1000px; + flex: none; + padding: 1px 10px; + font-size: 13px; + border-radius: 2px; &--public { color: @primary-color; - background-color: .addAlpha(@primary-color, 0.08) []; + background-color: .addAlpha(@primary-color, 0.1) []; border-color: .addAlpha(@primary-color, 0.5) []; } &--private { color: @warning-color; - background-color: .addAlpha(@warning-color, 0.08) []; + background-color: .addAlpha(@warning-color, 0.1) []; border-color: .addAlpha(@warning-color, 0.5) []; } } + :global { + .ant-btn { + flex: none; + color: #808080; + } + } + + &__url-box { + margin-bottom: 15px; + padding: 14px; + background-color: .addAlpha(@primary-color, 0.03) []; + border-radius: 4px; + } + &__url { - margin-bottom: 10px !important; + margin-bottom: 15px !important; color: @text-color-secondary; font-size: 14px; } &__branch { - margin-bottom: 20px; color: @text-color-tertiary; font-size: 14px; } @@ -59,13 +105,4 @@ color: #808080; font-size: 13px; } - - &:hover { - border-color: @primary-color; - box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1); - } - - &:hover &__name { - color: @primary-color; - } } diff --git a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx index fe062bac..de903f47 100644 --- a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx +++ b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx @@ -19,6 +19,11 @@ function CodeConfigItem({ item, onClick, onEdit, onRemove }: CodeConfigItemProps return (
onClick?.(item)}> + - - {item.git_url} - -
{item.git_branch}
+
+ + {item.git_url} + +
{item.git_branch}
+
{ + const handleTableChange: TableProps['onChange'] = ( + pagination, + _filters, + _sorter, + { action }, + ) => { if (action === 'paginate') { setPagination(pagination); } diff --git a/react-ui/src/pages/Experiment/components/ExperimentInstance/index.less b/react-ui/src/pages/Experiment/components/ExperimentInstance/index.less index 56aa07e2..31df6572 100644 --- a/react-ui/src/pages/Experiment/components/ExperimentInstance/index.less +++ b/react-ui/src/pages/Experiment/components/ExperimentInstance/index.less @@ -4,7 +4,7 @@ width: 100%; padding: 0 0 0 33px; color: @text-color; - font-size: 15px; + font-size: 14px; & > div { padding: 0 16px; diff --git a/react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx b/react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx index 04508fb7..754034aa 100644 --- a/react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx +++ b/react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx @@ -129,7 +129,8 @@ function ExperimentInstanceComponent({ {selectedIns.length > 0 && (
); })} @@ -180,7 +182,9 @@ function VersionCompareModal({ version1, version2, ...rest }: CreateMirrorModalP [styles['version-compare__right__text--different']]: isDifferent(key), })} > - {text} + + {text} +
); })} diff --git a/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.less b/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.less index 3dd58383..cb77da6d 100644 --- a/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.less +++ b/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.less @@ -20,10 +20,6 @@ } } - .ant-pagination { - text-align: center; - } - .ant-input-group-addon { display: none; } diff --git a/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx b/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx index 6a42535c..24a54293 100644 --- a/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx +++ b/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx @@ -97,6 +97,7 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { ))}
Date: Wed, 23 Oct 2024 15:31:09 +0800 Subject: [PATCH 4/5] =?UTF-8?q?style:=20=E4=BF=AE=E6=94=B9=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=85=8D=E7=BD=AE=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CodeConfig/components/CodeConfigItem/index.less | 10 +++++----- .../pages/Dataset/components/ResourceItem/index.less | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less index c2855954..41415a9a 100644 --- a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less +++ b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less @@ -61,13 +61,13 @@ &--public { color: @primary-color; background-color: .addAlpha(@primary-color, 0.1) []; - border-color: .addAlpha(@primary-color, 0.5) []; + border: 1px solid .addAlpha(@primary-color, 0.5) []; } &--private { color: @warning-color; background-color: .addAlpha(@warning-color, 0.1) []; - border-color: .addAlpha(@warning-color, 0.5) []; + border: 1px solid .addAlpha(@warning-color, 0.5) []; } } @@ -81,18 +81,18 @@ &__url-box { margin-bottom: 15px; padding: 14px; - background-color: .addAlpha(@primary-color, 0.03) []; + background-color: .addAlpha(@primary-color, 0.04) []; border-radius: 4px; } &__url { margin-bottom: 15px !important; - color: @text-color-secondary; + color: @text-color; font-size: 14px; } &__branch { - color: @text-color-tertiary; + color: @text-color-secondary; font-size: 14px; } diff --git a/react-ui/src/pages/Dataset/components/ResourceItem/index.less b/react-ui/src/pages/Dataset/components/ResourceItem/index.less index cdb69428..01be647c 100644 --- a/react-ui/src/pages/Dataset/components/ResourceItem/index.less +++ b/react-ui/src/pages/Dataset/components/ResourceItem/index.less @@ -24,6 +24,7 @@ height: 24px; margin: 0 10px 0 0 !important; color: @text-color; + font-weight: 500; font-size: 16px; &::after { From d65c126f754864e705379f64340a5acd183c3499 Mon Sep 17 00:00:00 2001 From: cp3hnu Date: Thu, 24 Oct 2024 08:41:44 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20=E5=AE=9E=E9=AA=8C=E5=AF=B9?= =?UTF-8?q?=E6=AF=94=E5=88=86=E9=A1=B5=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Experiment/Comparison/index.less | 2 +- .../src/pages/Experiment/Comparison/index.tsx | 102 ++++++++---------- 2 files changed, 46 insertions(+), 58 deletions(-) diff --git a/react-ui/src/pages/Experiment/Comparison/index.less b/react-ui/src/pages/Experiment/Comparison/index.less index 273791c4..3be69ed9 100644 --- a/react-ui/src/pages/Experiment/Comparison/index.less +++ b/react-ui/src/pages/Experiment/Comparison/index.less @@ -14,7 +14,7 @@ &__table { height: calc(100% - 60px); - padding: 20px 30px; + padding: 20px 30px 0; background-color: white; border-radius: 10px; diff --git a/react-ui/src/pages/Experiment/Comparison/index.tsx b/react-ui/src/pages/Experiment/Comparison/index.tsx index 1edfd0a3..b900842c 100644 --- a/react-ui/src/pages/Experiment/Comparison/index.tsx +++ b/react-ui/src/pages/Experiment/Comparison/index.tsx @@ -4,7 +4,6 @@ * @Description: 实验对比 */ -import { useDomSize } from '@/hooks'; import { getExpEvaluateInfosReq, getExpMetricsReq, @@ -14,7 +13,8 @@ import { tableSorter } from '@/utils'; import { to } from '@/utils/promise'; import tableCellRender, { TableCellValueType } from '@/utils/table'; import { useSearchParams } from '@umijs/max'; -import { App, Button, Table, /* TablePaginationConfig,*/ TableProps, Tooltip } from 'antd'; +import { App, Button, Table, TablePaginationConfig, TableProps, Tooltip } from 'antd'; +import classNames from 'classnames'; import { useEffect, useMemo, useState } from 'react'; import ExperimentStatusCell from '../components/ExperimentStatusCell'; import { ComparisonType, comparisonConfig } from './config'; @@ -26,63 +26,45 @@ type TableData = { dataset: string[]; start_time: string; status: string; - metrics_names: string[]; - metrics: Record; - params_names: string[]; - params: Record; + metrics_names?: string[]; + metrics?: Record; + params_names?: string[]; + params?: Record; }; -const pageSize = 30; - -// function Footer() { -// return ( -//
-//
-//

我是有底线的

-//
-//
-// ); -// } - function ExperimentComparison() { const [searchParams] = useSearchParams(); const comparisonType = searchParams.get('type') as ComparisonType; const experimentId = searchParams.get('id'); const [tableData, setTableData] = useState([]); const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [total, setTotal] = useState(0); + const [pagination, setPagination] = useState({ + current: 1, + pageSize: 10, + }); + const { message } = App.useApp(); const config = useMemo(() => comparisonConfig[comparisonType], [comparisonType]); - const [tableRef, { width: tableWidth, height: tableHeight }] = useDomSize( - 0, - 0, - [], - ); - const [loadCompleted, setLoadCompleted] = useState(false); - const [loading, setLoading] = useState(false); // 避免误触发加载更多 useEffect(() => { getComparisonData(); }, [experimentId]); // 获取对比数据列表 - const getComparisonData = async (offset: string = '') => { + const getComparisonData = async () => { const request = comparisonType === ComparisonType.Train ? getExpTrainInfosReq : getExpEvaluateInfosReq; - const [res] = await to(request(experimentId, { offset: offset, limit: pageSize })); + const params = { + page: pagination.current! - 1, + size: pagination.pageSize, + }; + const [res] = await to(request(experimentId, params)); if (res && res.data) { - setTableData((prev) => [...prev, ...res.data]); - if (res.data.length === 0) { - setLoadCompleted(true); - const ele = document.getElementsByClassName('ant-table-body')[0]; - if (ele) { - const div = document.createElement('div'); - div.className = styles['experiment-comparison__table__footer']; - div.innerHTML = '

我是有底线的

'; - ele.appendChild(div); - } - } + const { content = [], totalElements = 0 } = res.data; + setTableData(content); + setTotal(totalElements); } - setLoading(false); }; // 获取对比 url @@ -114,21 +96,22 @@ function ExperimentComparison() { }, }; - const handleTableScroll = (e: React.UIEvent) => { - const target = e.target as HTMLDivElement; - - const { scrollTop, scrollHeight, clientHeight } = target; - - // 实现自动加载更多 - if (!loadCompleted && !loading && scrollHeight - scrollTop - clientHeight <= 0) { - const last = tableData[tableData.length - 1]; - setLoading(true); - getComparisonData(last?.run_id); + // 分页切换 + const handleTableChange: TableProps['onChange'] = ( + pagination, + _filters, + _sorter, + { action }, + ) => { + if (action === 'paginate') { + setPagination(pagination); } }; const columns: TableProps['columns'] = useMemo(() => { - const first: TableData | undefined = tableData[0]; + const first: TableData | undefined = tableData.find( + (item) => item.metrics_names && item.metrics_names.length > 0, + ); const metricsNames = first?.metrics_names ?? []; const paramsNames = first?.params_names ?? []; return [ @@ -190,7 +173,7 @@ function ExperimentComparison() { align: 'center', render: tableCellRender(true), ellipsis: { showTitle: false }, - sorter: (a, b) => tableSorter(a.params[name], b.params[name]), + sorter: (a, b) => tableSorter(a.params?.[name], b.params?.[name]), showSorterTooltip: false, })), }, @@ -209,7 +192,7 @@ function ExperimentComparison() { align: 'center', render: tableCellRender(true), ellipsis: { showTitle: false }, - sorter: (a, b) => tableSorter(a.metrics[name], b.metrics[name]), + sorter: (a, b) => tableSorter(a.metrics?.[name], b.metrics?.[name]), showSorterTooltip: false, })), }, @@ -223,16 +206,21 @@ function ExperimentComparison() { 可视化对比
-
+
record.run_id || record.experiment_ins_id} />