diff --git a/react-ui/src/components/BasicTableInfo/index.tsx b/react-ui/src/components/BasicTableInfo/index.tsx index 104bc2bb..571c4b5b 100644 --- a/react-ui/src/components/BasicTableInfo/index.tsx +++ b/react-ui/src/components/BasicTableInfo/index.tsx @@ -32,9 +32,9 @@ export default function BasicTableInfo({ return (
- {showDatas.map((item) => ( + {showDatas.map((item, index) => ( = { page: pagination.current! - 1, size: pagination.pageSize, - ml_name: searchText, + ml_name: searchText || undefined, }; const [res] = await to(getAutoMLListReq(params)); if (res && res.data) { @@ -76,6 +76,10 @@ function AutoMLList() { // 搜索 const onSearch: SearchProps['onSearch'] = (value) => { setSearchText(value); + setPagination((prev) => ({ + ...prev, + current: 1, + })); }; // 删除模型部署 diff --git a/react-ui/src/pages/CodeConfig/List/index.tsx b/react-ui/src/pages/CodeConfig/List/index.tsx index e8a2557f..2efef04c 100644 --- a/react-ui/src/pages/CodeConfig/List/index.tsx +++ b/react-ui/src/pages/CodeConfig/List/index.tsx @@ -59,7 +59,7 @@ function CodeConfigList() { const params = { page: pagination.current! - 1, size: pagination.pageSize, - code_repo_name: searchText !== '' ? searchText : undefined, + code_repo_name: searchText || undefined, }; const [res] = await to(getCodeConfigListReq(params)); if (res && res.data && res.data.content) { @@ -83,6 +83,10 @@ function CodeConfigList() { // 搜索 const handleSearch = (value: string) => { setSearchText(value); + setPagination((prev) => ({ + ...prev, + current: 1, + })); }; // 删除 diff --git a/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx b/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx index 87d215bd..e592f740 100644 --- a/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx +++ b/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx @@ -4,7 +4,7 @@ import KFModal from '@/components/KFModal'; import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config'; import { addDataset } from '@/services/dataset/index.js'; import { to } from '@/utils/promise'; -import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; +import { getFileListFromEvent, limitUploadFileType, validateUploadFiles } from '@/utils/ui'; import { Button, Form, @@ -42,6 +42,8 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr Authorization: getAccessToken() || '', }, defaultFileList: [], + accept: '.zip,.tgz', + beforeUpload: limitUploadFileType('zip,tgz'), }; // 获取集群版本数据 @@ -200,7 +202,7 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr }, ]} > - +
); } diff --git a/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx b/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx index 106df228..1037e1b9 100644 --- a/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx +++ b/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx @@ -119,6 +119,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { resourceType: resourceType, resourceId: resourceId, resoureName: info.name, + owner: info.owner, identifier: info.identifier, is_public: info.is_public, onOk: () => { diff --git a/react-ui/src/pages/Dataset/components/ResourceList/index.tsx b/react-ui/src/pages/Dataset/components/ResourceList/index.tsx index 9e872aca..6f2c7523 100644 --- a/react-ui/src/pages/Dataset/components/ResourceList/index.tsx +++ b/react-ui/src/pages/Dataset/components/ResourceList/index.tsx @@ -89,7 +89,7 @@ function ResourceList( is_public: isPublic, [config.typeParamKey]: dataType, [config.tagParamKey]: dataTag, - name: searchText !== '' ? searchText : undefined, + name: searchText || undefined, }; const request = config.getList; const [res] = await to(request(params)); @@ -115,6 +115,10 @@ function ResourceList( // 搜索 const handleSearch = (value: string) => { setSearchText(value); + setPagination((prev) => ({ + ...prev, + current: 1, + })); }; // 删除 diff --git a/react-ui/src/pages/Dataset/config.tsx b/react-ui/src/pages/Dataset/config.tsx index 2ae165bc..c5b53afb 100644 --- a/react-ui/src/pages/Dataset/config.tsx +++ b/react-ui/src/pages/Dataset/config.tsx @@ -14,7 +14,8 @@ import { getModelList, getModelVersionList, } from '@/services/dataset/index.js'; -import type { TabsProps } from 'antd'; +import { limitUploadFileType } from '@/utils/ui'; +import type { TabsProps, UploadFile } from 'antd'; export enum ResourceType { Model = 'Model', // 模型 @@ -49,6 +50,7 @@ type ResourceTypeInfo = { addBtnTitle: string; // 新增按钮的title uploadAction: string; // 上传接口 url uploadAccept?: string; // 上传文件类型 + beforeUpload?: (file: UploadFile) => boolean | string; downloadAllAction: string; // 批量下载接口 url downloadSingleAction: string; // 单个下载接口 url }; @@ -87,6 +89,7 @@ export const resourceConfig: Record = { addBtnTitle: '新建数据集', uploadAction: '/api/mmp/newdataset/upload', uploadAccept: '.zip,.tgz', + beforeUpload: limitUploadFileType('zip,tgz'), downloadAllAction: '/api/mmp/newdataset/downloadAllFiles', downloadSingleAction: '/api/mmp/newdataset/downloadSingleFile', }, diff --git a/react-ui/src/pages/Mirror/List/index.tsx b/react-ui/src/pages/Mirror/List/index.tsx index 76f289b7..629563d4 100644 --- a/react-ui/src/pages/Mirror/List/index.tsx +++ b/react-ui/src/pages/Mirror/List/index.tsx @@ -87,7 +87,7 @@ function MirrorList() { const params: Record = { page: pagination.current! - 1, size: pagination.pageSize, - name: searchText, + name: searchText || undefined, image_type: activeTab === CommonTabKeys.Public ? 1 : 0, }; const [res] = await to(getMirrorListReq(params)); @@ -120,6 +120,10 @@ function MirrorList() { // 搜索 const onSearch: SearchProps['onSearch'] = (value) => { setSearchText(value); + setPagination((prev) => ({ + ...prev, + current: 1, + })); }; // 查看详情 diff --git a/react-ui/src/pages/ModelDeployment/List/index.tsx b/react-ui/src/pages/ModelDeployment/List/index.tsx index 6e61c3ab..0632e547 100644 --- a/react-ui/src/pages/ModelDeployment/List/index.tsx +++ b/react-ui/src/pages/ModelDeployment/List/index.tsx @@ -69,7 +69,7 @@ function ModelDeployment() { const params: Record = { page: pagination.current! - 1, size: pagination.pageSize, - service_name: searchText, + service_name: searchText || undefined, service_type: serviceType, }; const [res] = await to(getServiceListReq(params)); @@ -102,6 +102,10 @@ function ModelDeployment() { // 搜索 const onSearch: SearchProps['onSearch'] = (value) => { setSearchText(value); + setPagination((prev) => ({ + ...prev, + current: 1, + })); }; // 处理删除 diff --git a/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx b/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx index f57c86cb..037bbb72 100644 --- a/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx +++ b/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx @@ -110,7 +110,7 @@ function ServiceInfo() { const params: Record = { page: pagination.current! - 1, size: pagination.pageSize, - version: searchText, + version: searchText || undefined, run_state: serviceStatus, service_id: serviceId, }; @@ -159,6 +159,10 @@ function ServiceInfo() { // 搜索 const onSearch: SearchProps['onSearch'] = (value) => { setSearchText(value); + setPagination((prev) => ({ + ...prev, + current: 1, + })); }; // 处理删除 diff --git a/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx b/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx index 24a54293..13198a78 100644 --- a/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx +++ b/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx @@ -40,7 +40,7 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { const params = { page: pagination.current! - 1, size: pagination.pageSize, - code_repo_name: searchText !== '' ? searchText : undefined, + code_repo_name: searchText || undefined, }; const [res] = await to(getCodeConfigListReq(params)); if (res && res.data && res.data.content) { @@ -52,6 +52,10 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { // 搜索 const handleSearch = (value: string) => { setSearchText(value); + setPagination((prev) => ({ + ...prev, + current: 1, + })); }; const handleClick = (item: CodeConfigData) => { diff --git a/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx b/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx index c4e8af4a..80e3ab3c 100644 --- a/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx +++ b/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx @@ -44,14 +44,14 @@ const convertMirrorToTreeData = (list: MirrorData[]): TreeDataNode[] => { })); }; -// 数据集版本列表转为树形结构 -const convertDatasetVersionToTreeData = ( +// 数据集、模型版本列表转为树形结构 +const convertResourceVersionToTreeData = ( parentId: string, info: ResourceData, list: ResourceVersionData[], ): TreeDataNode[] => { return list.map((item: ResourceVersionData) => ({ - ...pick(info, ['id', 'name', 'owner', 'identifier']), + ...pick(info, ['id', 'name', 'owner', 'identifier', 'is_public']), version: item.name, title: item.name, key: `${parentId}-${item.name}`, @@ -85,6 +85,7 @@ interface SelectorTypeInfo { readonly buttontTitle: string; // 按钮 title } +// 数据集选择 export class DatasetSelector implements SelectorTypeInfo { readonly name = '数据集'; readonly modalIcon = datasetImg; @@ -114,14 +115,14 @@ export class DatasetSelector implements SelectorTypeInfo { const res = await getDatasetVersionList(pick(parentNode, ['owner', 'identifier'])); if (res && res.data) { const list = res.data; - return convertDatasetVersionToTreeData(parentKey, parentNode, list); + return convertResourceVersionToTreeData(parentKey, parentNode, list); } else { return Promise.reject('获取数据集版本列表失败'); } } async getFiles(_parentKey: string, parentNode: ResourceData & ResourceVersionData) { - const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version']); + const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version', 'is_public']); const res = await getDatasetInfo(params); if (res && res.data) { const path = res.data.relative_paths || ''; @@ -136,6 +137,7 @@ export class DatasetSelector implements SelectorTypeInfo { } } +// 模型选择 export class ModelSelector implements SelectorTypeInfo { readonly name = '模型'; readonly modalIcon = modelImg; @@ -165,14 +167,14 @@ export class ModelSelector implements SelectorTypeInfo { const res = await getModelVersionList(pick(parentNode, ['owner', 'identifier'])); if (res && res.data) { const list = res.data; - return convertDatasetVersionToTreeData(key, parentNode, list); + return convertResourceVersionToTreeData(key, parentNode, list); } else { return Promise.reject('获取模型版本列表失败'); } } async getFiles(_parentKey: string, parentNode: ResourceData & ResourceVersionData) { - const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version']); + const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version', 'is_public']); const res = await getModelInfo(params); if (res && res.data) { const path = res.data.relative_paths || ''; @@ -187,6 +189,7 @@ export class ModelSelector implements SelectorTypeInfo { } } +// 镜像选择 export class MirrorSelector implements SelectorTypeInfo { readonly name = '镜像'; readonly modalIcon = mirrorImg; diff --git a/react-ui/src/pages/System/User/edit.tsx b/react-ui/src/pages/System/User/edit.tsx index 8d24b539..71252d51 100644 --- a/react-ui/src/pages/System/User/edit.tsx +++ b/react-ui/src/pages/System/User/edit.tsx @@ -221,7 +221,7 @@ const UserForm: React.FC = (props) => { allowClear rules={[ { - required: false, + required: true, message: , }, ]} diff --git a/react-ui/src/utils/ui.tsx b/react-ui/src/utils/ui.tsx index c3d2c44a..a3a214ec 100644 --- a/react-ui/src/utils/ui.tsx +++ b/react-ui/src/utils/ui.tsx @@ -8,7 +8,7 @@ import { removeAllPageCacheState } from '@/hooks/pageCacheState'; import themes from '@/styles/theme.less'; import { type ClientInfo } from '@/types'; import { history } from '@umijs/max'; -import { Modal, message, type ModalFuncProps, type UploadFile } from 'antd'; +import { Modal, Upload, message, type ModalFuncProps, type UploadFile } from 'antd'; import { closeAllModals } from './modal'; import SessionStorage from './sessionStorage'; @@ -52,23 +52,6 @@ export function modalConfirm({ }); } -// 从事件中获取上传文件列表,用于 Upload + Form 中 -export const getFileListFromEvent = (e: any) => { - const fileList: UploadFile[] = (Array.isArray(e) ? e : e?.fileList) || []; - return fileList.map((item) => { - if (item.status === 'done') { - const { response } = item; - if (response?.code !== 200) { - return { - ...item, - status: 'error', - }; - } - } - return item; - }); -}; - /** * 跳转到登录页 * @param toHome 是否跳转到首页 @@ -97,6 +80,23 @@ export const gotoOAuth2 = () => { } }; +// 从事件中获取上传文件列表,用于 Upload + Form 中 +export const getFileListFromEvent = (e: any) => { + const fileList: UploadFile[] = (Array.isArray(e) ? e : e?.fileList) || []; + return fileList.map((item) => { + if (item.status === 'done') { + const { response } = item; + if (response?.code !== 200) { + return { + ...item, + status: 'error', + }; + } + } + return item; + }); +}; + /** * 验证文件上传 * @@ -128,6 +128,20 @@ export const validateUploadFiles = (files: UploadFile[], required: boolean = tru return !hasError; }; +// 限制上传文件类型 +export const limitUploadFileType = (type: string) => { + return (file: UploadFile): boolean | string => { + const acceptTypes = type.split(',').map((item) => item.trim()); + const fileType = file.name.split('.').pop()?.trim(); + if (!(fileType && acceptTypes.includes(fileType))) { + message.error(`文件类型不正确,只支持 "${acceptTypes.join('、')}" 类型的文件`); + file.status = 'error'; + return Upload.LIST_IGNORE; + } + return true; + }; +}; + /** * 滚动到底部 *