| @@ -17,7 +17,7 @@ const filterResourceStandard: SelectProps<string, ComputingResource>['filterOpti | |||||
| const convertId = (item: any) => ({ | const convertId = (item: any) => ({ | ||||
| ...item, | ...item, | ||||
| id: JSON.stringify({ | id: JSON.stringify({ | ||||
| id: item.id, | |||||
| id: `${item.id}`, | |||||
| name: item.name, | name: item.name, | ||||
| identifier: item.identifier, | identifier: item.identifier, | ||||
| owner: item.owner, | owner: item.owner, | ||||
| @@ -37,7 +37,7 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = { | |||||
| const res = await getDatasetList({ | const res = await getDatasetList({ | ||||
| page: 0, | page: 0, | ||||
| size: 1000, | size: 1000, | ||||
| available_range: 0, | |||||
| is_public: false, | |||||
| }); | }); | ||||
| return res?.data?.content?.map(convertId) ?? []; | return res?.data?.content?.map(convertId) ?? []; | ||||
| }, | }, | ||||
| @@ -52,7 +52,7 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = { | |||||
| const res = await getModelList({ | const res = await getModelList({ | ||||
| page: 0, | page: 0, | ||||
| size: 1000, | size: 1000, | ||||
| available_range: 0, | |||||
| is_public: false, | |||||
| }); | }); | ||||
| return res?.data?.content?.map(convertId) ?? []; | return res?.data?.content?.map(convertId) ?? []; | ||||
| }, | }, | ||||
| @@ -101,15 +101,18 @@ body { | |||||
| ::-webkit-scrollbar { | ::-webkit-scrollbar { | ||||
| width: 8px; | width: 8px; | ||||
| height: 8px; | |||||
| background: transparent; | background: transparent; | ||||
| } | } | ||||
| ::-webkit-scrollbar-thumb { | ::-webkit-scrollbar-thumb { | ||||
| width: 8px; | width: 8px; | ||||
| height: 8px; | |||||
| background: rgba(0, 0, 0, 0.5); | background: rgba(0, 0, 0, 0.5); | ||||
| border-radius: 99px; | border-radius: 99px; | ||||
| } | } | ||||
| ::-webkit-scrollbar-track { | ::-webkit-scrollbar-track { | ||||
| width: 8px; | width: 8px; | ||||
| height: 8px; | |||||
| background: transparent; | background: transparent; | ||||
| } | } | ||||
| ul, | ul, | ||||
| @@ -14,35 +14,43 @@ | |||||
| } | } | ||||
| &__name { | &__name { | ||||
| position: relative; | |||||
| display: inline-block; | |||||
| height: 24px; | |||||
| margin: 0 10px 0 0 !important; | |||||
| margin-right: 10px; | |||||
| margin-bottom: 0 !important; | |||||
| color: @text-color; | color: @text-color; | ||||
| font-size: 16px; | font-size: 16px; | ||||
| } | } | ||||
| &__tag { | &__tag { | ||||
| padding: 4px; | |||||
| color: @primary-color; | |||||
| padding: 2px 11px; | |||||
| font-size: 12px; | font-size: 12px; | ||||
| background-color: .addAlpha(@primary-color, 0.1) []; | |||||
| border-radius: 4px; | |||||
| border-radius: 1000px; | |||||
| &--public { | |||||
| color: @primary-color; | |||||
| background-color: .addAlpha(@primary-color, 0.08) []; | |||||
| border-color: .addAlpha(@primary-color, 0.5) []; | |||||
| } | |||||
| &--private { | |||||
| color: @warning-color; | |||||
| background-color: .addAlpha(@warning-color, 0.08) []; | |||||
| border-color: .addAlpha(@warning-color, 0.5) []; | |||||
| } | |||||
| } | } | ||||
| &__url { | &__url { | ||||
| margin-bottom: 10px; | |||||
| color: @text-color-secondary; | color: @text-color-secondary; | ||||
| font-size: 14px; | font-size: 14px; | ||||
| } | } | ||||
| &__description { | |||||
| height: 44px; | |||||
| &__branch { | |||||
| margin-bottom: 20px; | margin-bottom: 20px; | ||||
| color: @text-color-secondary; | |||||
| color: @text-color-tertiary; | |||||
| font-size: 14px; | font-size: 14px; | ||||
| .multiLine(2); | |||||
| } | } | ||||
| &__user, | |||||
| &__time { | &__time { | ||||
| display: flex; | display: flex; | ||||
| flex: 0 1 content; | flex: 0 1 content; | ||||
| @@ -55,21 +63,9 @@ | |||||
| &:hover { | &:hover { | ||||
| border-color: @primary-color; | border-color: @primary-color; | ||||
| box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1); | 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: ''; | |||||
| &:hover &__name { | |||||
| color: @primary-color; | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,6 +5,7 @@ import { AvailableRange } from '@/enums'; | |||||
| import { type CodeConfigData } from '@/pages/CodeConfig/List'; | import { type CodeConfigData } from '@/pages/CodeConfig/List'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { Button, Flex, Typography } from 'antd'; | import { Button, Flex, Typography } from 'antd'; | ||||
| import classNames from 'classnames'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| type CodeConfigItemProps = { | type CodeConfigItemProps = { | ||||
| @@ -24,10 +25,16 @@ function CodeConfigItem({ item, onClick, onEdit, onRemove }: CodeConfigItemProps | |||||
| > | > | ||||
| {item.code_repo_name} | {item.code_repo_name} | ||||
| </Typography.Paragraph> | </Typography.Paragraph> | ||||
| <div className={styles['code-config-item__tag']}> | |||||
| <div | |||||
| className={classNames( | |||||
| styles['code-config-item__tag'], | |||||
| item.code_repo_vis === AvailableRange.Public | |||||
| ? styles['code-config-item__tag--public'] | |||||
| : styles['code-config-item__tag--private'], | |||||
| )} | |||||
| > | |||||
| {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} | {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} | ||||
| </div> | </div> | ||||
| <Button | <Button | ||||
| type="text" | type="text" | ||||
| shape="circle" | shape="circle" | ||||
| @@ -54,15 +61,12 @@ function CodeConfigItem({ item, onClick, onEdit, onRemove }: CodeConfigItemProps | |||||
| <Typography.Paragraph | <Typography.Paragraph | ||||
| className={styles['code-config-item__url']} | className={styles['code-config-item__url']} | ||||
| ellipsis={{ tooltip: item.git_url }} | ellipsis={{ tooltip: item.git_url }} | ||||
| style={{ marginBottom: '8px' }} | |||||
| > | > | ||||
| {item.git_url} | {item.git_url} | ||||
| </Typography.Paragraph> | </Typography.Paragraph> | ||||
| <div className={styles['code-config-item__url']} style={{ marginBottom: '20px' }}> | |||||
| {item.git_branch} | |||||
| </div> | |||||
| <div className={styles['code-config-item__branch']}>{item.git_branch}</div> | |||||
| <Flex justify="space-between"> | <Flex justify="space-between"> | ||||
| <div className={styles['code-config-item__time']}> | |||||
| <div className={styles['code-config-item__user']}> | |||||
| <img | <img | ||||
| style={{ width: '17px', marginRight: '6px' }} | style={{ width: '17px', marginRight: '6px' }} | ||||
| src={creatByImg} | src={creatByImg} | ||||
| @@ -1,7 +1,7 @@ | |||||
| import { getAccessToken } from '@/access'; | import { getAccessToken } from '@/access'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { CategoryData, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | |||||
| import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | |||||
| import { addDataset } from '@/services/dataset/index.js'; | import { addDataset } from '@/services/dataset/index.js'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | ||||
| @@ -67,6 +67,7 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr | |||||
| if (validateUploadFiles(fileList)) { | if (validateUploadFiles(fileList)) { | ||||
| const params = { | const params = { | ||||
| ...omit(formData, ['fileList']), | ...omit(formData, ['fileList']), | ||||
| dataset_source: DataSource.Create, | |||||
| dataset_version_vos: fileList.map((item) => { | dataset_version_vos: fileList.map((item) => { | ||||
| const data = item.response?.data?.[0] ?? {}; | const data = item.response?.data?.[0] ?? {}; | ||||
| return { | return { | ||||
| @@ -120,6 +121,10 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr | |||||
| required: true, | required: true, | ||||
| message: '请输入数据集版本', | message: '请输入数据集版本', | ||||
| }, | }, | ||||
| { | |||||
| pattern: /^[a-zA-Z0-9._-]+$/, | |||||
| message: '版本只支持字母、数字、下划线、点、横杠', | |||||
| }, | |||||
| { | { | ||||
| validator: (_rule, value) => { | validator: (_rule, value) => { | ||||
| if (value === 'master') { | if (value === 'master') { | ||||
| @@ -1,7 +1,7 @@ | |||||
| import { getAccessToken } from '@/access'; | import { getAccessToken } from '@/access'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { CategoryData, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | |||||
| import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | |||||
| import { addModel } from '@/services/dataset/index.js'; | import { addModel } from '@/services/dataset/index.js'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | ||||
| @@ -54,6 +54,7 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) | |||||
| if (validateUploadFiles(fileList)) { | if (validateUploadFiles(fileList)) { | ||||
| const params = { | const params = { | ||||
| ...omit(formData, ['fileList']), | ...omit(formData, ['fileList']), | ||||
| model_source: DataSource.Create, | |||||
| model_version_vos: fileList.map((item) => { | model_version_vos: fileList.map((item) => { | ||||
| const data = item.response?.data?.[0] ?? {}; | const data = item.response?.data?.[0] ?? {}; | ||||
| return { | return { | ||||
| @@ -105,6 +106,10 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) | |||||
| required: true, | required: true, | ||||
| message: '请输入模型版本', | message: '请输入模型版本', | ||||
| }, | }, | ||||
| { | |||||
| pattern: /^[a-zA-Z0-9._-]+$/, | |||||
| message: '版本只支持字母、数字、下划线、点、横杠', | |||||
| }, | |||||
| { | { | ||||
| validator: (_rule, value) => { | validator: (_rule, value) => { | ||||
| if (value === 'master') { | if (value === 'master') { | ||||
| @@ -1,7 +1,7 @@ | |||||
| import { getAccessToken } from '@/access'; | import { getAccessToken } from '@/access'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { ResourceType, resourceConfig } from '@/pages/Dataset/config'; | |||||
| import { DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | ||||
| import { | import { | ||||
| @@ -73,6 +73,7 @@ function AddVersionModal({ | |||||
| identifier, | identifier, | ||||
| [config.filePropKey]: version_vos, | [config.filePropKey]: version_vos, | ||||
| ...omit(formData, 'fileList'), | ...omit(formData, 'fileList'), | ||||
| [config.sourceParamKey]: DataSource.Create, | |||||
| }; | }; | ||||
| createDatasetVersion(params); | createDatasetVersion(params); | ||||
| } | } | ||||
| @@ -120,6 +121,10 @@ function AddVersionModal({ | |||||
| required: true, | required: true, | ||||
| message: `请输入${name}版本`, | message: `请输入${name}版本`, | ||||
| }, | }, | ||||
| { | |||||
| pattern: /^[a-zA-Z0-9._-]+$/, | |||||
| message: '版本只支持字母、数字、下划线、点、横杠', | |||||
| }, | |||||
| { | { | ||||
| validator: (_rule, value) => { | validator: (_rule, value) => { | ||||
| if (value === 'master') { | if (value === 'master') { | ||||
| @@ -145,7 +150,7 @@ function AddVersionModal({ | |||||
| <Input.TextArea | <Input.TextArea | ||||
| placeholder="请输入版本描述" | placeholder="请输入版本描述" | ||||
| autoSize={{ minRows: 2, maxRows: 6 }} | autoSize={{ minRows: 2, maxRows: 6 }} | ||||
| maxLength={256} | |||||
| maxLength={200} | |||||
| showCount | showCount | ||||
| allowClear | allowClear | ||||
| /> | /> | ||||
| @@ -16,9 +16,11 @@ | |||||
| color: @text-color; | color: @text-color; | ||||
| font-weight: 500; | font-weight: 500; | ||||
| font-size: 20px; | font-size: 20px; | ||||
| .singleLine(); | |||||
| } | } | ||||
| &__tag { | &__tag { | ||||
| flex: none; | |||||
| padding: 4px 10px; | padding: 4px 10px; | ||||
| color: @primary-color; | color: @primary-color; | ||||
| font-size: 14px; | font-size: 14px; | ||||
| @@ -24,7 +24,12 @@ function ResourceVersion({ resourceType, info }: ResourceVersionProps) { | |||||
| // 全部导出 | // 全部导出 | ||||
| const handleExport = async () => { | const handleExport = async () => { | ||||
| const url = config.downloadAllAction; | const url = config.downloadAllAction; | ||||
| downLoadZip(url, { name: info.name, id: info.id, version: info.version }); | |||||
| downLoadZip(url, { | |||||
| name: info.name, | |||||
| id: info.id, | |||||
| version: info.version, | |||||
| identifier: info.identifier, | |||||
| }); | |||||
| }; | }; | ||||
| // 单个导出 | // 单个导出 | ||||
| @@ -21,6 +21,11 @@ export enum ResourceType { | |||||
| Dataset = 'Dataset', // 数据集 | Dataset = 'Dataset', // 数据集 | ||||
| } | } | ||||
| export enum DataSource { | |||||
| Export = 'export', // 导出 | |||||
| Create = 'add', // 新增 | |||||
| } | |||||
| type ResourceTypeInfo = { | type ResourceTypeInfo = { | ||||
| getList: (params: any) => Promise<any>; // 获取资源列表 | getList: (params: any) => Promise<any>; // 获取资源列表 | ||||
| getVersions: (params: any) => Promise<any>; // 获取版本列表 | getVersions: (params: any) => Promise<any>; // 获取版本列表 | ||||
| @@ -32,6 +37,7 @@ type ResourceTypeInfo = { | |||||
| typeParamKey: 'data_type' | 'model_type'; // 类型参数名称,获取资源列表接口使用 | typeParamKey: 'data_type' | 'model_type'; // 类型参数名称,获取资源列表接口使用 | ||||
| tagParamKey: 'data_tag' | 'model_tag'; // 标签参数名称,获取资源列表接口使用 | tagParamKey: 'data_tag' | 'model_tag'; // 标签参数名称,获取资源列表接口使用 | ||||
| filePropKey: 'dataset_version_vos' | 'model_version_vos'; // 文件列表属性 | filePropKey: 'dataset_version_vos' | 'model_version_vos'; // 文件列表属性 | ||||
| sourceParamKey: 'dataset_source' | 'model_source'; // 来源参数名称 | |||||
| tabItems: TabsProps['items']; // tab 列表 | tabItems: TabsProps['items']; // tab 列表 | ||||
| typeTitle: string; // 类型标题 | typeTitle: string; // 类型标题 | ||||
| tagTitle: string; // 标签标题 | tagTitle: string; // 标签标题 | ||||
| @@ -58,6 +64,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | |||||
| typeParamKey: 'data_type', | typeParamKey: 'data_type', | ||||
| tagParamKey: 'data_tag', | tagParamKey: 'data_tag', | ||||
| filePropKey: 'dataset_version_vos', | filePropKey: 'dataset_version_vos', | ||||
| sourceParamKey: 'dataset_source', | |||||
| tabItems: [ | tabItems: [ | ||||
| { | { | ||||
| key: CommonTabKeys.Public, | key: CommonTabKeys.Public, | ||||
| @@ -80,7 +87,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | |||||
| uploadAction: '/api/mmp/newdataset/upload', | uploadAction: '/api/mmp/newdataset/upload', | ||||
| uploadAccept: '.zip,.tgz', | uploadAccept: '.zip,.tgz', | ||||
| downloadAllAction: '/api/mmp/newdataset/downloadAllFiles', | downloadAllAction: '/api/mmp/newdataset/downloadAllFiles', | ||||
| downloadSingleAction: '/api/mmp/newdataset/downloadSinggerFile', | |||||
| downloadSingleAction: '/api/mmp/newdataset/downloadSingleFile', | |||||
| }, | }, | ||||
| [ResourceType.Model]: { | [ResourceType.Model]: { | ||||
| getList: getModelList, | getList: getModelList, | ||||
| @@ -93,6 +100,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | |||||
| typeParamKey: 'model_type', | typeParamKey: 'model_type', | ||||
| tagParamKey: 'model_tag', | tagParamKey: 'model_tag', | ||||
| filePropKey: 'model_version_vos', | filePropKey: 'model_version_vos', | ||||
| sourceParamKey: 'model_source', | |||||
| tabItems: [ | tabItems: [ | ||||
| { | { | ||||
| key: CommonTabKeys.Public, | key: CommonTabKeys.Public, | ||||
| @@ -1,6 +1,6 @@ | |||||
| import editExperimentIcon from '@/assets/img/edit-experiment.png'; | import editExperimentIcon from '@/assets/img/edit-experiment.png'; | ||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { type ResourceData } from '@/pages/Dataset/config'; | |||||
| import { ResourceVersionData, type ResourceData } from '@/pages/Dataset/config'; | |||||
| import { | import { | ||||
| addModelVersion, | addModelVersion, | ||||
| exportModelReq, | exportModelReq, | ||||
| @@ -10,6 +10,7 @@ import { | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { InfoCircleOutlined } from '@ant-design/icons'; | import { InfoCircleOutlined } from '@ant-design/icons'; | ||||
| import { Form, Input, ModalProps, Select } from 'antd'; | import { Form, Input, ModalProps, Select } from 'antd'; | ||||
| import { pick } from 'lodash'; | |||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| @@ -40,7 +41,7 @@ interface ExportModelModalProps extends Omit<ModalProps, 'onOk'> { | |||||
| function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) { | function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) { | ||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const [models, setModels] = useState<ResourceData[]>([]); | const [models, setModels] = useState<ResourceData[]>([]); | ||||
| const [versions, setVersions] = useState<string[]>([]); | |||||
| const [versions, setVersions] = useState<ResourceVersionData[]>([]); | |||||
| const [uuid] = useState(Date.now()); | const [uuid] = useState(Date.now()); | ||||
| const layout = { | const layout = { | ||||
| @@ -56,8 +57,9 @@ function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) { | |||||
| const getTooltip = () => { | const getTooltip = () => { | ||||
| const id = form.getFieldValue('models_id'); | const id = form.getFieldValue('models_id'); | ||||
| const name = models.find((item) => item.id === id)?.name ?? ''; | const name = models.find((item) => item.id === id)?.name ?? ''; | ||||
| const versionNames = versions.map((item: ResourceVersionData) => item.name).join('、'); | |||||
| const tooltip = | const tooltip = | ||||
| versions.length > 0 ? `${name}有以下版本:\n${versions.join('、')}\n注意不能重复` : undefined; | |||||
| versions.length > 0 ? `${name}有以下版本:\n${versionNames}\n注意不能重复` : undefined; | |||||
| return tooltip; | return tooltip; | ||||
| }; | }; | ||||
| @@ -75,7 +77,7 @@ function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) { | |||||
| const params = { | const params = { | ||||
| page: 0, | page: 0, | ||||
| size: 1000, | size: 1000, | ||||
| available_range: 0, // 个人 | |||||
| is_public: false, // 个人 | |||||
| }; | }; | ||||
| const [res] = await to(getModelList(params)); | const [res] = await to(getModelList(params)); | ||||
| if (res && res.data) { | if (res && res.data) { | ||||
| @@ -85,7 +87,11 @@ function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) { | |||||
| // 获取模型版本列表 | // 获取模型版本列表 | ||||
| const getModelVersions = async (id: number) => { | const getModelVersions = async (id: number) => { | ||||
| const [res] = await to(getModelVersionList(id)); | |||||
| const model = models.find((item) => item.id === id); | |||||
| if (!model) { | |||||
| return; | |||||
| } | |||||
| const [res] = await to(getModelVersionList(pick(model, ['identifier', 'owner']))); | |||||
| if (res && res.data) { | if (res && res.data) { | ||||
| setVersions(res.data); | setVersions(res.data); | ||||
| } | } | ||||
| @@ -175,7 +181,7 @@ function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) { | |||||
| { required: true, message: '请输入模型版本' }, | { required: true, message: '请输入模型版本' }, | ||||
| { | { | ||||
| validator: (_, value) => { | validator: (_, value) => { | ||||
| if (value && versions.includes(value)) { | |||||
| if (value && versions.map((item) => item.name).includes(value)) { | |||||
| return Promise.reject('模型版本已存在'); | return Promise.reject('模型版本已存在'); | ||||
| } else { | } else { | ||||
| return Promise.resolve(); | return Promise.resolve(); | ||||
| @@ -1,68 +1,56 @@ | |||||
| .code-config-item { | .code-config-item { | ||||
| position: relative; | position: relative; | ||||
| padding: 20px; | |||||
| background: white; | |||||
| border: 1px solid #eaeaea; | |||||
| width: calc(25% - 7.5px); | |||||
| padding: 15px; | |||||
| background-color: .addAlpha(@primary-color, 0.04) []; | |||||
| border: 1px solid transparent; | |||||
| border-radius: 4px; | border-radius: 4px; | ||||
| cursor: pointer; | cursor: pointer; | ||||
| &__name { | &__name { | ||||
| position: relative; | |||||
| display: inline-block; | |||||
| height: 24px; | |||||
| margin: 0 10px 0 0 !important; | |||||
| margin-right: 8px; | |||||
| margin-bottom: 0 !important; | |||||
| color: @text-color; | color: @text-color; | ||||
| font-size: 16px; | |||||
| font-size: 14px; | |||||
| } | } | ||||
| &__tag { | &__tag { | ||||
| padding: 4px; | |||||
| color: @primary-color; | |||||
| flex: none; | |||||
| padding: 2px 11px; | |||||
| font-size: 12px; | font-size: 12px; | ||||
| background-color: .addAlpha(@primary-color, 0.1) []; | |||||
| border-radius: 4px; | |||||
| } | |||||
| border-radius: 1000px; | |||||
| &__url { | |||||
| color: @text-color-secondary; | |||||
| font-size: 14px; | |||||
| &--public { | |||||
| color: @primary-color; | |||||
| background-color: .addAlpha(@primary-color, 0.08) []; | |||||
| border-color: .addAlpha(@primary-color, 0.5) []; | |||||
| } | |||||
| &--private { | |||||
| color: @warning-color; | |||||
| background-color: .addAlpha(@warning-color, 0.08) []; | |||||
| border-color: .addAlpha(@warning-color, 0.5) []; | |||||
| } | |||||
| } | } | ||||
| &__description { | |||||
| height: 44px; | |||||
| margin-bottom: 20px; | |||||
| &__url { | |||||
| height: 40px; | |||||
| margin-bottom: 10px !important; | |||||
| color: @text-color-secondary; | color: @text-color-secondary; | ||||
| font-size: 14px; | |||||
| .multiLine(2); | |||||
| font-size: 13px; | |||||
| } | } | ||||
| &__time { | |||||
| display: flex; | |||||
| flex: 0 1 content; | |||||
| align-items: center; | |||||
| width: 100%; | |||||
| color: #808080; | |||||
| font-size: 13px; | |||||
| &__branch { | |||||
| color: @text-color-tertiary; | |||||
| font-size: 12px; | |||||
| } | } | ||||
| &:hover { | &:hover { | ||||
| border-color: @primary-color; | border-color: @primary-color; | ||||
| box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1); | 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: ''; | |||||
| &:hover &__name { | |||||
| color: @primary-color; | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| import { AvailableRange } from '@/enums'; | import { AvailableRange } from '@/enums'; | ||||
| import { type CodeConfigData } from '@/pages/CodeConfig/List'; | import { type CodeConfigData } from '@/pages/CodeConfig/List'; | ||||
| import { Flex, Typography } from 'antd'; | import { Flex, Typography } from 'antd'; | ||||
| import classNames from 'classnames'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| type CodeConfigItemProps = { | type CodeConfigItemProps = { | ||||
| @@ -11,27 +12,31 @@ type CodeConfigItemProps = { | |||||
| function CodeConfigItem({ item, onClick }: CodeConfigItemProps) { | function CodeConfigItem({ item, onClick }: CodeConfigItemProps) { | ||||
| return ( | return ( | ||||
| <div className={styles['code-config-item']} onClick={() => onClick?.(item)}> | <div className={styles['code-config-item']} onClick={() => onClick?.(item)}> | ||||
| <Flex justify="space-between" align="center" style={{ marginBottom: '20px', height: '32px' }}> | |||||
| <Flex justify="space-between" align="center" style={{ marginBottom: '15px' }}> | |||||
| <Typography.Paragraph | <Typography.Paragraph | ||||
| className={styles['code-config-item__name']} | className={styles['code-config-item__name']} | ||||
| ellipsis={{ tooltip: item.code_repo_name }} | ellipsis={{ tooltip: item.code_repo_name }} | ||||
| > | > | ||||
| {item.code_repo_name} | {item.code_repo_name} | ||||
| </Typography.Paragraph> | </Typography.Paragraph> | ||||
| <div className={styles['code-config-item__tag']}> | |||||
| <div | |||||
| className={classNames( | |||||
| styles['code-config-item__tag'], | |||||
| item.code_repo_vis === AvailableRange.Public | |||||
| ? styles['code-config-item__tag--public'] | |||||
| : styles['code-config-item__tag--private'], | |||||
| )} | |||||
| > | |||||
| {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} | {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} | ||||
| </div> | </div> | ||||
| </Flex> | </Flex> | ||||
| <Typography.Paragraph | <Typography.Paragraph | ||||
| className={styles['code-config-item__url']} | className={styles['code-config-item__url']} | ||||
| ellipsis={{ tooltip: item.git_url }} | |||||
| style={{ marginBottom: '8px' }} | |||||
| ellipsis={{ rows: 2, tooltip: item.git_url }} | |||||
| > | > | ||||
| {item.git_url} | {item.git_url} | ||||
| </Typography.Paragraph> | </Typography.Paragraph> | ||||
| <div className={styles['code-config-item__url']} style={{ marginBottom: '20px' }}> | |||||
| {item.git_branch} | |||||
| </div> | |||||
| <div className={styles['code-config-item__branch']}>{item.git_branch}</div> | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -2,28 +2,53 @@ | |||||
| width: 100%; | width: 100%; | ||||
| height: 100%; | height: 100%; | ||||
| &__search { | |||||
| width: 100%; | |||||
| } | |||||
| :global { | :global { | ||||
| .ant-input-affix-wrapper .ant-input-prefix { | |||||
| margin-inline-end: 12px; | |||||
| .ant-input-affix-wrapper { | |||||
| border-radius: 23px !important; | |||||
| .ant-input-prefix { | |||||
| margin-inline-end: 12px; | |||||
| } | |||||
| .ant-input-suffix { | |||||
| margin-inline-end: 12px; | |||||
| } | |||||
| .ant-input-clear-icon { | |||||
| font-size: 16px; | |||||
| } | |||||
| } | } | ||||
| .ant-pagination { | .ant-pagination { | ||||
| text-align: right; | |||||
| text-align: center; | |||||
| } | } | ||||
| .ant-input-group-addon { | .ant-input-group-addon { | ||||
| display: none; | display: none; | ||||
| } | } | ||||
| .ant-pagination { | |||||
| .ant-select-single { | |||||
| height: 32px !important; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| &__content { | &__content { | ||||
| display: grid; | |||||
| grid-template-columns: repeat(3, minmax(0, 1fr)); | |||||
| gap: 20px; | |||||
| display: flex; | |||||
| flex-direction: row; | |||||
| flex-wrap: wrap; | |||||
| gap: 10px; | |||||
| width: 100%; | width: 100%; | ||||
| margin-top: 30px; | |||||
| max-height: 50vh; | |||||
| margin-top: 24px; | |||||
| margin-bottom: 30px; | margin-bottom: 30px; | ||||
| overflow-x: hidden; | overflow-x: hidden; | ||||
| overflow-y: auto; | overflow-y: auto; | ||||
| } | } | ||||
| &__empty { | |||||
| padding-top: 40px; | |||||
| } | |||||
| } | } | ||||
| @@ -8,6 +8,7 @@ import KFModal from '@/components/KFModal'; | |||||
| import { type CodeConfigData } from '@/pages/CodeConfig/List'; | import { type CodeConfigData } from '@/pages/CodeConfig/List'; | ||||
| import { getCodeConfigListReq } from '@/services/codeConfig'; | import { getCodeConfigListReq } from '@/services/codeConfig'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { Icon } from '@umijs/max'; | |||||
| import type { ModalProps, PaginationProps } from 'antd'; | import type { ModalProps, PaginationProps } from 'antd'; | ||||
| import { Empty, Input, Pagination } from 'antd'; | import { Empty, Input, Pagination } from 'antd'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| @@ -74,15 +75,17 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { | |||||
| > | > | ||||
| <div className={styles['code-selector']}> | <div className={styles['code-selector']}> | ||||
| <Input.Search | <Input.Search | ||||
| className={styles['code-selector__search']} | |||||
| placeholder="按代码仓库名称筛选" | placeholder="按代码仓库名称筛选" | ||||
| allowClear | allowClear | ||||
| onSearch={handleSearch} | onSearch={handleSearch} | ||||
| style={{ | |||||
| width: '100%', | |||||
| }} | |||||
| size="large" | |||||
| onChange={(e) => setInputText(e.target.value)} | onChange={(e) => setInputText(e.target.value)} | ||||
| suffix={null} | suffix={null} | ||||
| value={inputText} | value={inputText} | ||||
| prefix={ | |||||
| <Icon icon="local:magnifying-glass" style={{ marginLeft: '10px', marginTop: '2px' }} /> | |||||
| } | |||||
| /> | /> | ||||
| {dataList?.length !== 0 ? ( | {dataList?.length !== 0 ? ( | ||||
| <> | <> | ||||
| @@ -103,7 +106,7 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { | |||||
| </> | </> | ||||
| ) : ( | ) : ( | ||||
| <div className={styles['code-selector__empty']}> | <div className={styles['code-selector__empty']}> | ||||
| <Empty></Empty> | |||||
| <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty> | |||||
| </div> | </div> | ||||
| )} | )} | ||||
| </div> | </div> | ||||
| @@ -50,10 +50,12 @@ | |||||
| border-radius: 8px; | border-radius: 8px; | ||||
| &__title { | &__title { | ||||
| height: 46px; | |||||
| margin-bottom: 15px; | margin-bottom: 15px; | ||||
| padding: 3px 0 6px; | padding: 3px 0 6px; | ||||
| color: @text-color; | color: @text-color; | ||||
| font-size: @font-size; | font-size: @font-size; | ||||
| line-height: 46px; | |||||
| border-bottom: 1px solid @border-color-secondary; | border-bottom: 1px solid @border-color-secondary; | ||||
| } | } | ||||
| &__files { | &__files { | ||||
| @@ -48,7 +48,7 @@ | |||||
| &__count { | &__count { | ||||
| width: 24px; | width: 24px; | ||||
| height: 24px; | height: 24px; | ||||
| color: #8a8a8a; | |||||
| color: @text-color-tertiary; | |||||
| font-size: 12px; | font-size: 12px; | ||||
| line-height: 24px; | line-height: 24px; | ||||
| text-align: center; | text-align: center; | ||||
| @@ -3,7 +3,14 @@ | |||||
| * @Date: 2024-03-25 13:52:54 | * @Date: 2024-03-25 13:52:54 | ||||
| * @Description: 网络请求配置,详情请参考 https://umijs.org/docs/max/request | * @Description: 网络请求配置,详情请参考 https://umijs.org/docs/max/request | ||||
| */ | */ | ||||
| import type { AxiosRequestConfig, AxiosResponse, RequestConfig, RequestOptions } from '@umijs/max'; | |||||
| import type { | |||||
| AxiosError, | |||||
| AxiosRequestConfig, | |||||
| AxiosResponse, | |||||
| RequestConfig, | |||||
| RequestError, | |||||
| RequestOptions, | |||||
| } from '@umijs/max'; | |||||
| import { message } from 'antd'; | import { message } from 'antd'; | ||||
| import { clearSessionToken, getAccessToken } from './access'; | import { clearSessionToken, getAccessToken } from './access'; | ||||
| import { setRemoteMenu } from './services/session'; | import { setRemoteMenu } from './services/session'; | ||||
| @@ -12,13 +19,12 @@ import { gotoLoginPage } from './utils/ui'; | |||||
| // [antd: Notification] You are calling notice in render which will break in React 18 concurrent mode. Please trigger in effect instead. | // [antd: Notification] You are calling notice in render which will break in React 18 concurrent mode. Please trigger in effect instead. | ||||
| const popupError = (error: string, skipErrorHandler: boolean | undefined = false) => { | const popupError = (error: string, skipErrorHandler: boolean | undefined = false) => { | ||||
| if (skipErrorHandler) { | |||||
| return; | |||||
| if (!skipErrorHandler) { | |||||
| // 直接调用 message.error 有时候不弹出来 | |||||
| setTimeout(() => { | |||||
| message.error(error); | |||||
| }, 100); | |||||
| } | } | ||||
| // 直接调用 message.error 有时候不弹出来 | |||||
| setTimeout(() => { | |||||
| message.error(error); | |||||
| }, 100); | |||||
| }; | }; | ||||
| /** | /** | ||||
| @@ -32,22 +38,29 @@ export const requestConfig: RequestConfig = { | |||||
| const headers = options.headers ?? {}; | const headers = options.headers ?? {}; | ||||
| const authHeader = headers['Authorization']; | const authHeader = headers['Authorization']; | ||||
| const isToken = headers['isToken']; | const isToken = headers['isToken']; | ||||
| const skipLoading = (options as RequestOptions)?.skipLoading; | |||||
| if (!authHeader && isToken !== false) { | if (!authHeader && isToken !== false) { | ||||
| const accessToken = getAccessToken(); | const accessToken = getAccessToken(); | ||||
| if (accessToken) { | if (accessToken) { | ||||
| headers['Authorization'] = `Bearer ${accessToken}`; | headers['Authorization'] = `Bearer ${accessToken}`; | ||||
| } | } | ||||
| } | } | ||||
| Loading.show(); | |||||
| if (!skipLoading) { | |||||
| Loading.show(); | |||||
| } | |||||
| return { url, options }; | return { url, options }; | ||||
| }, | }, | ||||
| ], | ], | ||||
| responseInterceptors: [ | responseInterceptors: [ | ||||
| [ | [ | ||||
| (response: AxiosResponse) => { | (response: AxiosResponse) => { | ||||
| Loading.hide(); | |||||
| const { status, data, config } = response || {}; | const { status, data, config } = response || {}; | ||||
| const skipErrorHandler = (config as RequestOptions)?.skipErrorHandler; | |||||
| const options = config as RequestOptions; | |||||
| const skipErrorHandler = options?.skipErrorHandler; | |||||
| const skipLoading = options?.skipLoading; | |||||
| if (!skipLoading) { | |||||
| Loading.hide(); | |||||
| } | |||||
| if (status >= 200 && status < 300) { | if (status >= 200 && status < 300) { | ||||
| if (data && (data instanceof Blob || data.code === 200)) { | if (data && (data instanceof Blob || data.code === 200)) { | ||||
| return response; | return response; | ||||
| @@ -66,9 +79,14 @@ export const requestConfig: RequestConfig = { | |||||
| return Promise.reject(response); | return Promise.reject(response); | ||||
| } | } | ||||
| }, | }, | ||||
| (error: Error) => { | |||||
| Loading.hide(); | |||||
| popupError(error.message ?? '请求失败'); | |||||
| (error: RequestError) => { | |||||
| const options = (error as AxiosError).config as RequestOptions; | |||||
| const skipErrorHandler = options?.skipErrorHandler; | |||||
| const skipLoading = options?.skipLoading; | |||||
| if (!skipLoading) { | |||||
| Loading.hide(); | |||||
| } | |||||
| popupError(error.message ?? '请求失败', skipErrorHandler); | |||||
| return Promise.reject(error); | return Promise.reject(error); | ||||
| }, | }, | ||||
| ], | ], | ||||
| @@ -67,14 +67,6 @@ export function downloadAllFiles(params) { | |||||
| }); | }); | ||||
| } | } | ||||
| // 下载数据集单个文件 | |||||
| export function downloadSingleFile(params) { | |||||
| return request(`/api/mmp/newdataset/downloadSinggerFile`, { | |||||
| method: 'GET', | |||||
| params, | |||||
| }); | |||||
| } | |||||
| // 删除数据集版本 | // 删除数据集版本 | ||||
| export function deleteDatasetVersion(params) { | export function deleteDatasetVersion(params) { | ||||
| return request(`/api/mmp/newdataset/deleteDatasetVersion`, { | return request(`/api/mmp/newdataset/deleteDatasetVersion`, { | ||||
| @@ -103,7 +103,7 @@ export function putExperiment(data) { | |||||
| }); | }); | ||||
| } | } | ||||
| // 启动tensorBoard | |||||
| // 启动 tensorBoard | |||||
| export function runTensorBoardReq(data) { | export function runTensorBoardReq(data) { | ||||
| return request(`/api/mmp/tensorBoard/run`, { | return request(`/api/mmp/tensorBoard/run`, { | ||||
| method: 'POST', | method: 'POST', | ||||
| @@ -111,11 +111,12 @@ export function runTensorBoardReq(data) { | |||||
| }); | }); | ||||
| } | } | ||||
| // 启动tensorBoard | |||||
| // 获取 tensorBoard 状态 | |||||
| export function getTensorBoardStatusReq(data) { | export function getTensorBoardStatusReq(data) { | ||||
| return request(`/api/mmp/tensorBoard/getStatus`, { | return request(`/api/mmp/tensorBoard/getStatus`, { | ||||
| method: 'POST', | method: 'POST', | ||||
| data, | data, | ||||
| skipLoading: true, | |||||
| }); | }); | ||||
| } | } | ||||
| @@ -11,6 +11,7 @@ | |||||
| @background-color: #f9fafb; // 页面背景颜色 | @background-color: #f9fafb; // 页面背景颜色 | ||||
| @text-color: #1d1d20; | @text-color: #1d1d20; | ||||
| @text-color-secondary: #575757; | @text-color-secondary: #575757; | ||||
| @text-color-tertiary: #8a8a8a; | |||||
| @success-color: #6ac21d; | @success-color: #6ac21d; | ||||
| @error-color: #c73131; | @error-color: #c73131; | ||||
| @warning-color: #f98e1b; | @warning-color: #f98e1b; | ||||
| @@ -18,6 +19,7 @@ | |||||
| @pending-color: #ecb934; | @pending-color: #ecb934; | ||||
| @underline-color: #5d93ff; | @underline-color: #5d93ff; | ||||
| @border-color-base: #eaeaea; | |||||
| @border-color: rgba(22, 100, 255, 0.3); | @border-color: rgba(22, 100, 255, 0.3); | ||||
| @border-color-secondary: rgba(22, 100, 255, 0.1); | @border-color-secondary: rgba(22, 100, 255, 0.1); | ||||
| @background-color-primary: rgba(22, 100, 255, 0.03); | @background-color-primary: rgba(22, 100, 255, 0.03); | ||||
| @@ -25,7 +27,7 @@ | |||||
| @heading-color: rgba(0, 0, 0, 0.85); | @heading-color: rgba(0, 0, 0, 0.85); | ||||
| @input-icon-hover-color: rgba(0, 0, 0, 0.85); | @input-icon-hover-color: rgba(0, 0, 0, 0.85); | ||||
| @border-color-base: #d9d9d9; | |||||
| @link-hover-color: #69b1ff; | @link-hover-color: #69b1ff; | ||||
| @sider-background-color: #f2f5f7; | @sider-background-color: #f2f5f7; | ||||
| @@ -74,6 +76,7 @@ | |||||
| warningColor: @warning-color; | warningColor: @warning-color; | ||||
| textColor: @text-color; | textColor: @text-color; | ||||
| textColorSecondary: @text-color-secondary; | textColorSecondary: @text-color-secondary; | ||||
| textColorTertiary: @text-color-tertiary; | |||||
| fontSize: @font-size; | fontSize: @font-size; | ||||
| fontSizeTitle: @font-size-title; | fontSizeTitle: @font-size-title; | ||||
| fontSizeContent: @font-size-content; | fontSizeContent: @font-size-content; | ||||