diff --git a/react-ui/src/components/PageTitle/index.less b/react-ui/src/components/PageTitle/index.less index 80d18169..8ab36193 100644 --- a/react-ui/src/components/PageTitle/index.less +++ b/react-ui/src/components/PageTitle/index.less @@ -3,5 +3,5 @@ align-items: center; height: 50px; padding-left: 30px; - background-image: url('../../assets/img/page-title-bg.png'); + background-image: url(@/assets/img/page-title-bg.png); } diff --git a/react-ui/src/hooks/index.ts b/react-ui/src/hooks/index.ts index 1d56a24d..4dccabb7 100644 --- a/react-ui/src/hooks/index.ts +++ b/react-ui/src/hooks/index.ts @@ -126,3 +126,13 @@ export const useResetFormOnCloseModal = (form: FormInstance, open: boolean) => { } }, [form, prevOpen, open]); }; + +export const useInputModel = (initialValue: T) => { + const [value, setValue] = useState(initialValue); + + const updateValue = useCallback((e: any) => { + setValue(e.target?.value); + }, []); + + return [value, updateValue]; +}; diff --git a/react-ui/src/iconfont/iconfont.js b/react-ui/src/iconfont/iconfont.js index 77f8486f..80008ba1 100644 --- a/react-ui/src/iconfont/iconfont.js +++ b/react-ui/src/iconfont/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_4511447='',function(t){var a=(a=document.getElementsByTagName("script"))[a.length-1],h=a.getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var l,i,v,o,z,m=function(a,h){h.parentNode.insertBefore(a,h)};if(h&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}l=function(){var a,h=document.createElement("div");h.innerHTML=t._iconfont_svg_string_4511447,(h=h.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",h=h,(a=document.body).firstChild?m(h,a.firstChild):a.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(i=function(){document.removeEventListener("DOMContentLoaded",i,!1),l()},document.addEventListener("DOMContentLoaded",i,!1)):document.attachEvent&&(v=l,o=t.document,z=!1,p(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,n())})}function n(){z||(z=!0,v())}function p(){try{o.documentElement.doScroll("left")}catch(a){return void setTimeout(p,50)}n()}}(window); \ No newline at end of file +window._iconfont_svg_string_4511447='',function(t){var a=(a=document.getElementsByTagName("script"))[a.length-1],h=a.getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var v,l,i,z,o,m=function(a,h){h.parentNode.insertBefore(a,h)};if(h&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}v=function(){var a,h=document.createElement("div");h.innerHTML=t._iconfont_svg_string_4511447,(h=h.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",h=h,(a=document.body).firstChild?m(h,a.firstChild):a.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(v,0):(l=function(){document.removeEventListener("DOMContentLoaded",l,!1),v()},document.addEventListener("DOMContentLoaded",l,!1)):document.attachEvent&&(i=v,z=t.document,o=!1,n(),z.onreadystatechange=function(){"complete"==z.readyState&&(z.onreadystatechange=null,p())})}function p(){o||(o=!0,i())}function n(){try{z.documentElement.doScroll("left")}catch(a){return void setTimeout(n,50)}p()}}(window); \ No newline at end of file diff --git a/react-ui/src/pages/Dataset/components/AddDatasetModal/index.less b/react-ui/src/pages/Dataset/components/AddDatasetModal/index.less new file mode 100644 index 00000000..529521af --- /dev/null +++ b/react-ui/src/pages/Dataset/components/AddDatasetModal/index.less @@ -0,0 +1,9 @@ +.upload-tip { + margin-top: 5px; + color: @error-color; +} + +.upload-button { + height: 48px; + font-size: 15px; +} diff --git a/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx b/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx new file mode 100644 index 00000000..c83d5143 --- /dev/null +++ b/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx @@ -0,0 +1,202 @@ +import { getAccessToken } from '@/access'; +import { DictValueEnumObj } from '@/components/DictTag'; +import KFIcon from '@/components/KFIcon'; +import KFModal from '@/components/KFModal'; +import { addDatesetAndVesion } from '@/services/dataset/index.js'; +import { getDictSelectOption } from '@/services/system/dict'; +import { to } from '@/utils/promise'; +import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; +import { + Button, + Form, + Input, + Radio, + Select, + Upload, + UploadFile, + message, + type ModalProps, + type UploadProps, +} from 'antd'; +import { omit } from 'lodash'; +import { useEffect, useState } from 'react'; +import { CategoryData } from '../../type'; +import styles from './index.less'; + +interface AddDatasetModalProps extends ModalProps { + typeList: CategoryData[]; + tagList: CategoryData[]; + onOk: () => void; +} + +function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalProps) { + const [uuid] = useState(Date.now()); + const [clusterOptions, setClusterOptions] = useState([]); + + useEffect(() => { + getClusterOptions(); + }, []); + + // 上传组件参数 + const uploadProps: UploadProps = { + action: '/api/mmp/dataset/upload', + headers: { + Authorization: getAccessToken() || '', + }, + defaultFileList: [], + }; + + // 获取集群版本数据 + const getClusterOptions = async () => { + const [res] = await to(getDictSelectOption('available_cluster')); + if (res) { + setClusterOptions(res); + } + }; + + // 上传请求 + const createDataset = async (params: any) => { + const [res] = await to(addDatesetAndVesion(params)); + if (res) { + message.success('创建成功'); + onOk?.(); + } + }; + + // 提交 + const onFinish = (formData: any) => { + const fileList: UploadFile[] = formData['fileList'] ?? []; + if (validateUploadFiles(fileList)) { + const params = { + ...omit(formData, ['fileList']), + dataset_version_vos: fileList.map((item) => { + const data = item.response?.data?.[0] ?? {}; + return { + file_name: data.fileName, + file_size: data.fileSize, + url: data.url, + }; + }), + }; + createDataset(params); + } + }; + + return ( + +
+ + + + + + + + + + + - - - - - - { - return { value: item.id, label: item.name }; - })} - /> - - - - - - - 仅自己可见 - 工作空间可见 - - - - - -
只允许上传.zip,.tgz格式文件
-
-
-
- - - ); -}); -export default PublicData; diff --git a/react-ui/src/pages/Dataset/publicData.jsx b/react-ui/src/pages/Dataset/publicData.jsx deleted file mode 100644 index 8e97a936..00000000 --- a/react-ui/src/pages/Dataset/publicData.jsx +++ /dev/null @@ -1,260 +0,0 @@ -import clock from '@/assets/img/clock.png'; -import creatByImg from '@/assets/img/creatBy.png'; -import { getAssetIcon, getDatasetList } from '@/services/dataset/index.js'; -import { Form, Input, Pagination } from 'antd'; -import moment from 'moment'; -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import Styles from './index.less'; -const { Search } = Input; -const leftdataList = [1, 2, 3]; - -const PublicData = () => { - const [queryFlow, setQueryFlow] = useState({ - page: 0, - size: 10, - name: null, - available_range: 1, - }); - const [iconParams, setIconParams] = useState({ - name: null, - page: 0, - size: 10000, - }); - const navgite = useNavigate(); - const [datasetTypeList, setDatasetTypeList] = useState([]); - const [datasetDirectionList, setDatasetDirectionList] = useState([]); - const [activeType, setActiveType] = useState(null); - const [activeTag, setActiveTag] = useState(null); - const [isModalOpen, setIsModalOpen] = useState(false); - const [datasetList, setDatasetList] = useState([]); - const [total, setTotal] = useState(0); - const [form] = Form.useForm(); - const [dialogTitle, setDialogTitle] = useState('新建数据'); - const getDatasetlist = (queryFlow) => { - getDatasetList(queryFlow).then((ret) => { - console.log(ret); - if (ret.code == 200) { - setDatasetList(ret.data.content); - setTotal(ret.data.totalElements); - } - }); - }; - const onSearch = (values) => { - console.log(values); - getAssetIconList({ ...iconParams, name: values }); - }; - const getAssetIconList = (params) => { - getAssetIcon(params).then((ret) => { - console.log(ret); - if (ret.code == 200 && ret.data.content && ret.data.content.length > 0) { - setDatasetTypeList(ret.data.content.filter((item) => item.category_id == 1)); - setDatasetDirectionList(ret.data.content.filter((item) => item.category_id == 2)); - } else { - setDatasetTypeList([]); - setDatasetDirectionList([]); - } - }); - }; - const nameSearch = (values) => { - console.log(values); - getDatasetlist({ ...queryFlow, name: values }); - }; - const showModal = () => { - form.resetFields(); - setDialogTitle('新建数据集'); - setIsModalOpen(true); - }; - const handleOk = () => { - console.log(1111); - setIsModalOpen(false); - }; - const handleCancel = () => { - setIsModalOpen(false); - }; - const chooseDatasetType = (val, item) => { - console.log(val, item); - if (item.id == queryFlow.data_type) { - setActiveType(''); - setQueryFlow({ ...queryFlow, data_type: null }); - getDatasetlist({ ...queryFlow, data_type: null }); - } else { - setActiveType(item.id); - setQueryFlow({ ...queryFlow, data_type: item.id }); - getDatasetlist({ ...queryFlow, data_type: item.id }); - } - // setQueryFlow({...queryFlow,data_type:item.path},()=>{ - // getDatasetlist() - // }) - }; - const chooseDatasetTag = (val, item) => { - console.log(val, item); - if (item.id == queryFlow.data_tag) { - setActiveTag(''); - setQueryFlow({ ...queryFlow, data_tag: null }); - getDatasetlist({ ...queryFlow, data_tag: null }); - } else { - setActiveTag(item.id); - setQueryFlow({ ...queryFlow, data_tag: item.id }); - getDatasetlist({ ...queryFlow, data_tag: item.id }); - } - // setQueryFlow({...queryFlow,data_type:item.path},()=>{ - // getDatasetlist() - // }) - }; - - const routeToIntro = (e, record) => { - e.stopPropagation(); - console.log(record); - navgite({ pathname: `/dataset/dataset/${record.id}?isPublic=true` }); - }; - const onFinishFailed = (errorInfo) => { - console.log('Failed:', errorInfo); - }; - const onPageChange = (pageNum, pageSize) => { - console.log(pageNum, pageSize); - setQueryFlow({ ...queryFlow, page: pageNum - 1, size: pageSize }); - getDatasetlist({ ...queryFlow, page: pageNum - 1, size: pageSize }); - }; - useEffect(() => { - getAssetIconList(iconParams); - getDatasetlist(queryFlow); - return () => {}; - }, []); - return ( - <> -
-
-
- -
分类
-
- {datasetTypeList && datasetTypeList.length > 0 - ? datasetTypeList.map((item) => { - return ( -
-
{ - chooseDatasetType(e, item); - }} - > - - - {item.name} -
-
- ); - }) - : ''} -
-
研究方向/应用领域
-
- {datasetDirectionList && datasetDirectionList.length > 0 - ? datasetDirectionList.map((item) => { - return ( -
-
{ - chooseDatasetTag(e, item); - }} - > - - - {item.name} -
-
- ); - }) - : ''} -
-
-
-
-
- 数据总数:{total}个 -
- -
-
-
- {datasetList && datasetList.length > 0 - ? datasetList.map((item) => { - return ( -
routeToIntro(e, item)}> - {item.name} -
{item.description}
-
- - {item.create_by} -
-
- - 最近更新: {moment(item.update_time).format('YYYY-MM-DD')} -
-
- ); - }) - : ''} - {/* Demo */} -
- -
-
- - ); -}; -export default PublicData; diff --git a/react-ui/src/pages/Dataset/type.tsx b/react-ui/src/pages/Dataset/type.tsx new file mode 100644 index 00000000..91808ea2 --- /dev/null +++ b/react-ui/src/pages/Dataset/type.tsx @@ -0,0 +1,118 @@ +import KFIcon from '@/components/KFIcon'; +import { CommonTabKeys } from '@/enums'; +import { + deleteDataset, + deleteModel, + getDatasetList, + getDatasetVersionIdList, + getDatasetVersionsById, + getModelList, + getModelVersionIdList, + getModelVersionsById, +} from '@/services/dataset/index.js'; +import type { TabsProps } from 'antd'; + +export enum ResourceType { + Model = 'Model', // 模型 + Dataset = 'Dataset', // 数据集 +} + +type ResourceTypeKeys = keyof typeof ResourceType; +type ResourceTypeValues = (typeof ResourceType)[ResourceTypeKeys]; + +export type ResourceTypeInfo = { + getList: (params: any) => Promise; + getVersions: (params: any) => Promise; + getFiles: (params: any) => Promise; + deleteRecord: (params: any) => Promise; + name: string; + typeParamKey: string; + tagParamKey: string; + fileReqParamKey: 'models_id' | 'dataset_id'; + tabItems: TabsProps['items']; + typeTitle: string; + tagTitle: string; + typeValue: number; // 从 getAssetIcon 接口获取特定值的数据为 type 分类 (category_id === typeValue) + tagValue: number; // 从 getAssetIcon 接口获取特定值的数据为 tag 分类(category_id === tagValue) + iconPathPrefix: string; // 图标路径前缀 + deleteModalTitle: string; // 删除弹框的title + addBtnTitle: string; // 新增按钮的title +}; + +export const resourceConfig: Record = { + [ResourceType.Dataset]: { + getList: getDatasetList, + getVersions: getDatasetVersionsById, + getFiles: getDatasetVersionIdList, + deleteRecord: deleteDataset, + name: '数据集', + typeParamKey: 'data_type', + tagParamKey: 'data_tag', + fileReqParamKey: 'dataset_id', + tabItems: [ + { + key: CommonTabKeys.Public, + label: '数据广场', + icon: , + }, + { + key: CommonTabKeys.Private, + label: '个人数据', + icon: , + }, + ], + typeTitle: '分类', + tagTitle: '研究方向/应用领域', + typeValue: 1, + tagValue: 2, + iconPathPrefix: 'dataset', + deleteModalTitle: '确定删除该条数据集实例吗?', + addBtnTitle: '新建数据集', + }, + [ResourceType.Model]: { + getList: getModelList, + getVersions: getModelVersionsById, + getFiles: getModelVersionIdList, + deleteRecord: deleteModel, + name: '模型', + typeParamKey: 'model_type', + tagParamKey: 'model_tag', + fileReqParamKey: 'models_id', + tabItems: [ + { + key: CommonTabKeys.Public, + label: '模型广场', + icon: , + }, + { + key: CommonTabKeys.Private, + label: '个人模型', + icon: , + }, + ], + typeTitle: '模型框架', + tagTitle: '模型能力', + typeValue: 3, + tagValue: 4, + iconPathPrefix: 'model', + deleteModalTitle: '确定删除该条模型实例吗?', + addBtnTitle: '新建模型', + }, +}; + +// 分类数据 +export type CategoryData = { + id: number; + category_id: number; + name: string; + path: string; +}; + +// 数据类型 +export type ResourceData = { + id: number; + name: string; + description: string; + create_by: string; + update_time: string; +}; diff --git a/react-ui/src/pages/Experiment/experimentText/addExperimentModal.tsx b/react-ui/src/pages/Experiment/experimentText/addExperimentModal.tsx index 5c853c42..f5b2bfc4 100644 --- a/react-ui/src/pages/Experiment/experimentText/addExperimentModal.tsx +++ b/react-ui/src/pages/Experiment/experimentText/addExperimentModal.tsx @@ -132,7 +132,6 @@ function AddExperimentModal({ {...layout} labelAlign="left" labelWrap - size="large" >
- +
diff --git a/react-ui/src/pages/Model/components/AddModelModal/index.less b/react-ui/src/pages/Model/components/AddModelModal/index.less new file mode 100644 index 00000000..bb520221 --- /dev/null +++ b/react-ui/src/pages/Model/components/AddModelModal/index.less @@ -0,0 +1,4 @@ +.upload-button { + height: 48px; + font-size: 15px; +} diff --git a/react-ui/src/pages/Model/components/AddModelModal/index.tsx b/react-ui/src/pages/Model/components/AddModelModal/index.tsx new file mode 100644 index 00000000..e04b1ba1 --- /dev/null +++ b/react-ui/src/pages/Model/components/AddModelModal/index.tsx @@ -0,0 +1,180 @@ +import { getAccessToken } from '@/access'; +import KFIcon from '@/components/KFIcon'; +import KFModal from '@/components/KFModal'; +import { CategoryData } from '@/pages/Dataset/type'; +import { addModel } from '@/services/dataset/index.js'; +import { to } from '@/utils/promise'; +import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; +import { + Button, + Form, + Input, + Select, + Upload, + UploadFile, + message, + type ModalProps, + type UploadProps, +} from 'antd'; +import { omit } from 'lodash'; +import { useState } from 'react'; +import styles from './index.less'; + +interface AddModelModalProps extends ModalProps { + typeList: CategoryData[]; + tagList: CategoryData[]; + onOk: () => void; +} + +function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) { + const [uuid] = useState(Date.now()); + // 上传组件参数 + const uploadProps: UploadProps = { + action: '/api/mmp/models/upload', + headers: { + Authorization: getAccessToken() || '', + }, + defaultFileList: [], + }; + + // 上传请求 + const createModel = async (params: any) => { + const [res] = await to(addModel(params)); + if (res) { + message.success('创建成功'); + onOk?.(); + } + }; + + // 提交 + const onFinish = (formData: any) => { + const fileList: UploadFile[] = formData['fileList'] ?? []; + if (validateUploadFiles(fileList)) { + const params = { + ...omit(formData, ['fileList']), + models_version_vos: fileList.map((item) => { + const data = item.response?.data?.[0] ?? {}; + return { + file_name: data.fileName, + file_size: data.fileSize, + url: data.url, + }; + }), + }; + createModel(params); + } + }; + + return ( + +
+ + + + + + + + + + + {/* + + 仅自己可见 + 工作空间可见 + + */} + + + + + + + + +
+
+ ); +} + +export default AddModelModal; diff --git a/react-ui/src/pages/Model/index.jsx b/react-ui/src/pages/Model/index.jsx index 310823f4..f8add51f 100644 --- a/react-ui/src/pages/Model/index.jsx +++ b/react-ui/src/pages/Model/index.jsx @@ -1,89 +1,7 @@ -import { Form, Input, Tabs } from 'antd'; -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import Styles from './index.less'; -import PersonalData from './personalData'; -import PublicData from './publicData'; -// import {getModelList} from '@/services/dataset/index.js' -const { Search } = Input; -const { TabPane } = Tabs; -const leftdataList = [1, 2, 3]; +import ResourcePage from '@/pages/Dataset/components/ResourcePage'; +import { ResourceType } from '@/pages/Dataset/type'; -const Dataset = () => { - const [queryFlow, setQueryFlow] = useState({ - page: 0, - size: 10, - name: null, - }); - const navgite = useNavigate(); - const [isModalOpen, setIsModalOpen] = useState(false); - const [datasetList, setDatasetList] = useState([]); - const [total, setTotal] = useState(0); - const [form] = Form.useForm(); - const [dialogTitle, setDialogTitle] = useState('新建数据'); - // const getModelLists=()=>{ - // getModelList(queryFlow).then(ret=>{ - // console.log(ret); - // if(ret.code==200){ - // setDatasetList(ret.data.content) - // setTotal(ret.data.totalElements) - // } - // }) - // } - - const showModal = () => { - form.resetFields(); - setDialogTitle('新建数据集'); - setIsModalOpen(true); - }; - const handleOk = () => { - console.log(1111); - setIsModalOpen(false); - }; - const handleCancel = () => { - setIsModalOpen(false); - }; - const onFinish = (values) => {}; - const routeToIntro = (e, record) => { - e.stopPropagation(); - navgite({ pathname: '/dataset/dataset' }); - }; - const onFinishFailed = (errorInfo) => { - console.log('Failed:', errorInfo); - }; - useEffect(() => { - return () => {}; - }, []); - return ( -
-
-
- - - - -
-
- ); +const ModelPage = () => { + return ; }; -export default Dataset; +export default ModelPage; diff --git a/react-ui/src/pages/Model/index.less b/react-ui/src/pages/Model/index.less index 4442e66a..596c64d7 100644 --- a/react-ui/src/pages/Model/index.less +++ b/react-ui/src/pages/Model/index.less @@ -1,13 +1,3 @@ -.datasetTopBox { - display: flex; - align-items: center; - width: 100%; - height: 49px; - padding: 0 30px; - padding-right: 30px; - background-image: url(/assets/images/pipeline-back.png); - background-size: 100% 100%; -} .datasetIntroTopBox { display: flex; flex-direction: column; @@ -78,250 +68,11 @@ } } } -.datasetAllBox { - :global { - .ant-tabs-nav .ant-tabs-nav-wrap { - margin: -48px 0 0 30px; - } - } -} .plusButton { margin: 0 18px 0 20px; } -.datasetCneterBox { - display: flex; - justify-content: space-between; - width: 100%; - height: 87.5vh; - - .datasetCneterLeftBox { - width: 340px; - height: 100%; - margin-right: 10px; - padding-top: 15px; - background: #ffffff; - box-shadow: 0px 3px 6px rgba(146, 146, 146, 0.09); - .custTab { - display: flex; - height: 32px; - border-bottom: 1px solid #e0eaff; - .tabItem { - width: 52px; - height: 100%; - color: #808080; - font-size: 15px; - text-align: center; - cursor: pointer; - } - } - .leftContentBox { - max-height: 80vh; - padding: 15px 20px; - overflow-x: hidden; - overflow-y: auto; - .itemTitle { - margin-bottom: 15px; - color: #1d1d20; - font-size: 14px; - } - .itemBox { - display: flex; - flex-wrap: wrap; - align-content: start; - width: 110%; - .messageBox { - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-between; - width: 92px; - height: 62px; - margin: 0 12px 20px 0; - padding: 11px 0px 7px 0px; - color: #1d1d20; - font-size: 12px; - border: 1px solid; - border-color: rgba(22, 100, 255, 0.05); - border-radius: 4px; - cursor: pointer; - .ptIcon { - display: block; - } - .hoverIcon { - display: none; - } - .messageText { - width: 65px; - overflow: hidden; - white-space: nowrap; - text-align: center; - text-overflow: ellipsis; - transition: all 0.2s; - } - } - .messageBox:hover { - background: rgba(22, 100, 255, 0.03); - border: 1px solid; - border-color: #1664ff; - .ptIcon { - display: none; - } - .hoverIcon { - display: block; - } - } - .active { - background: rgba(22, 100, 255, 0.03) !important; - border: 1px solid !important; - border-color: #1664ff !important; - .ptIcon { - display: none !important; - } - .hoverIcon { - display: block !important; - } - } - } - } - } - .datasetCneterRightBox { - display: flex; - flex: 1; - flex-direction: column; - height: 100%; - padding: 22px 30px 26px 30px; - overflow-y: auto; - background: #ffffff; - box-shadow: 0px 3px 6px rgba(146, 146, 146, 0.09); - .dataSource { - display: flex; - align-items: center; - justify-content: space-between; - height: 32px; - margin-bottom: 30px; - color: rgba(29, 29, 32, 0.8); - font-size: 15px; - } - .dataContent { - display: flex; - flex: 1; - flex-wrap: wrap; - align-content: flex-start; - width: 100%; - .dataItem { - position: relative; - width: 23.8%; - height: 164px; - margin: 0 20px 25px 0; - background: #ffffff; - border: 1px solid; - border-color: #eaeaea; - border-radius: 4px; - cursor: pointer; - .dropdown { - position: absolute; - top: 15px; - right: 20px; - } - .itemText { - position: absolute; - top: 20px; - left: 20px; - height: 6px; - color: #1d1d20; - font-size: 16px; - font-family: 'Alibaba'; - line-height: 0px; - background: linear-gradient( - to right, - rgba(22, 100, 255, 0.3) 0, - rgba(22, 100, 255, 0) 100% - ); - } - .itemDescripition { - position: absolute; - top: 57px; - left: 20px; - display: -webkit-box; - padding-right: 28px; - overflow: hidden; - color: #575757; - font-size: 14px; - word-break: break-all; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - } - .itemTime { - position: absolute; - bottom: 22px; - left: 20px; - display: flex; - align-items: center; - color: #808080; - font-size: 13px; - } - .itemIcon { - position: absolute; - right: 20px; - bottom: 22px; - display: flex; - align-items: center; - color: #808080; - font-size: 13px; - } - } - .dataItem:hover { - border-color: #1664ff; - box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1); - } - .dataItem:hover .itemText { - color: #1664ff; - } - } - } -} .tipContent { margin-top: 5px; color: #c73131; } -.modal { - :global { - .ant-modal-content { - width: 825px; - padding: 20px 67px; - background-image: url(/assets/images/modal-back.png); - background-repeat: no-repeat; - background-position: top center; - background-size: 100%; - border-radius: 21px; - } - .ant-modal-header { - margin: 20px 0; - background-color: transparent; - } - .ant-input { - height: 40px; - border-color: #e6e6e6; - } - .ant-form-item .ant-form-item-label > label { - color: rgba(29, 29, 32, 0.8); - } - .ant-modal-footer { - display: flex; - justify-content: center; - margin: 40px 0 30px 0; - } - .ant-btn { - width: 110px; - height: 42px; - font-size: 18px; - background: rgba(22, 100, 255, 0.06); - border-color: transparent; - border-radius: 10px; - } - .ant-btn-primary { - background: #1664ff; - } - } -} diff --git a/react-ui/src/pages/Model/modelIntro.jsx b/react-ui/src/pages/Model/modelIntro.jsx index dfc76e53..d978f15e 100644 --- a/react-ui/src/pages/Model/modelIntro.jsx +++ b/react-ui/src/pages/Model/modelIntro.jsx @@ -1,5 +1,6 @@ import { getAccessToken } from '@/access'; import KFIcon from '@/components/KFIcon'; +import KFModal from '@/components/KFModal'; import { addModelsVersionDetail, deleteModelVersion, @@ -8,9 +9,10 @@ import { getModelVersionsById, } from '@/services/dataset/index.js'; import { downLoadZip } from '@/utils/downloadfile'; +import { modalConfirm } from '@/utils/ui'; import { UploadOutlined } from '@ant-design/icons'; import { useParams, useSearchParams } from '@umijs/max'; -import { Button, Form, Input, Modal, Select, Table, Tabs, Upload, message } from 'antd'; +import { Button, Form, Input, Select, Table, Tabs, Upload, message } from 'antd'; import moment from 'moment'; import { useEffect, useRef, useState } from 'react'; import Styles from './index.less'; @@ -78,6 +80,9 @@ const Dataset = () => { ); setVersion(ret.data[0]); getModelVersions({ version: ret.data[0], models_id: locationParams.id }); + } else { + setVersion(null); + setWordList([]); } }); }; @@ -98,18 +103,9 @@ const Dataset = () => { setIsModalOpen(false); }; const deleteDataset = () => { - Modal.confirm({ - title: ( -
- -
删除后,该模型版本将不可恢复
-
- ), - content:
是否确认删除?
, + modalConfirm({ + title: '删除后,该版本将不可恢复', + content: '是否确认删除?', okText: '确认', cancelText: '取消', @@ -298,19 +294,11 @@ const Dataset = () => {
- - - {dialogTitle} -
- } + {
- +
); }; diff --git a/react-ui/src/pages/Model/personalData.jsx b/react-ui/src/pages/Model/personalData.jsx deleted file mode 100644 index e44816d7..00000000 --- a/react-ui/src/pages/Model/personalData.jsx +++ /dev/null @@ -1,525 +0,0 @@ -import { getAccessToken } from '@/access'; -import clock from '@/assets/img/clock.png'; -import creatByImg from '@/assets/img/creatBy.png'; -import deleteIcon from '@/assets/img/delete-icon.png'; -import KFIcon from '@/components/KFIcon'; -import { addModel, deleteModel, getAssetIcon, getModelList } from '@/services/dataset/index.js'; -import { modalConfirm } from '@/utils/ui'; -import { UploadOutlined } from '@ant-design/icons'; -import { Button, Form, Input, Modal, Pagination, Select, Upload, message } from 'antd'; -import moment from 'moment'; -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import Styles from './index.less'; -const { Search } = Input; - -const leftdataList = [1, 2, 3]; - -const PublicData = () => { - const props = { - action: '/api/mmp/dataset/upload', - // headers: { - // 'X-Requested-With': null - // }, - headers: { - Authorization: getAccessToken(), - 'X-Requested-With': null, - }, - onChange({ file, fileList }) { - if (file.status !== 'uploading') { - console.log(file, fileList); - form.setFieldsValue({ - models_version_vos: fileList.map((item) => { - const data = item.response.data[0]; - return { - file_name: data.fileName, - file_size: data.fileSize, - url: data.url, - }; - }), - }); - } - }, - defaultFileList: [], - }; - const [queryFlow, setQueryFlow] = useState({ - page: 0, - size: 20, - name: null, - available_range: 0, - }); - const navgite = useNavigate(); - const [iconParams, setIconParams] = useState({ - name: null, - page: 0, - size: 10000, - }); - const [activeType, setActiveType] = useState(null); - const [activeTag, setActiveTag] = useState(null); - const [modelTypeList, setmodelTypeList] = useState([]); - const [modelDirectionList, setmodelDirectionList] = useState([]); - const [isModalOpen, setIsModalOpen] = useState(false); - const [datasetList, setDatasetList] = useState([]); - const [total, setTotal] = useState(0); - const [form] = Form.useForm(); - const [dialogTitle, setDialogTitle] = useState('新建模型'); - const [uuid, setUuid] = useState(Date.now()); - const getModelLists = (queryFlow) => { - getModelList(queryFlow).then((ret) => { - console.log(ret); - if (ret.code == 200) { - setDatasetList(ret.data.content); - setTotal(ret.data.totalElements); - } - }); - }; - - const showModal = () => { - form.resetFields(); - setDialogTitle('新建模型'); - setUuid(Date.now()); - setIsModalOpen(true); - }; - const getAssetIconList = (params) => { - getAssetIcon(params).then((ret) => { - console.log(ret); - if (ret.code == 200 && ret.data.content && ret.data.content.length > 0) { - setmodelTypeList(ret.data.content.filter((item) => item.category_id == 3)); - setmodelDirectionList(ret.data.content.filter((item) => item.category_id == 4)); - } else { - setmodelTypeList([]); - setmodelDirectionList([]); - } - }); - }; - const onSearch = (values) => { - console.log(values); - getAssetIconList({ ...iconParams, name: values }); - }; - const nameSearch = (values) => { - console.log(values); - getModelLists({ ...queryFlow, name: values }); - }; - const handleOk = () => { - console.log(1111); - setIsModalOpen(false); - }; - const handleCancel = () => { - setIsModalOpen(false); - }; - const onFinish = (values) => { - const params = { - ...values, - available_range: 0, - }; - addModel(values).then((ret) => { - console.log(ret); - getModelLists(queryFlow); - setIsModalOpen(false); - }); - }; - - const chooseModelType = (val, item) => { - console.log(val, item); - if (item.id == queryFlow.model_type) { - setActiveType(''); - setQueryFlow({ ...queryFlow, model_type: null }); - getModelLists({ ...queryFlow, model_type: null }); - } else { - setActiveType(item.id); - setQueryFlow({ ...queryFlow, model_type: item.id }); - getModelLists({ ...queryFlow, model_type: item.id }); - } - - // setQueryFlow({...queryFlow,data_type:item.path},()=>{ - // getDatasetlist() - // }) - }; - const chooseModelTag = (val, item) => { - if (item.id == queryFlow.model_tag) { - setActiveTag(''); - setQueryFlow({ ...queryFlow, model_tag: null }); - getModelLists({ ...queryFlow, model_tag: null }); - } else { - setActiveTag(item.id); - setQueryFlow({ ...queryFlow, model_tag: item.id }); - getModelLists({ ...queryFlow, model_tag: item.id }); - } - // setQueryFlow({...queryFlow,data_type:item.path},()=>{ - // getDatasetlist() - // }) - }; - const routeToIntro = (e, record) => { - e.stopPropagation(); - console.log(record); - navgite({ pathname: `/dataset/model/${record.id}?isPublic=false` }); - }; - const onFinishFailed = (errorInfo) => { - console.log('Failed:', errorInfo); - }; - const onPageChange = (pageNum, pageSize) => { - console.log(pageNum, pageSize); - setQueryFlow({ ...queryFlow, page: pageNum - 1, size: pageSize }); - getModelLists({ ...queryFlow, page: pageNum - 1, size: pageSize }); - }; - useEffect(() => { - getAssetIconList(iconParams); - getModelLists(queryFlow); - return () => {}; - }, []); - return ( - <> -
-
-
- -
模型框架
-
- {modelTypeList && modelTypeList.length > 0 - ? modelTypeList.map((item) => { - return ( -
-
{ - chooseModelType(e, item); - }} - > - - - { - chooseModelTag(e, item); - }} - > - {item.name} - -
-
- ); - }) - : ''} -
-
模型能力
-
- {modelDirectionList && modelDirectionList.length > 0 - ? modelDirectionList.map((item) => { - return ( -
-
{ - chooseModelTag(e, item); - }} - > - - - {item.name} -
-
- ); - }) - : ''} -
-
-
-
-
- 数据总数:{total}个 -
- - -
-
-
- {datasetList && datasetList.length > 0 - ? datasetList.map((item) => { - return ( -
{ - routeToIntro(e, item); - }} - > - {item.name} - { - e.stopPropagation(); - modalConfirm({ - title: '确定删除该条模型实例吗?', - onOk: () => { - deleteModel(item.id).then((ret) => { - if (ret.code === 200) { - message.success('删除成功'); - getModelLists(queryFlow); - } else { - message.error(ret.msg); - } - }); - }, - }); - }} - className={Styles.dropdown} - style={{ width: '17px', marginRight: '6px' }} - src={deleteIcon} - alt="" - /> - {/* { - console.log(e); - if (e.key === 'detail') { - routeToIntro(e, item); - } else if (e.key === 'delete') { - modalConfirm({ - title: '确定删除该条模型实例吗?', - onOk: () => { - deleteModel(item.id).then((ret) => { - if (ret.code === 200) { - message.success('删除成功'); - getModelLists(queryFlow); - } else { - message.error(ret.msg); - } - }); - }, - }); - } - }, - }} - > -
- -
-
*/} -
{item.description}
-
- - {item.create_by} -
-
- - 最近更新: {moment(item.update_time).format('YYYY-MM-DD')} -
-
- ); - }) - : ''} - {/* Demo */} -
- -
-
- - - {dialogTitle} - - } - open={isModalOpen} - className={Styles.modal} - okButtonProps={{ - htmlType: 'submit', - form: 'form', - }} - onCancel={handleCancel} - > -
- - - - - - - - - - - - {/* - - 仅自己可见 - 工作空间可见 - - */} - - { - return { value: item.id, label: item.name }; - })} - /> - - - - - - -
-
- - ); -}; -export default PublicData; diff --git a/react-ui/src/pages/Model/publicData.jsx b/react-ui/src/pages/Model/publicData.jsx deleted file mode 100644 index 62790d17..00000000 --- a/react-ui/src/pages/Model/publicData.jsx +++ /dev/null @@ -1,360 +0,0 @@ -import clock from '@/assets/img/clock.png'; -import creatByImg from '@/assets/img/creatBy.png'; -import { getAssetIcon, getModelList } from '@/services/dataset/index.js'; -import { Form, Input, Modal, Pagination, Radio } from 'antd'; -import moment from 'moment'; -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import Styles from './index.less'; -const { Search } = Input; -const leftdataList = [1, 2, 3]; - -const PublicData = () => { - const [queryFlow, setQueryFlow] = useState({ - page: 0, - size: 20, - name: null, - available_range: 1, - }); - const [iconParams, setIconParams] = useState({ - name: null, - page: 0, - size: 10000, - }); - const [activeType, setActiveType] = useState(null); - const [activeTag, setActiveTag] = useState(null); - const [datasetTypeList, setDatasetTypeList] = useState([]); - const [datasetDirectionList, setDatasetDirectionList] = useState([]); - const navgite = useNavigate(); - const [isModalOpen, setIsModalOpen] = useState(false); - const [datasetList, setDatasetList] = useState([]); - const [total, setTotal] = useState(0); - const [form] = Form.useForm(); - const [dialogTitle, setDialogTitle] = useState('新建数据'); - const getModelLists = (queryFlow) => { - getModelList(queryFlow).then((ret) => { - console.log(ret); - if (ret.code == 200) { - setDatasetList(ret.data.content); - setTotal(ret.data.totalElements); - } - }); - }; - - const showModal = () => { - form.resetFields(); - setDialogTitle('新建数据集'); - setIsModalOpen(true); - }; - const nameSearch = (values) => { - console.log(values); - getModelLists({ ...queryFlow, name: values }); - }; - const getAssetIconList = (params) => { - getAssetIcon(params).then((ret) => { - console.log(ret); - if (ret.code == 200 && ret.data.content && ret.data.content.length > 0) { - setDatasetTypeList(ret.data.content.filter((item) => item.category_id == 3)); - setDatasetDirectionList(ret.data.content.filter((item) => item.category_id == 4)); - } else { - setDatasetTypeList([]); - setDatasetDirectionList([]); - } - }); - }; - const onSearch = (values) => { - console.log(values); - getAssetIconList({ ...iconParams, name: values }); - }; - const handleOk = () => { - console.log(1111); - setIsModalOpen(false); - }; - const handleCancel = () => { - setIsModalOpen(false); - }; - const chooseModelType = (val, item) => { - console.log(val, item); - if (item.id == queryFlow.model_type) { - setActiveType(''); - setQueryFlow({ ...queryFlow, model_type: null }); - getModelLists({ ...queryFlow, model_type: null }); - } else { - setActiveType(item.id); - setQueryFlow({ ...queryFlow, model_type: item.id }); - getModelLists({ ...queryFlow, model_type: item.id }); - } - - // setQueryFlow({...queryFlow,data_type:item.path},()=>{ - // getDatasetlist() - // }) - }; - const chooseModelTag = (val, item) => { - if (item.id == queryFlow.model_tag) { - setActiveTag(''); - setQueryFlow({ ...queryFlow, model_tag: null }); - getModelLists({ ...queryFlow, model_tag: null }); - } else { - setActiveTag(item.id); - setQueryFlow({ ...queryFlow, model_tag: item.id }); - getModelLists({ ...queryFlow, model_tag: item.id }); - } - // setQueryFlow({...queryFlow,data_type:item.path},()=>{ - // getDatasetlist() - // }) - }; - const onFinish = (values) => {}; - const routeToIntro = (e, record) => { - e.stopPropagation(); - console.log(record); - navgite({ pathname: `/dataset/model/${record.id}?isPublic=true` }); - }; - const onFinishFailed = (errorInfo) => { - console.log('Failed:', errorInfo); - }; - const onPageChange = (pageNum, pageSize) => { - console.log(pageNum, pageSize); - setQueryFlow({ ...queryFlow, page: pageNum - 1, size: pageSize }); - getModelLists({ ...queryFlow, page: pageNum - 1, size: pageSize }); - }; - useEffect(() => { - getAssetIconList(iconParams); - getModelLists(queryFlow); - return () => {}; - }, []); - return ( - <> -
-
-
- -
模型框架
-
- {datasetTypeList && datasetTypeList.length > 0 - ? datasetTypeList.map((item) => { - return ( -
-
{ - chooseModelType(e, item); - }} - > - - - {item.name} -
-
- ); - }) - : ''} -
-
模型能力
-
- {datasetDirectionList && datasetDirectionList.length > 0 - ? datasetDirectionList.map((item) => { - return ( -
-
{ - chooseModelTag(e, item); - }} - > - - - {item.name} -
-
- ); - }) - : ''} -
-
-
-
-
- 数据总数:{total}个 -
- -
-
-
- {datasetList && datasetList.length > 0 - ? datasetList.map((item) => { - return ( -
{ - routeToIntro(e, item); - }} - > - {item.name} -
{item.description}
-
- - {item.create_by} -
-
- - 最近更新: {moment(item.update_time).format('YYYY-MM-DD')} -
-
- ); - }) - : ''} - {/* Demo */} -
- -
-
- - - {dialogTitle} - - } - open={isModalOpen} - className={Styles.modal} - okButtonProps={{ - htmlType: 'submit', - form: 'form', - }} - onCancel={handleCancel} - > -
- - - - - - - - - - - - 仅自己可见 - 工作空间可见 - - - - - -
-
- - ); -}; -export default PublicData; diff --git a/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx b/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx index e073a449..20c265f2 100644 --- a/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx +++ b/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx @@ -40,7 +40,6 @@ export type SelectorTypeInfo = { getFiles: (params: any) => Promise; handleVersionResponse: (res: any) => any[]; modalIcon: string; - buttonIcon: string; name: string; litReqParamKey: 'available_range' | 'image_type'; fileReqParamKey: 'models_id' | 'dataset_id'; @@ -71,7 +70,6 @@ export const selectorTypeData: Record res.data || [], name: '模型', modalIcon: modelImg, - buttonIcon: 'local:model-select-button', litReqParamKey: 'available_range', fileReqParamKey: 'models_id', tabItems: [ @@ -92,7 +90,6 @@ export const selectorTypeData: Record res.data || [], name: '数据集', modalIcon: datasetImg, - buttonIcon: 'local:dataset-select-button', litReqParamKey: 'available_range', fileReqParamKey: 'dataset_id', tabItems: [ @@ -115,7 +112,6 @@ export const selectorTypeData: Record { type = ResourceSelectorType.Mirror; break; } - const { close } = openAntdModal( - ResourceSelectorModal, - { - type, - defaultExpandedKeys: resource ? [resource.id] : [], - defaultCheckedKeys: resource ? [`${resource.id}-${resource.version}`] : [], - defaultActiveTab: resource?.activeTab, - onOk: (res) => { - if (res) { - if (type === ResourceSelectorType.Mirror) { - form.setFieldValue(name, res); - } else { - const jsonObj = pick(res, ['id', 'version', 'path']); - const value = JSON.stringify(jsonObj); - form.setFieldValue(name, value); - } - - if (type === ResourceSelectorType.Dataset) { - setSelectedDataset(res); - } else if (type === ResourceSelectorType.Model) { - setSelectedModel(res); - } + const { close } = openAntdModal(ResourceSelectorModal, { + type, + defaultExpandedKeys: resource ? [resource.id] : [], + defaultCheckedKeys: resource ? [`${resource.id}-${resource.version}`] : [], + defaultActiveTab: resource?.activeTab, + onOk: (res) => { + if (res) { + if (type === ResourceSelectorType.Mirror) { + form.setFieldValue(name, res); } else { - if (type === ResourceSelectorType.Dataset) { - setSelectedDataset(null); - } else if (type === ResourceSelectorType.Model) { - setSelectedModel(null); - } - form.setFieldValue(name, ''); + const jsonObj = pick(res, ['id', 'version', 'path']); + const value = JSON.stringify(jsonObj); + form.setFieldValue(name, value); + } + + if (type === ResourceSelectorType.Dataset) { + setSelectedDataset(res); + } else if (type === ResourceSelectorType.Model) { + setSelectedModel(res); } - close(); - }, + } else { + if (type === ResourceSelectorType.Dataset) { + setSelectedDataset(null); + } else if (type === ResourceSelectorType.Model) { + setSelectedModel(null); + } + form.setFieldValue(name, ''); + } + close(); }, - true, - ); + }); }; // 获取选择数据集、模型后面按钮 icon diff --git a/react-ui/src/pages/Pipeline/index.jsx b/react-ui/src/pages/Pipeline/index.jsx index 51728c3c..fcbdc03e 100644 --- a/react-ui/src/pages/Pipeline/index.jsx +++ b/react-ui/src/pages/Pipeline/index.jsx @@ -170,7 +170,7 @@ const Pipeline = () => { key="clone" icon={} onClick={async () => { - Modal.confirm({ + modalConfirm({ title: '复制', content: '确定复制该条流水线吗?', okText: '确认', diff --git a/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx b/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx index f4d30fe1..89c1f34d 100644 --- a/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx +++ b/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx @@ -140,6 +140,7 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) { itemStyle: { borderRadius: 3, }, + minAngle: 5, label: { show: false, }, @@ -152,11 +153,11 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) { show: false, }, data: [ - { value: chartData.Failed, name: '失败' }, - { value: chartData.Succeeded, name: '成功' }, - { value: chartData.Terminated, name: '中止' }, - { value: chartData.Pending, name: '等待' }, - { value: chartData.Running, name: '运行中' }, + { value: chartData.Failed > 0 ? chartData.Failed : null, name: '失败' }, + { value: chartData.Succeeded > 0 ? chartData.Succeeded : null, name: '成功' }, + { value: chartData.Terminated > 0 ? chartData.Terminated : null, name: '中止' }, + { value: chartData.Pending > 0 ? chartData.Pending : null, name: '等待' }, + { value: chartData.Running > 0 ? chartData.Running : null, name: '运行中' }, ], }, { diff --git a/react-ui/src/pages/Workspace/components/ExperimentTable/index.less b/react-ui/src/pages/Workspace/components/ExperimentTable/index.less index f40613e6..fc83b21d 100644 --- a/react-ui/src/pages/Workspace/components/ExperimentTable/index.less +++ b/react-ui/src/pages/Workspace/components/ExperimentTable/index.less @@ -36,15 +36,15 @@ } &__duration { - width: 25%; + width: 30%; } &__date { - width: 35%; + width: calc(50% - 60px); } &__operation { - width: 20%; + width: 60px; :global { .ant-btn-link { diff --git a/react-ui/src/requestConfig.ts b/react-ui/src/requestConfig.ts index 74cb5c43..fe9cd44d 100644 --- a/react-ui/src/requestConfig.ts +++ b/react-ui/src/requestConfig.ts @@ -40,7 +40,7 @@ export const requestConfig: RequestConfig = { clearSessionToken(); setRemoteMenu(null); gotoLoginPage(false); - message.error(data?.msg ?? '请重新登录'); + message.error('请重新登录'); return Promise.reject(response); } else { message.error(data?.msg ?? '请求失败'); diff --git a/react-ui/src/styles/theme.less b/react-ui/src/styles/theme.less index d97b3ef8..65a5a32e 100644 --- a/react-ui/src/styles/theme.less +++ b/react-ui/src/styles/theme.less @@ -47,6 +47,22 @@ @result: rgba(@red, @green, @blue, @alpha); } +// 混合 +.singleLine() { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + word-break: break-all; +} + +.multiLine(@line) { + display: -webkit-box; + overflow: hidden; + word-break: break-all; + -webkit-box-orient: vertical; + -webkit-line-clamp: @line; +} + // 导出变量 :export { primaryColor: @primary-color; diff --git a/react-ui/src/utils/ui.tsx b/react-ui/src/utils/ui.tsx index 108bc8c0..8f8bde3f 100644 --- a/react-ui/src/utils/ui.tsx +++ b/react-ui/src/utils/ui.tsx @@ -6,7 +6,7 @@ import { PageEnum } from '@/enums/pagesEnums'; import themes from '@/styles/theme.less'; import { history } from '@umijs/max'; -import { Modal, type ModalFuncProps, type UploadFile } from 'antd'; +import { Modal, message, type ModalFuncProps, type UploadFile } from 'antd'; // 自定义 Confirm 弹框 export function modalConfirm({ title, content, onOk, ...rest }: ModalFuncProps) { @@ -54,6 +54,7 @@ export const gotoLoginPage = (toHome: boolean = true) => { const urlParams = new URLSearchParams(); urlParams.append('redirect', pathname + search); const newSearch = toHome && pathname && pathname !== PageEnum.LOGIN ? '' : urlParams.toString(); + console.log('gotoLoginPage', pathname, search); if (window.location.pathname !== PageEnum.LOGIN) { history.replace({ pathname: PageEnum.LOGIN, @@ -61,3 +62,28 @@ export const gotoLoginPage = (toHome: boolean = true) => { }); } }; + +// 上传文件校验 +export const validateUploadFiles = (files: UploadFile[], required: boolean = true): boolean => { + if (required && files.length === 0) { + message.error('请上传文件'); + return false; + } + + const hasError = files.some((file) => { + if (file.status === 'uploading') { + message.error('请等待文件上传完成'); + return true; + } + if (file.status === 'error') { + message.error('存在上传失败的文件,请删除后重新上传文件'); + return true; + } + if (!file.response || file.response.code !== 200 || !file.response.data) { + message.error('存在上传失败的文件,请删除后重新上传文件'); + return true; + } + return false; + }); + return !hasError; +};