| @@ -38,13 +38,8 @@ | |||||
| margin-left: 16px; | margin-left: 16px; | ||||
| font-size: @font-size-content; | font-size: @font-size-content; | ||||
| line-height: 1.6; | line-height: 1.6; | ||||
| white-space: pre-line; | |||||
| word-break: break-all; | word-break: break-all; | ||||
| &--ellipsis { | |||||
| .singleLine(); | |||||
| } | |||||
| &__text { | &__text { | ||||
| color: @text-color; | color: @text-color; | ||||
| } | } | ||||
| @@ -120,13 +120,10 @@ export function BasicInfoItemValue({ | |||||
| } | } | ||||
| return ( | return ( | ||||
| <Typography.Text | |||||
| className={classNames(myClassName, { | |||||
| [`${myClassName}--ellipsis`]: ellipsis, | |||||
| })} | |||||
| ellipsis={{ tooltip: value }} | |||||
| > | |||||
| {component} | |||||
| </Typography.Text> | |||||
| <div className={myClassName}> | |||||
| <Typography.Text ellipsis={ellipsis ? { tooltip: value } : false}> | |||||
| {component} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| ); | ); | ||||
| } | } | ||||
| @@ -33,10 +33,10 @@ | |||||
| &__value { | &__value { | ||||
| flex: 1; | flex: 1; | ||||
| min-width: 0; | |||||
| margin: 0 !important; | margin: 0 !important; | ||||
| padding: 12px 20px 4px; | padding: 12px 20px 4px; | ||||
| font-size: @font-size; | font-size: @font-size; | ||||
| white-space: pre-line; | |||||
| word-break: break-all; | word-break: break-all; | ||||
| & + & { | & + & { | ||||
| @@ -47,10 +47,6 @@ | |||||
| padding-bottom: 12px; | padding-bottom: 12px; | ||||
| } | } | ||||
| &--ellipsis { | |||||
| .singleLine(); | |||||
| } | |||||
| &__text { | &__text { | ||||
| color: @text-color; | color: @text-color; | ||||
| } | } | ||||
| @@ -1,3 +1,4 @@ | |||||
| export enum PageEnum { | export enum PageEnum { | ||||
| LOGIN = '/user/login', | LOGIN = '/user/login', | ||||
| Authorize = '/authorize', | |||||
| } | } | ||||
| @@ -162,7 +162,7 @@ export const useCheck = <T>(list: T[]) => { | |||||
| const [selected, setSelected] = useState<T[]>([]); | const [selected, setSelected] = useState<T[]>([]); | ||||
| const checked = useMemo(() => { | const checked = useMemo(() => { | ||||
| return selected.length === list.length; | |||||
| return selected.length === list.length && selected.length > 0; | |||||
| }, [selected, list]); | }, [selected, list]); | ||||
| const indeterminate = useMemo(() => { | const indeterminate = useMemo(() => { | ||||
| @@ -10,6 +10,7 @@ import { | |||||
| getExpMetricsReq, | getExpMetricsReq, | ||||
| getExpTrainInfosReq, | getExpTrainInfosReq, | ||||
| } from '@/services/experiment'; | } from '@/services/experiment'; | ||||
| import { tableSorter } from '@/utils'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import tableCellRender, { TableCellValueType } from '@/utils/table'; | import tableCellRender, { TableCellValueType } from '@/utils/table'; | ||||
| import { useSearchParams } from '@umijs/max'; | import { useSearchParams } from '@umijs/max'; | ||||
| @@ -103,7 +104,7 @@ function ExperimentComparison() { | |||||
| }; | }; | ||||
| // 选择行 | // 选择行 | ||||
| const rowSelection: TableProps['rowSelection'] = { | |||||
| const rowSelection: TableProps<TableData>['rowSelection'] = { | |||||
| type: 'checkbox', | type: 'checkbox', | ||||
| columnWidth: 48, | columnWidth: 48, | ||||
| fixed: 'left', | fixed: 'left', | ||||
| @@ -126,8 +127,10 @@ function ExperimentComparison() { | |||||
| } | } | ||||
| }; | }; | ||||
| const columns: TableProps['columns'] = useMemo(() => { | |||||
| const columns: TableProps<TableData>['columns'] = useMemo(() => { | |||||
| const first: TableData | undefined = tableData[0]; | const first: TableData | undefined = tableData[0]; | ||||
| const metricsNames = first?.metrics_names ?? []; | |||||
| const paramsNames = first?.params_names ?? []; | |||||
| return [ | return [ | ||||
| { | { | ||||
| title: '基本信息', | title: '基本信息', | ||||
| @@ -175,7 +178,7 @@ function ExperimentComparison() { | |||||
| { | { | ||||
| title: `${config.title}参数`, | title: `${config.title}参数`, | ||||
| align: 'center', | align: 'center', | ||||
| children: first?.params_names.map((name) => ({ | |||||
| children: paramsNames.map((name) => ({ | |||||
| title: ( | title: ( | ||||
| <Tooltip title={name}> | <Tooltip title={name}> | ||||
| <span>{name}</span> | <span>{name}</span> | ||||
| @@ -187,14 +190,14 @@ function ExperimentComparison() { | |||||
| align: 'center', | align: 'center', | ||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| ellipsis: { showTitle: false }, | ellipsis: { showTitle: false }, | ||||
| sorter: (a, b) => a.params[name] - b.params[name], | |||||
| sorter: (a, b) => tableSorter(a.params[name], b.params[name]), | |||||
| showSorterTooltip: false, | showSorterTooltip: false, | ||||
| })), | })), | ||||
| }, | }, | ||||
| { | { | ||||
| title: `${config.title}指标`, | title: `${config.title}指标`, | ||||
| align: 'center', | align: 'center', | ||||
| children: first?.metrics_names.map((name) => ({ | |||||
| children: metricsNames.map((name) => ({ | |||||
| title: ( | title: ( | ||||
| <Tooltip title={name}> | <Tooltip title={name}> | ||||
| <span>{name}</span> | <span>{name}</span> | ||||
| @@ -206,7 +209,7 @@ function ExperimentComparison() { | |||||
| align: 'center', | align: 'center', | ||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| ellipsis: { showTitle: false }, | ellipsis: { showTitle: false }, | ||||
| sorter: (a, b) => a.metrics[name] - b.metrics[name], | |||||
| sorter: (a, b) => tableSorter(a.metrics[name], b.metrics[name]), | |||||
| showSorterTooltip: false, | showSorterTooltip: false, | ||||
| })), | })), | ||||
| }, | }, | ||||
| @@ -1,6 +1,7 @@ | |||||
| import SubAreaTitle from '@/components/SubAreaTitle'; | import SubAreaTitle from '@/components/SubAreaTitle'; | ||||
| import { useCheck } from '@/hooks'; | import { useCheck } from '@/hooks'; | ||||
| import { getModelPageVersionsReq, getModelVersionsMetricsReq } from '@/services/dataset'; | import { getModelPageVersionsReq, getModelVersionsMetricsReq } from '@/services/dataset'; | ||||
| import { tableSorter } from '@/utils'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import tableCellRender from '@/utils/table'; | import tableCellRender from '@/utils/table'; | ||||
| import { Checkbox, Table, Tooltip, type TablePaginationConfig, type TableProps } from 'antd'; | import { Checkbox, Table, Tooltip, type TablePaginationConfig, type TableProps } from 'antd'; | ||||
| @@ -18,7 +19,7 @@ type TableData = { | |||||
| metrics_names?: string[]; | metrics_names?: string[]; | ||||
| metrics?: Record<string, number>; | metrics?: Record<string, number>; | ||||
| params_names?: string[]; | params_names?: string[]; | ||||
| params?: Record<string, string>; | |||||
| params?: Record<string, number>; | |||||
| }; | }; | ||||
| type ModelMetricsProps = { | type ModelMetricsProps = { | ||||
| @@ -113,14 +114,18 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||||
| }; | }; | ||||
| // 分页切换 | // 分页切换 | ||||
| const handleTableChange: TableProps['onChange'] = (pagination, _filters, _sorter, { action }) => { | |||||
| const handleTableChange: TableProps<TableData>['onChange'] = ( | |||||
| pagination, | |||||
| _filters, | |||||
| _sorter, | |||||
| { action }, | |||||
| ) => { | |||||
| if (action === 'paginate') { | if (action === 'paginate') { | ||||
| setPagination(pagination); | setPagination(pagination); | ||||
| } | } | ||||
| // console.log(pagination, filters, sorter, action); | |||||
| }; | }; | ||||
| const rowSelection: TableProps['rowSelection'] = { | |||||
| const rowSelection: TableProps<TableData>['rowSelection'] = { | |||||
| type: 'checkbox', | type: 'checkbox', | ||||
| fixed: 'left', | fixed: 'left', | ||||
| selectedRowKeys, | selectedRowKeys, | ||||
| @@ -142,10 +147,12 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||||
| }, [version, tableData]); | }, [version, tableData]); | ||||
| // 表头 | // 表头 | ||||
| const columns: TableProps['columns'] = useMemo(() => { | |||||
| const columns: TableProps<TableData>['columns'] = useMemo(() => { | |||||
| const first: TableData | undefined = tableData.find( | const first: TableData | undefined = tableData.find( | ||||
| (item) => item.metrics_names && item.metrics_names.length > 0, | (item) => item.metrics_names && item.metrics_names.length > 0, | ||||
| ); | ); | ||||
| const metricsNames = first?.metrics_names ?? []; | |||||
| const paramsNames = first?.params_names ?? []; | |||||
| return [ | return [ | ||||
| { | { | ||||
| title: '基本信息', | title: '基本信息', | ||||
| @@ -165,7 +172,7 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||||
| { | { | ||||
| title: `训练参数`, | title: `训练参数`, | ||||
| align: 'center', | align: 'center', | ||||
| children: first?.params_names?.map((name) => ({ | |||||
| children: paramsNames.map((name) => ({ | |||||
| title: ( | title: ( | ||||
| <Tooltip title={name}> | <Tooltip title={name}> | ||||
| <span>{name}</span> | <span>{name}</span> | ||||
| @@ -177,7 +184,7 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||||
| align: 'center', | align: 'center', | ||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| ellipsis: { showTitle: false }, | 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, | showSorterTooltip: false, | ||||
| })), | })), | ||||
| }, | }, | ||||
| @@ -188,12 +195,13 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||||
| checked={metricsChecked} | checked={metricsChecked} | ||||
| indeterminate={metricsIndeterminate} | indeterminate={metricsIndeterminate} | ||||
| onChange={checkAllMetrics} | onChange={checkAllMetrics} | ||||
| disabled={metricsNames.length === 0} | |||||
| ></Checkbox>{' '} | ></Checkbox>{' '} | ||||
| <span>训练指标</span> | <span>训练指标</span> | ||||
| </div> | </div> | ||||
| ), | ), | ||||
| align: 'center', | align: 'center', | ||||
| children: first?.metrics_names?.map((name) => ({ | |||||
| children: metricsNames.map((name) => ({ | |||||
| title: ( | title: ( | ||||
| <div> | <div> | ||||
| <Checkbox | <Checkbox | ||||
| @@ -215,7 +223,7 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||||
| align: 'center', | align: 'center', | ||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| ellipsis: { showTitle: false }, | ellipsis: { showTitle: false }, | ||||
| sorter: (a, b) => a.metrics?.[name] ?? 0 - b.metrics?.[name] ?? 0, | |||||
| sorter: (a, b) => tableSorter(a.metrics?.[name], b.metrics?.[name]), | |||||
| showSorterTooltip: false, | showSorterTooltip: false, | ||||
| })), | })), | ||||
| }, | }, | ||||
| @@ -18,6 +18,7 @@ import { | |||||
| } from '@/services/modelDeployment'; | } from '@/services/modelDeployment'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { openAntdModal } from '@/utils/modal'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import SessionStorage from '@/utils/sessionStorage'; | import SessionStorage from '@/utils/sessionStorage'; | ||||
| import tableCellRender, { TableCellValueType } from '@/utils/table'; | import tableCellRender, { TableCellValueType } from '@/utils/table'; | ||||
| @@ -37,6 +38,7 @@ import { type SearchProps } from 'antd/es/input'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import ServiceRunStatusCell from '../components/ModelDeployStatusCell'; | import ServiceRunStatusCell from '../components/ModelDeployStatusCell'; | ||||
| import VersionCompareModal from '../components/VersionCompareModal'; | |||||
| import { | import { | ||||
| CreateServiceVersionFrom, | CreateServiceVersionFrom, | ||||
| ServiceData, | ServiceData, | ||||
| @@ -56,6 +58,7 @@ function ServiceInfo() { | |||||
| const [inputText, setInputText] = useState(cacheState?.searchText); | const [inputText, setInputText] = useState(cacheState?.searchText); | ||||
| const [tableData, setTableData] = useState<ServiceVersionData[]>([]); | const [tableData, setTableData] = useState<ServiceVersionData[]>([]); | ||||
| const [total, setTotal] = useState(0); | const [total, setTotal] = useState(0); | ||||
| const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); | |||||
| const [pagination, setPagination] = useState<TablePaginationConfig>( | const [pagination, setPagination] = useState<TablePaginationConfig>( | ||||
| cacheState?.pagination ?? { | cacheState?.pagination ?? { | ||||
| current: 1, | current: 1, | ||||
| @@ -208,11 +211,39 @@ function ServiceInfo() { | |||||
| }; | }; | ||||
| // 分页切换 | // 分页切换 | ||||
| const handleTableChange: TableProps['onChange'] = (pagination, _filters, _sorter, { action }) => { | |||||
| const handleTableChange: TableProps<ServiceVersionData>['onChange'] = ( | |||||
| pagination, | |||||
| _filters, | |||||
| _sorter, | |||||
| { action }, | |||||
| ) => { | |||||
| if (action === 'paginate') { | if (action === 'paginate') { | ||||
| setPagination(pagination); | 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<ServiceVersionData>['rowSelection'] = { | |||||
| type: 'checkbox', | |||||
| columnWidth: 48, | |||||
| fixed: 'left', | |||||
| selectedRowKeys, | |||||
| onChange: (selectedRowKeys: React.Key[]) => { | |||||
| setSelectedRowKeys(selectedRowKeys); | |||||
| }, | |||||
| }; | }; | ||||
| const columns: TableProps<ServiceVersionData>['columns'] = [ | const columns: TableProps<ServiceVersionData>['columns'] = [ | ||||
| @@ -379,13 +410,16 @@ function ServiceInfo() { | |||||
| allowClear | allowClear | ||||
| ></Select> | ></Select> | ||||
| <Button | <Button | ||||
| style={{ marginRight: '20px', marginLeft: 'auto' }} | |||||
| style={{ marginRight: '15px', marginLeft: 'auto' }} | |||||
| type="default" | type="default" | ||||
| onClick={() => createServiceVersion(ServiceOperationType.Create)} | onClick={() => createServiceVersion(ServiceOperationType.Create)} | ||||
| icon={<KFIcon type="icon-xinjian2" />} | icon={<KFIcon type="icon-xinjian2" />} | ||||
| > | > | ||||
| 新增版本 | 新增版本 | ||||
| </Button> | </Button> | ||||
| <Button style={{ marginRight: '15px' }} type="default" onClick={handleVersionCompare}> | |||||
| 版本对比 | |||||
| </Button> | |||||
| <Button | <Button | ||||
| style={{ marginRight: 0 }} | style={{ marginRight: 0 }} | ||||
| type="default" | type="default" | ||||
| @@ -410,6 +444,7 @@ function ServiceInfo() { | |||||
| showTotal: () => `共${total}条`, | showTotal: () => `共${total}条`, | ||||
| }} | }} | ||||
| onChange={handleTableChange} | onChange={handleTableChange} | ||||
| rowSelection={rowSelection} | |||||
| rowKey="id" | rowKey="id" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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<string, any>; | |||||
| version1: ServiceVersionData; | |||||
| version2: ServiceVersionData; | |||||
| }; | |||||
| type ServiceVersionDataKey = keyof ServiceVersionData; | |||||
| type FiledType = { | |||||
| key: ServiceVersionDataKey; | |||||
| text: string; | |||||
| format?: (data: any) => any; | |||||
| }; | |||||
| interface CreateMirrorModalProps extends Omit<ModalProps, 'onOk'> { | |||||
| version1: string; | |||||
| version2: string; | |||||
| } | |||||
| // 格式化环境变量 | |||||
| const formatEnvText = (env: Record<string, string>) => { | |||||
| 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<CompareData | undefined>(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 ( | |||||
| <KFModal | |||||
| {...rest} | |||||
| title="服务版本对比" | |||||
| width={825} | |||||
| footer={null} | |||||
| className={styles['version-compare']} | |||||
| > | |||||
| <div className={styles['version-compare__container']}> | |||||
| <div className={styles['version-compare__fields']}> | |||||
| <div className={styles['version-compare__fields__title']}>基础版本号</div> | |||||
| {fields.map(({ key, text }) => ( | |||||
| <div | |||||
| className={classNames(styles['version-compare__fields__text'], { | |||||
| [styles['version-compare__fields__text--different']]: isDifferent(key), | |||||
| })} | |||||
| key={key} | |||||
| > | |||||
| {text} | |||||
| </div> | |||||
| ))} | |||||
| </div> | |||||
| <div className={styles['version-compare__left']}> | |||||
| <div className={styles['version-compare__left__title']}>{v1.version}</div> | |||||
| {fields.map(({ key, format }) => { | |||||
| const text = format ? format(v1[key]) : v1[key]; | |||||
| return ( | |||||
| <div | |||||
| key={key} | |||||
| className={classNames(styles['version-compare__left__text'], { | |||||
| [styles['version-compare__left__text--different']]: isDifferent(key), | |||||
| })} | |||||
| > | |||||
| <Typography.Text ellipsis={{ tooltip: text }}>{text}</Typography.Text> | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </div> | |||||
| <div className={styles['version-compare__right']}> | |||||
| <div className={styles['version-compare__right__title']}>{v2.version}</div> | |||||
| {fields.map(({ key, format }) => { | |||||
| const text = format ? format(v2[key]) : v2[key]; | |||||
| return ( | |||||
| <div | |||||
| key={key} | |||||
| className={classNames(styles['version-compare__right__text'], { | |||||
| [styles['version-compare__right__text--different']]: isDifferent(key), | |||||
| })} | |||||
| > | |||||
| <Typography.Text ellipsis={{ tooltip: text }}>{text}</Typography.Text> | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </div> | |||||
| </div> | |||||
| </KFModal> | |||||
| ); | |||||
| } | |||||
| export default VersionCompareModal; | |||||
| @@ -104,3 +104,11 @@ export function getServiceVersionLogReq(params: any) { | |||||
| params, | params, | ||||
| }); | }); | ||||
| } | } | ||||
| // 获取服务版本对比 | |||||
| export function getServiceVersionCompareReq(params: any) { | |||||
| return request(`/api/mmp/service/serviceVersionCompare`, { | |||||
| method: 'GET', | |||||
| params, | |||||
| }); | |||||
| } | |||||
| @@ -4,6 +4,7 @@ | |||||
| * @Description: 工具类 | * @Description: 工具类 | ||||
| */ | */ | ||||
| import { PageEnum } from '@/enums/pagesEnums'; | |||||
| import G6 from '@antv/g6'; | import G6 from '@antv/g6'; | ||||
| // 生成 8 位随机数 | // 生成 8 位随机数 | ||||
| @@ -218,3 +219,25 @@ export const getGitUrl = (url: string, branch: string): string => { | |||||
| const gitUrl = url.replace(/\.git$/, ''); | const gitUrl = url.replace(/\.git$/, ''); | ||||
| return branch ? `${gitUrl}/tree/${branch}` : gitUrl; | 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; | |||||
| }; | |||||