| @@ -0,0 +1 @@ | |||
| save-prefix=~ | |||
| @@ -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": [ | |||
| @@ -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; | |||
| } | |||
| @@ -120,13 +120,10 @@ export function BasicInfoItemValue({ | |||
| } | |||
| 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 { | |||
| 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; | |||
| } | |||
| @@ -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; | |||
| @@ -1,3 +1,4 @@ | |||
| export enum PageEnum { | |||
| LOGIN = '/user/login', | |||
| Authorize = '/authorize', | |||
| } | |||
| @@ -162,7 +162,7 @@ export const useCheck = <T>(list: T[]) => { | |||
| const [selected, setSelected] = useState<T[]>([]); | |||
| const checked = useMemo(() => { | |||
| return selected.length === list.length; | |||
| return selected.length === list.length && selected.length > 0; | |||
| }, [selected, list]); | |||
| const indeterminate = useMemo(() => { | |||
| @@ -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; | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ( | |||
| <div className={styles['code-config-list']}> | |||
| <div className={styles['code-config-list__header']}> | |||
| <span>数据总数:{total} 个</span> | |||
| <div> | |||
| <div className={styles['code-config']}> | |||
| <PageTitle title="代码配置"></PageTitle> | |||
| <div className={styles['code-config__list']}> | |||
| <div className={styles['code-config__list__header']}> | |||
| <span>数据总数:{total} 个</span> | |||
| <Input.Search | |||
| placeholder="按代码仓库名称筛选" | |||
| allowClear | |||
| onSearch={handleSearch} | |||
| style={{ | |||
| width: 300, | |||
| marginRight: '20px', | |||
| marginLeft: 'auto', | |||
| }} | |||
| onChange={(e) => setInputText(e.target.value)} | |||
| value={inputText} | |||
| /> | |||
| <Button | |||
| type="default" | |||
| style={{ marginLeft: '20px' }} | |||
| style={{ marginRight: 0 }} | |||
| onClick={createCodeConfig} | |||
| icon={<KFIcon type="icon-xinjian2" />} | |||
| > | |||
| 新建代码配置 | |||
| </Button> | |||
| </div> | |||
| </div> | |||
| {dataList && dataList.length !== 0 && ( | |||
| <> | |||
| <div className={styles['code-config-list__content']}> | |||
| {dataList.map((item) => ( | |||
| <CodeConfigItem | |||
| item={item} | |||
| key={item.id} | |||
| onRemove={handleRemove} | |||
| onEdit={handleEdit} | |||
| onClick={handleClick} | |||
| /> | |||
| ))} | |||
| </div> | |||
| <Pagination | |||
| total={total} | |||
| showSizeChanger | |||
| defaultPageSize={20} | |||
| pageSizeOptions={[20, 40, 60, 80, 100]} | |||
| showQuickJumper | |||
| onChange={handlePageChange} | |||
| {...pagination} | |||
| {dataList && dataList.length !== 0 && ( | |||
| <> | |||
| <div className={styles['code-config__list__content']}> | |||
| {dataList.map((item) => ( | |||
| <CodeConfigItem | |||
| item={item} | |||
| key={item.id} | |||
| onRemove={handleRemove} | |||
| onEdit={handleEdit} | |||
| onClick={handleClick} | |||
| /> | |||
| ))} | |||
| </div> | |||
| <Pagination | |||
| align="end" | |||
| total={total} | |||
| showSizeChanger | |||
| defaultPageSize={20} | |||
| pageSizeOptions={[20, 40, 60, 80, 100]} | |||
| showQuickJumper | |||
| onChange={handlePageChange} | |||
| {...pagination} | |||
| /> | |||
| </> | |||
| )} | |||
| {dataList && dataList.length === 0 && ( | |||
| <KFEmpty | |||
| className={styles['code-config__list__empty']} | |||
| type={EmptyType.NoData} | |||
| title="暂无数据" | |||
| content={'很抱歉,没有搜索到您想要的内容\n建议刷新试试'} | |||
| hasFooter={true} | |||
| onRefresh={getDataList} | |||
| /> | |||
| </> | |||
| )} | |||
| {dataList && dataList.length === 0 && ( | |||
| <KFEmpty | |||
| className={styles['code-config-list__empty']} | |||
| type={EmptyType.NoData} | |||
| title="暂无数据" | |||
| content={'很抱歉,没有搜索到您想要的内容\n建议刷新试试'} | |||
| hasFooter={true} | |||
| onRefresh={getDataList} | |||
| /> | |||
| )} | |||
| )} | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -2,51 +2,97 @@ | |||
| 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) []; | |||
| border-color: .addAlpha(@primary-color, 0.5) []; | |||
| background-color: .addAlpha(@primary-color, 0.1) []; | |||
| border: 1px solid .addAlpha(@primary-color, 0.5) []; | |||
| } | |||
| &--private { | |||
| color: @warning-color; | |||
| background-color: .addAlpha(@warning-color, 0.08) []; | |||
| border-color: .addAlpha(@warning-color, 0.5) []; | |||
| background-color: .addAlpha(@warning-color, 0.1) []; | |||
| border: 1px solid .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.04) []; | |||
| border-radius: 4px; | |||
| } | |||
| &__url { | |||
| margin-bottom: 10px !important; | |||
| color: @text-color-secondary; | |||
| margin-bottom: 15px !important; | |||
| color: @text-color; | |||
| font-size: 14px; | |||
| } | |||
| &__branch { | |||
| margin-bottom: 20px; | |||
| color: @text-color-tertiary; | |||
| color: @text-color-secondary; | |||
| 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; | |||
| } | |||
| } | |||
| @@ -19,6 +19,11 @@ function CodeConfigItem({ item, onClick, onEdit, onRemove }: CodeConfigItemProps | |||
| return ( | |||
| <div className={styles['code-config-item']} onClick={() => onClick?.(item)}> | |||
| <Flex justify="space-between" align="center" style={{ marginBottom: '20px', height: '32px' }}> | |||
| <img | |||
| className={styles['code-config-item__icon']} | |||
| src={require('@/assets/img/code-name-icon.png')} | |||
| alt="" | |||
| /> | |||
| <Typography.Paragraph | |||
| className={styles['code-config-item__name']} | |||
| ellipsis={{ tooltip: item.code_repo_name }} | |||
| @@ -58,17 +63,19 @@ function CodeConfigItem({ item, onClick, onEdit, onRemove }: CodeConfigItemProps | |||
| <KFIcon type="icon-shanchu" font={17} /> | |||
| </Button> | |||
| </Flex> | |||
| <Typography.Paragraph | |||
| className={styles['code-config-item__url']} | |||
| ellipsis={{ tooltip: item.git_url }} | |||
| > | |||
| {item.git_url} | |||
| </Typography.Paragraph> | |||
| <div className={styles['code-config-item__branch']}>{item.git_branch}</div> | |||
| <div className={styles['code-config-item__url-box']}> | |||
| <Typography.Paragraph | |||
| className={styles['code-config-item__url']} | |||
| ellipsis={{ tooltip: item.git_url }} | |||
| > | |||
| {item.git_url} | |||
| </Typography.Paragraph> | |||
| <div className={styles['code-config-item__branch']}>{item.git_branch}</div> | |||
| </div> | |||
| <Flex justify="space-between"> | |||
| <div className={styles['code-config-item__user']}> | |||
| <img | |||
| style={{ width: '17px', marginRight: '6px' }} | |||
| style={{ width: '16px', marginRight: '6px' }} | |||
| src={creatByImg} | |||
| alt="" | |||
| draggable={false} | |||
| @@ -13,13 +13,37 @@ | |||
| } | |||
| } | |||
| &:hover { | |||
| border-color: @primary-color; | |||
| box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1); | |||
| } | |||
| &__name { | |||
| position: relative; | |||
| display: inline-block; | |||
| height: 24px; | |||
| margin: 0 10px 0 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.3) [] 0, | |||
| .addAlpha(@primary-color, 0) [] 100% | |||
| ); | |||
| content: ''; | |||
| } | |||
| } | |||
| &:hover &__name { | |||
| color: @primary-color; | |||
| } | |||
| &__description { | |||
| @@ -37,25 +61,4 @@ | |||
| color: #808080; | |||
| font-size: 13px; | |||
| } | |||
| &:hover { | |||
| border-color: @primary-color; | |||
| box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1); | |||
| .resource-item__name { | |||
| color: @primary-color; | |||
| } | |||
| } | |||
| } | |||
| .resource-item__name { | |||
| &::after { | |||
| position: absolute; | |||
| top: 14px; | |||
| left: 0; | |||
| width: 100%; | |||
| height: 6px; | |||
| background: linear-gradient(to right, rgba(22, 100, 255, 0.3) 0, rgba(22, 100, 255, 0) 100%); | |||
| content: ''; | |||
| } | |||
| } | |||
| @@ -33,7 +33,6 @@ | |||
| :global { | |||
| .ant-pagination { | |||
| margin-right: 30px; | |||
| text-align: right; | |||
| } | |||
| } | |||
| @@ -204,6 +204,7 @@ function ResourceList( | |||
| ))} | |||
| </div> | |||
| <Pagination | |||
| align="end" | |||
| total={total} | |||
| showSizeChanger | |||
| defaultPageSize={20} | |||
| @@ -152,7 +152,12 @@ function EditorList() { | |||
| }; | |||
| // 分页切换 | |||
| const handleTableChange: TableProps['onChange'] = (pagination, _filters, _sorter, { action }) => { | |||
| const handleTableChange: TableProps<EditorData>['onChange'] = ( | |||
| pagination, | |||
| _filters, | |||
| _sorter, | |||
| { action }, | |||
| ) => { | |||
| if (action === 'paginate') { | |||
| setPagination(pagination); | |||
| } | |||
| @@ -18,6 +18,26 @@ | |||
| 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; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,13 +1,19 @@ | |||
| // import { useCacheState } from '@/hooks/pageCacheState'; | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-10-10 09:55:12 | |||
| * @Description: 实验对比 | |||
| */ | |||
| import { | |||
| getExpEvaluateInfosReq, | |||
| 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'; | |||
| 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'; | |||
| @@ -20,10 +26,10 @@ type TableData = { | |||
| dataset: string[]; | |||
| start_time: string; | |||
| status: string; | |||
| metrics_names: string[]; | |||
| metrics: Record<string, number>; | |||
| params_names: string[]; | |||
| params: Record<string, number>; | |||
| metrics_names?: string[]; | |||
| metrics?: Record<string, number>; | |||
| params_names?: string[]; | |||
| params?: Record<string, number>; | |||
| }; | |||
| function ExperimentComparison() { | |||
| @@ -31,18 +37,15 @@ function ExperimentComparison() { | |||
| const comparisonType = searchParams.get('type') as ComparisonType; | |||
| const experimentId = searchParams.get('id'); | |||
| const [tableData, setTableData] = useState<TableData[]>([]); | |||
| // const [cacheState, setCacheState] = useCacheState(); | |||
| // const [total, setTotal] = useState(0); | |||
| const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); | |||
| // const [loading, setLoading] = useState(false); | |||
| const [total, setTotal] = useState(0); | |||
| const [pagination, setPagination] = useState<TablePaginationConfig>({ | |||
| current: 1, | |||
| pageSize: 10, | |||
| }); | |||
| const { message } = App.useApp(); | |||
| const config = useMemo(() => comparisonConfig[comparisonType], [comparisonType]); | |||
| // const [pagination, setPagination] = useState<TablePaginationConfig>( | |||
| // cacheState?.pagination ?? { | |||
| // current: 1, | |||
| // pageSize: 10, | |||
| // }, | |||
| // ); | |||
| useEffect(() => { | |||
| getComparisonData(); | |||
| @@ -50,15 +53,17 @@ function ExperimentComparison() { | |||
| // 获取对比数据列表 | |||
| const getComparisonData = async () => { | |||
| // setLoading(true); | |||
| const request = | |||
| comparisonType === ComparisonType.Train ? getExpTrainInfosReq : getExpEvaluateInfosReq; | |||
| const [res] = await to(request(experimentId, { offset: '', limit: 50 })); | |||
| // setLoading(false); | |||
| const params = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| }; | |||
| const [res] = await to(request(experimentId, params)); | |||
| if (res && res.data) { | |||
| // const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(res.data); | |||
| // setTotal(totalElements); | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| @@ -80,17 +85,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'] = { | |||
| const rowSelection: TableProps<TableData>['rowSelection'] = { | |||
| type: 'checkbox', | |||
| columnWidth: 48, | |||
| fixed: 'left', | |||
| selectedRowKeys, | |||
| onChange: (selectedRowKeys: React.Key[]) => { | |||
| @@ -98,8 +96,24 @@ function ExperimentComparison() { | |||
| }, | |||
| }; | |||
| // 分页切换 | |||
| const handleTableChange: TableProps<TableData>['onChange'] = ( | |||
| pagination, | |||
| _filters, | |||
| _sorter, | |||
| { action }, | |||
| ) => { | |||
| if (action === 'paginate') { | |||
| setPagination(pagination); | |||
| } | |||
| }; | |||
| const columns: TableProps<TableData>['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 [ | |||
| { | |||
| title: '基本信息', | |||
| @@ -147,7 +161,7 @@ function ExperimentComparison() { | |||
| { | |||
| title: `${config.title}参数`, | |||
| align: 'center', | |||
| children: first?.params_names.map((name) => ({ | |||
| children: paramsNames.map((name) => ({ | |||
| title: ( | |||
| <Tooltip title={name}> | |||
| <span>{name}</span> | |||
| @@ -159,14 +173,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: ( | |||
| <Tooltip title={name}> | |||
| <span>{name}</span> | |||
| @@ -178,7 +192,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, | |||
| })), | |||
| }, | |||
| @@ -192,30 +206,21 @@ function ExperimentComparison() { | |||
| 可视化对比 | |||
| </Button> | |||
| </div> | |||
| <div | |||
| className={classNames( | |||
| 'vertical-scroll-table-no-page', | |||
| styles['experiment-comparison__table'], | |||
| )} | |||
| > | |||
| <div className={classNames('vertical-scroll-table', styles['experiment-comparison__table'])}> | |||
| <Table | |||
| dataSource={tableData} | |||
| columns={columns} | |||
| rowSelection={rowSelection} | |||
| scroll={{ y: 'calc(100% - 110px)', x: '100%' }} | |||
| pagination={false} | |||
| pagination={{ | |||
| ...pagination, | |||
| total: total, | |||
| showSizeChanger: true, | |||
| showQuickJumper: true, | |||
| }} | |||
| onChange={handleTableChange} | |||
| bordered={true} | |||
| virtual | |||
| // onScroll={handleTableScroll} | |||
| // loading={loading} | |||
| // pagination={{ | |||
| // ...pagination, | |||
| // total: total, | |||
| // showSizeChanger: true, | |||
| // showQuickJumper: true, | |||
| // }} | |||
| // onChange={handleTableChange} | |||
| rowKey="run_id" | |||
| rowKey={(record) => record.run_id || record.experiment_ins_id} | |||
| /> | |||
| </div> | |||
| </div> | |||
| @@ -126,7 +126,7 @@ function AddExperimentModal({ | |||
| <Button key="cancel" onClick={onCancel}> | |||
| 取消 | |||
| </Button>, | |||
| <Button key="submit" type="primary" onClick={() => handleRun(false)}> | |||
| <Button key="submit" type={isAdd ? 'primary' : 'default'} onClick={() => handleRun(false)}> | |||
| 确定 | |||
| </Button>, | |||
| ]; | |||
| @@ -4,7 +4,7 @@ | |||
| width: 100%; | |||
| padding: 0 0 0 33px; | |||
| color: @text-color; | |||
| font-size: 15px; | |||
| font-size: 14px; | |||
| & > div { | |||
| padding: 0 16px; | |||
| @@ -129,7 +129,8 @@ function ExperimentInstanceComponent({ | |||
| {selectedIns.length > 0 && ( | |||
| <Button | |||
| style={{ position: 'absolute', right: '0' }} | |||
| type="primary" | |||
| color="primary" | |||
| variant="filled" | |||
| size="small" | |||
| onClick={handleDeleteAll} | |||
| icon={<KFIcon type="icon-shanchu" />} | |||
| @@ -5,7 +5,7 @@ | |||
| &__label { | |||
| color: rgba(29, 29, 32, 0.75); | |||
| font-size: 15px; | |||
| font-size: 14px; | |||
| &--running { | |||
| color: @success-color; | |||
| @@ -124,7 +124,12 @@ function MirrorInfo() { | |||
| }; | |||
| // 分页切换 | |||
| const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter, { action }) => { | |||
| const handleTableChange: TableProps<MirrorVersionData>['onChange'] = ( | |||
| pagination, | |||
| _filters, | |||
| _sorter, | |||
| { action }, | |||
| ) => { | |||
| if (action === 'paginate') { | |||
| setPagination(pagination); | |||
| } | |||
| @@ -155,7 +155,12 @@ function MirrorList() { | |||
| }; | |||
| // 分页切换 | |||
| const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter, { action }) => { | |||
| const handleTableChange: TableProps<MirrorData>['onChange'] = ( | |||
| pagination, | |||
| _filters, | |||
| _sorter, | |||
| { action }, | |||
| ) => { | |||
| if (action === 'paginate') { | |||
| setPagination(pagination); | |||
| } | |||
| @@ -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<string, number>; | |||
| params_names?: string[]; | |||
| params?: Record<string, string>; | |||
| params?: Record<string, number>; | |||
| }; | |||
| 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') { | |||
| setPagination(pagination); | |||
| } | |||
| // console.log(pagination, filters, sorter, action); | |||
| }; | |||
| const rowSelection: TableProps['rowSelection'] = { | |||
| const rowSelection: TableProps<TableData>['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<TableData>['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: ( | |||
| <Tooltip title={name}> | |||
| <span>{name}</span> | |||
| @@ -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} | |||
| ></Checkbox>{' '} | |||
| <span>训练指标</span> | |||
| </div> | |||
| ), | |||
| align: 'center', | |||
| children: first?.metrics_names?.map((name) => ({ | |||
| children: metricsNames.map((name) => ({ | |||
| title: ( | |||
| <div> | |||
| <Checkbox | |||
| @@ -215,7 +223,7 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||
| align: 'center', | |||
| render: tableCellRender(true), | |||
| 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, | |||
| })), | |||
| }, | |||
| @@ -176,7 +176,12 @@ function ModelDeployment() { | |||
| }; | |||
| // 分页切换 | |||
| const handleTableChange: TableProps['onChange'] = (pagination, _filters, _sorter, { action }) => { | |||
| const handleTableChange: TableProps<ServiceData>['onChange'] = ( | |||
| pagination, | |||
| _filters, | |||
| _sorter, | |||
| { action }, | |||
| ) => { | |||
| if (action === 'paginate') { | |||
| setPagination(pagination); | |||
| } | |||
| @@ -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<ServiceVersionData[]>([]); | |||
| const [total, setTotal] = useState(0); | |||
| const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); | |||
| const [pagination, setPagination] = useState<TablePaginationConfig>( | |||
| cacheState?.pagination ?? { | |||
| 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') { | |||
| 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'] = [ | |||
| @@ -379,13 +410,16 @@ function ServiceInfo() { | |||
| allowClear | |||
| ></Select> | |||
| <Button | |||
| style={{ marginRight: '20px', marginLeft: 'auto' }} | |||
| style={{ marginRight: '15px', marginLeft: 'auto' }} | |||
| type="default" | |||
| onClick={() => createServiceVersion(ServiceOperationType.Create)} | |||
| icon={<KFIcon type="icon-xinjian2" />} | |||
| > | |||
| 新增版本 | |||
| </Button> | |||
| <Button style={{ marginRight: '15px' }} type="default" onClick={handleVersionCompare}> | |||
| 版本对比 | |||
| </Button> | |||
| <Button | |||
| style={{ marginRight: 0 }} | |||
| type="default" | |||
| @@ -410,6 +444,7 @@ function ServiceInfo() { | |||
| showTotal: () => `共${total}条`, | |||
| }} | |||
| onChange={handleTableChange} | |||
| rowSelection={rowSelection} | |||
| rowKey="id" | |||
| /> | |||
| </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,197 @@ | |||
| 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 }} style={{ color: 'inherit' }}> | |||
| {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 }} style={{ color: 'inherit' }}> | |||
| {text} | |||
| </Typography.Text> | |||
| </div> | |||
| ); | |||
| })} | |||
| </div> | |||
| </div> | |||
| </KFModal> | |||
| ); | |||
| } | |||
| export default VersionCompareModal; | |||
| @@ -20,10 +20,6 @@ | |||
| } | |||
| } | |||
| .ant-pagination { | |||
| text-align: center; | |||
| } | |||
| .ant-input-group-addon { | |||
| display: none; | |||
| } | |||
| @@ -97,6 +97,7 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { | |||
| ))} | |||
| </div> | |||
| <Pagination | |||
| align="center" | |||
| total={total} | |||
| showSizeChanger | |||
| defaultPageSize={20} | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -104,3 +104,11 @@ export function getServiceVersionLogReq(params: any) { | |||
| params, | |||
| }); | |||
| } | |||
| // 获取服务版本对比 | |||
| export function getServiceVersionCompareReq(params: any) { | |||
| return request(`/api/mmp/service/serviceVersionCompare`, { | |||
| method: 'GET', | |||
| params, | |||
| }); | |||
| } | |||
| @@ -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; | |||
| }; | |||
| } | |||
| @@ -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; | |||
| }; | |||