| @@ -3,5 +3,5 @@ | |||||
| align-items: center; | align-items: center; | ||||
| height: 50px; | height: 50px; | ||||
| padding-left: 30px; | padding-left: 30px; | ||||
| background-image: url('../../assets/img/page-title-bg.png'); | |||||
| background-image: url(@/assets/img/page-title-bg.png); | |||||
| } | } | ||||
| @@ -126,3 +126,13 @@ export const useResetFormOnCloseModal = (form: FormInstance, open: boolean) => { | |||||
| } | } | ||||
| }, [form, prevOpen, open]); | }, [form, prevOpen, open]); | ||||
| }; | }; | ||||
| export const useInputModel = <T>(initialValue: T) => { | |||||
| const [value, setValue] = useState<T>(initialValue); | |||||
| const updateValue = useCallback((e: any) => { | |||||
| setValue(e.target?.value); | |||||
| }, []); | |||||
| return [value, updateValue]; | |||||
| }; | |||||
| @@ -0,0 +1,9 @@ | |||||
| .upload-tip { | |||||
| margin-top: 5px; | |||||
| color: @error-color; | |||||
| } | |||||
| .upload-button { | |||||
| height: 48px; | |||||
| font-size: 15px; | |||||
| } | |||||
| @@ -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<DictValueEnumObj[]>([]); | |||||
| 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 ( | |||||
| <KFModal | |||||
| {...rest} | |||||
| title="新建数据集" | |||||
| image={require('@/assets/img/create-experiment.png')} | |||||
| width={825} | |||||
| okButtonProps={{ | |||||
| htmlType: 'submit', | |||||
| form: 'form', | |||||
| }} | |||||
| destroyOnClose | |||||
| > | |||||
| <Form | |||||
| name="form" | |||||
| layout="vertical" | |||||
| initialValues={{ | |||||
| remember: true, | |||||
| }} | |||||
| onFinish={onFinish} | |||||
| autoComplete="off" | |||||
| > | |||||
| <Form.Item | |||||
| label="数据集名称" | |||||
| name="name" | |||||
| required | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入数据集名称', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input placeholder="请输入数据名称" showCount allowClear maxLength={64} /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="数据集版本" | |||||
| name="version" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入数据集版本', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input placeholder="请输入数据集版本" showCount allowClear maxLength={64} /> | |||||
| </Form.Item> | |||||
| <Form.Item label="数据集分类" name="data_type"> | |||||
| <Select | |||||
| allowClear | |||||
| placeholder="请选择数据集分类" | |||||
| options={typeList} | |||||
| fieldNames={{ label: 'name', value: 'id' }} | |||||
| /> | |||||
| </Form.Item> | |||||
| <Form.Item label="研究方向/应用领域" name="data_tag"> | |||||
| <Select | |||||
| allowClear | |||||
| placeholder="请选择研究方向/应用领域" | |||||
| options={tagList} | |||||
| fieldNames={{ label: 'name', value: 'id' }} | |||||
| /> | |||||
| </Form.Item> | |||||
| <Form.Item label="集群版本" name="available_cluster"> | |||||
| <Select allowClear placeholder="请选择集群版本" options={clusterOptions} /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="数据集简介" | |||||
| name="description" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入数据集简介', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input.TextArea | |||||
| placeholder="请输入数据集简介" | |||||
| showCount | |||||
| maxLength={256} | |||||
| autoSize={{ minRows: 2, maxRows: 6 }} | |||||
| allowClear | |||||
| /> | |||||
| </Form.Item> | |||||
| <Form.Item label="选择流水线" name="range"> | |||||
| <Radio.Group> | |||||
| <Radio value="0">仅自己可见</Radio> | |||||
| <Radio value="1">工作空间可见</Radio> | |||||
| </Radio.Group> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="数据集文件" | |||||
| name="fileList" | |||||
| valuePropName="fileList" | |||||
| getValueFromEvent={getFileListFromEvent} | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请上传数据集文件', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Upload {...uploadProps} data={{ uuid: uuid }} accept=".zip,.tgz"> | |||||
| <Button | |||||
| className={styles['upload-button']} | |||||
| type="default" | |||||
| icon={<KFIcon type="icon-shangchuan" />} | |||||
| > | |||||
| 上传文件 | |||||
| </Button> | |||||
| <div className={styles['upload-tip']}>只允许上传.zip,.tgz格式文件</div> | |||||
| </Upload> | |||||
| </Form.Item> | |||||
| </Form> | |||||
| </KFModal> | |||||
| ); | |||||
| } | |||||
| export default AddDatasetModal; | |||||
| @@ -0,0 +1,41 @@ | |||||
| .category-item { | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| align-items: center; | |||||
| width: 92px; | |||||
| height: 62px; | |||||
| padding: 11px 8px 7px; | |||||
| color: @text-color; | |||||
| font-size: 12px; | |||||
| border: 1px solid rgba(22, 100, 255, 0.05); | |||||
| border-radius: 4px; | |||||
| cursor: pointer; | |||||
| &__icon { | |||||
| display: block; | |||||
| } | |||||
| &__active-icon { | |||||
| display: none; | |||||
| } | |||||
| &__name { | |||||
| width: 100%; | |||||
| margin-top: 4px; | |||||
| overflow: hidden; | |||||
| white-space: nowrap; | |||||
| text-align: center; | |||||
| text-overflow: ellipsis; | |||||
| } | |||||
| &:hover, | |||||
| &--active { | |||||
| background: rgba(22, 100, 255, 0.03); | |||||
| border: 1px solid @primary-color; | |||||
| .category-item__icon { | |||||
| display: none; | |||||
| } | |||||
| .category-item__active-icon { | |||||
| display: block; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,37 @@ | |||||
| import classNames from 'classnames'; | |||||
| import { CategoryData, ResourceType, resourceConfig } from '../../type'; | |||||
| import styles from './index.less'; | |||||
| type CategoryItemProps = { | |||||
| resourceType: ResourceType; | |||||
| item: CategoryData; | |||||
| isSelected: boolean; | |||||
| onClick: (item: CategoryData) => void; | |||||
| }; | |||||
| function CategoryItem({ resourceType, item, isSelected, onClick }: CategoryItemProps) { | |||||
| return ( | |||||
| <div | |||||
| className={classNames(styles['category-item'], { | |||||
| [styles['category-item--active']]: isSelected, | |||||
| })} | |||||
| onClick={() => onClick(item)} | |||||
| > | |||||
| <img | |||||
| className={styles['category-item__icon']} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/${resourceConfig[resourceType].iconPathPrefix}/${item.path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| <img | |||||
| className={styles['category-item__active-icon']} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/${resourceConfig[resourceType].iconPathPrefix}/${item.path}-hover.png`} | |||||
| alt="" | |||||
| /> | |||||
| <span className={styles['category-item__name']}>{item.name}</span> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default CategoryItem; | |||||
| @@ -0,0 +1,22 @@ | |||||
| .category-list { | |||||
| width: 340px; | |||||
| height: 100%; | |||||
| margin-right: 10px; | |||||
| padding: 15px 0; | |||||
| background: white; | |||||
| border-radius: 4px; | |||||
| box-shadow: 0px 3px 6px rgba(146, 146, 146, 0.09); | |||||
| &__content { | |||||
| height: calc(100% - 32px - 15px); | |||||
| margin-top: 15px; | |||||
| padding-left: 20px; | |||||
| overflow-y: auto; | |||||
| &__title { | |||||
| margin-bottom: 15px; | |||||
| color: @text-color; | |||||
| font-size: 14px; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,71 @@ | |||||
| import { Flex, Input } from 'antd'; | |||||
| import { CategoryData, ResourceType, resourceConfig } from '../../type'; | |||||
| import CategoryItem from '../CategoryItem'; | |||||
| import styles from './index.less'; | |||||
| export type CategoryValue = { | |||||
| dataType: number | undefined; | |||||
| dataTag: number | undefined; | |||||
| }; | |||||
| type CategoryProps = { | |||||
| resourceType: ResourceType; // 资源类型,数据集还是模型 | |||||
| typeList: CategoryData[]; | |||||
| tagList: CategoryData[]; | |||||
| activeType?: number; | |||||
| activeTag?: number; | |||||
| onTypeSelect: (value: CategoryData) => void; | |||||
| onTagSelect: (value: CategoryData) => void; | |||||
| onSearch: (value: string) => void; | |||||
| }; | |||||
| function CategoryList({ | |||||
| resourceType, | |||||
| typeList, | |||||
| tagList, | |||||
| activeType, | |||||
| activeTag, | |||||
| onTypeSelect, | |||||
| onTagSelect, | |||||
| onSearch, | |||||
| }: CategoryProps) { | |||||
| return ( | |||||
| <div className={styles['category-list']}> | |||||
| <div style={{ padding: '0 20px' }}> | |||||
| <Input.Search placeholder="搜索" allowClear onSearch={onSearch} /> | |||||
| </div> | |||||
| <div className={styles['category-list__content']}> | |||||
| <div className={styles['category-list__content__title']}> | |||||
| {resourceConfig[resourceType].typeTitle} | |||||
| </div> | |||||
| <Flex wrap="wrap" gap="20px 12px"> | |||||
| {typeList?.map((item) => ( | |||||
| <CategoryItem | |||||
| key={item.id} | |||||
| resourceType={resourceType} | |||||
| item={item} | |||||
| onClick={onTypeSelect} | |||||
| isSelected={item.id === activeType} | |||||
| ></CategoryItem> | |||||
| ))} | |||||
| </Flex> | |||||
| <div className={styles['category-list__content__title']} style={{ marginTop: '25px' }}> | |||||
| {resourceConfig[resourceType].tagTitle} | |||||
| </div> | |||||
| <Flex wrap="wrap" gap="20px 12px"> | |||||
| {tagList?.map((item) => ( | |||||
| <CategoryItem | |||||
| key={item.id} | |||||
| resourceType={resourceType} | |||||
| item={item} | |||||
| onClick={onTagSelect} | |||||
| isSelected={item.id === activeTag} | |||||
| ></CategoryItem> | |||||
| ))} | |||||
| </Flex> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default CategoryList; | |||||
| @@ -0,0 +1,39 @@ | |||||
| .resource-list { | |||||
| display: flex; | |||||
| flex: 1; | |||||
| flex-direction: column; | |||||
| height: 100%; | |||||
| padding: 20px 0; | |||||
| background: white; | |||||
| box-shadow: 0px 3px 6px rgba(146, 146, 146, 0.09); | |||||
| &__header { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: space-between; | |||||
| height: 32px; | |||||
| margin-bottom: 30px; | |||||
| padding: 0 30px; | |||||
| color: @text-color; | |||||
| font-size: 15px; | |||||
| } | |||||
| &__content { | |||||
| display: flex; | |||||
| flex: 1; | |||||
| flex-wrap: wrap; | |||||
| gap: 20px; | |||||
| align-content: flex-start; | |||||
| width: 100%; | |||||
| margin-bottom: 30px; | |||||
| padding: 0 30px; | |||||
| overflow-y: auto; | |||||
| } | |||||
| :global { | |||||
| .ant-pagination { | |||||
| margin-right: 30px; | |||||
| text-align: right; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,210 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | |||||
| import { CommonTabKeys } from '@/enums'; | |||||
| import AddModelModal from '@/pages/Model/components/AddModelModal'; | |||||
| import { openAntdModal } from '@/utils/modal'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { modalConfirm } from '@/utils/ui'; | |||||
| import { useNavigate } from '@umijs/max'; | |||||
| import { Button, Input, Pagination, PaginationProps, message } from 'antd'; | |||||
| import { Ref, forwardRef, useEffect, useImperativeHandle, useState } from 'react'; | |||||
| import { CategoryData, ResourceData, ResourceType, resourceConfig } from '../../type'; | |||||
| import AddDatasetModal from '../AddDatasetModal'; | |||||
| import ResourceItem from '../Resourcetem'; | |||||
| import styles from './index.less'; | |||||
| export type ResourceListRef = { | |||||
| reset: () => void; | |||||
| }; | |||||
| type ResourceListProps = { | |||||
| resourceType: ResourceType; | |||||
| dataType?: number; | |||||
| dataTag?: number; | |||||
| isPublic: boolean; | |||||
| typeList: CategoryData[]; | |||||
| tagList: CategoryData[]; | |||||
| initialSearchText?: string; | |||||
| initialPagination?: PaginationProps; | |||||
| setCacheState: (state?: any) => void; | |||||
| }; | |||||
| function ResourceList( | |||||
| { | |||||
| resourceType, | |||||
| dataType, | |||||
| dataTag, | |||||
| typeList, | |||||
| tagList, | |||||
| isPublic, | |||||
| initialSearchText, | |||||
| initialPagination, | |||||
| setCacheState, | |||||
| }: ResourceListProps, | |||||
| ref: Ref<ResourceListRef>, | |||||
| ) { | |||||
| const navigate = useNavigate(); | |||||
| const [dataList, setDataList] = useState<ResourceData[]>([]); | |||||
| const [total, setTotal] = useState(0); | |||||
| const [pagination, setPagination] = useState<PaginationProps>( | |||||
| initialPagination ?? { | |||||
| current: 1, | |||||
| pageSize: 20, | |||||
| }, | |||||
| ); | |||||
| const [searchText, setSearchText] = useState(initialSearchText); | |||||
| const [inputText, setInputText] = useState(initialSearchText); | |||||
| useEffect(() => { | |||||
| getDataList(); | |||||
| }, [resourceType, dataType, dataTag, pagination, searchText, isPublic]); | |||||
| useImperativeHandle( | |||||
| ref, | |||||
| () => { | |||||
| return { | |||||
| reset: () => { | |||||
| setPagination({ | |||||
| current: 1, | |||||
| pageSize: 20, | |||||
| }); | |||||
| setSearchText(''); | |||||
| setInputText(''); | |||||
| setDataList([]); | |||||
| }, | |||||
| }; | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| // 获取数据请求 | |||||
| const getDataList = async () => { | |||||
| const reqParams = { | |||||
| page: pagination.current! - 1, | |||||
| size: pagination.pageSize, | |||||
| [resourceConfig[resourceType].typeParamKey]: dataType, | |||||
| [resourceConfig[resourceType].tagParamKey]: dataTag, | |||||
| available_range: isPublic ? 1 : 0, | |||||
| name: searchText !== '' ? searchText : undefined, | |||||
| }; | |||||
| const request = resourceConfig[resourceType].getList; | |||||
| const [res] = await to(request(reqParams)); | |||||
| if (res && res.data && res.data.content) { | |||||
| setDataList(res.data.content); | |||||
| setTotal(res.data.totalElements); | |||||
| } | |||||
| }; | |||||
| // 删除请求 | |||||
| const deleteRecord = async (id: number) => { | |||||
| const request = resourceConfig[resourceType].deleteRecord; | |||||
| const [res] = await to(request(id)); | |||||
| if (res) { | |||||
| getDataList(); | |||||
| message.success('删除成功'); | |||||
| } | |||||
| }; | |||||
| // 搜索 | |||||
| const handleSearch = (value: string) => { | |||||
| setSearchText(value); | |||||
| }; | |||||
| // 删除 | |||||
| const handleRemove = (record: ResourceData) => { | |||||
| modalConfirm({ | |||||
| title: resourceConfig[resourceType].deleteModalTitle, | |||||
| onOk: () => { | |||||
| deleteRecord(record.id); | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| // 跳转 | |||||
| const handleClick = (record: ResourceData) => { | |||||
| setCacheState({ | |||||
| activeTab: isPublic ? CommonTabKeys.Public : CommonTabKeys.Private, | |||||
| pagination, | |||||
| searchText, | |||||
| activeType: dataType, | |||||
| activeTag: dataTag, | |||||
| }); | |||||
| if (resourceType === ResourceType.Dataset) { | |||||
| navigate(`/dataset/dataset/${record.id}?isPublic=${isPublic}`); | |||||
| } else { | |||||
| navigate(`/dataset/model/${record.id}?isPublic=${isPublic}`); | |||||
| } | |||||
| }; | |||||
| // 分页切换 | |||||
| const handlePageChange: PaginationProps['onChange'] = (page, pageSize) => { | |||||
| setPagination({ | |||||
| current: page, | |||||
| pageSize: pageSize, | |||||
| }); | |||||
| }; | |||||
| // 新建弹框 | |||||
| const showModal = () => { | |||||
| const Modal = resourceType === ResourceType.Dataset ? AddDatasetModal : AddModelModal; | |||||
| const { close } = openAntdModal(Modal, { | |||||
| typeList: typeList, | |||||
| tagList: tagList, | |||||
| onOk: () => { | |||||
| getDataList(); | |||||
| close(); | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| return ( | |||||
| <div className={styles['resource-list']}> | |||||
| <div className={styles['resource-list__header']}> | |||||
| <span>数据总数:{total}个</span> | |||||
| <div> | |||||
| <Input.Search | |||||
| placeholder="按数据名称筛选" | |||||
| allowClear | |||||
| onSearch={handleSearch} | |||||
| style={{ | |||||
| width: 300, | |||||
| }} | |||||
| onChange={(e) => setInputText(e.target.value)} | |||||
| value={inputText} | |||||
| /> | |||||
| {!isPublic && ( | |||||
| <Button | |||||
| type="default" | |||||
| style={{ marginLeft: '20px' }} | |||||
| onClick={showModal} | |||||
| icon={<KFIcon type="icon-xinjian2" />} | |||||
| > | |||||
| {resourceConfig[resourceType].addBtnTitle} | |||||
| </Button> | |||||
| )} | |||||
| </div> | |||||
| </div> | |||||
| <div className={styles['resource-list__content']}> | |||||
| {dataList?.map((item) => ( | |||||
| <ResourceItem | |||||
| item={item} | |||||
| key={item.id} | |||||
| isPublic={isPublic} | |||||
| onRemove={handleRemove} | |||||
| onClick={handleClick} | |||||
| ></ResourceItem> | |||||
| ))} | |||||
| </div> | |||||
| <Pagination | |||||
| total={total} | |||||
| showSizeChanger | |||||
| defaultPageSize={20} | |||||
| pageSizeOptions={[20, 40, 60, 80, 100]} | |||||
| showQuickJumper | |||||
| onChange={handlePageChange} | |||||
| {...pagination} | |||||
| /> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default forwardRef(ResourceList); | |||||
| @@ -0,0 +1,8 @@ | |||||
| .resource-page { | |||||
| height: 100%; | |||||
| &__tabs-container { | |||||
| height: 50px; | |||||
| padding-left: 27px; | |||||
| background-image: url(@/assets/img/page-title-bg.png); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,113 @@ | |||||
| import { CommonTabKeys } from '@/enums'; | |||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { getAssetIcon } from '@/services/dataset/index.js'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { Flex, Tabs, type TabsProps } from 'antd'; | |||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| import { CategoryData, ResourceType, resourceConfig } from '../../type'; | |||||
| import CategoryList from '../CategoryList'; | |||||
| import ResourceList, { ResourceListRef } from '../ResourceList'; | |||||
| import styles from './index.less'; | |||||
| type ResourcePageProps = { | |||||
| resourceType: ResourceType; // 资源类型,数据集还是模型 | |||||
| }; | |||||
| function ResourcePage({ resourceType }: ResourcePageProps) { | |||||
| const [cacheState, setCacheState] = useCacheState(); | |||||
| const [activeTab, setActiveTab] = useState<string>(cacheState?.activeTab ?? CommonTabKeys.Public); | |||||
| const [typeList, setTypeList] = useState<CategoryData[]>([]); | |||||
| const [tagList, setTagList] = useState<CategoryData[]>([]); | |||||
| const [activeType, setActiveType] = useState<number | undefined>(cacheState?.activeType); | |||||
| const [activeTag, setActiveTag] = useState<number | undefined>(cacheState?.activeTag); | |||||
| const dataListRef = useRef<ResourceListRef>(null); | |||||
| useEffect(() => { | |||||
| getAssetIconList(); | |||||
| }, []); | |||||
| // 分类搜索 | |||||
| const handleCategorySearch = (value: string) => { | |||||
| getAssetIconList(value); | |||||
| }; | |||||
| // 选择类型 | |||||
| const chooseType = (record: CategoryData) => { | |||||
| setActiveType((prev) => (prev === record.id ? undefined : record.id)); | |||||
| }; | |||||
| // 选择 Tag | |||||
| const chooseTag = (record: CategoryData) => { | |||||
| setActiveTag((prev) => (prev === record.id ? undefined : record.id)); | |||||
| }; | |||||
| // 获取分类 | |||||
| const getAssetIconList = async (name: string = '') => { | |||||
| const params = { | |||||
| name: name, | |||||
| page: 0, | |||||
| size: 10000, | |||||
| }; | |||||
| const [res] = await to(getAssetIcon(params)); | |||||
| if (res && res.data && res.data.content) { | |||||
| const { content } = res.data; | |||||
| setTypeList( | |||||
| content.filter( | |||||
| (item: CategoryData) => | |||||
| Number(item.category_id) === resourceConfig[resourceType].typeValue, | |||||
| ), | |||||
| ); | |||||
| setTagList( | |||||
| content.filter( | |||||
| (item: CategoryData) => | |||||
| Number(item.category_id) === resourceConfig[resourceType].tagValue, | |||||
| ), | |||||
| ); | |||||
| } | |||||
| }; | |||||
| // 切换 Tab,重置数据 | |||||
| const hanleTabChange: TabsProps['onChange'] = (value) => { | |||||
| dataListRef.current?.reset(); | |||||
| setActiveTab(value); | |||||
| }; | |||||
| const isPublic = activeTab === CommonTabKeys.Public; | |||||
| return ( | |||||
| <div className={styles['resource-page']}> | |||||
| <div className={styles['resource-page__tabs-container']}> | |||||
| <Tabs | |||||
| activeKey={activeTab} | |||||
| items={resourceConfig[resourceType].tabItems} | |||||
| onChange={hanleTabChange} | |||||
| /> | |||||
| </div> | |||||
| <Flex style={{ marginTop: '10px', height: 'calc(100% - 59px)' }}> | |||||
| <CategoryList | |||||
| resourceType={resourceType} | |||||
| typeList={typeList} | |||||
| tagList={tagList} | |||||
| activeType={activeType} | |||||
| activeTag={activeTag} | |||||
| onTypeSelect={chooseType} | |||||
| onTagSelect={chooseTag} | |||||
| onSearch={handleCategorySearch} | |||||
| /> | |||||
| <ResourceList | |||||
| ref={dataListRef} | |||||
| resourceType={resourceType} | |||||
| typeList={typeList} | |||||
| tagList={tagList} | |||||
| isPublic={isPublic} | |||||
| dataType={activeType} | |||||
| dataTag={activeTag} | |||||
| initialSearchText={cacheState?.searchText} | |||||
| initialPagination={cacheState?.initialPagination} | |||||
| setCacheState={setCacheState} | |||||
| ></ResourceList> | |||||
| </Flex> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default ResourcePage; | |||||
| @@ -0,0 +1,61 @@ | |||||
| .resource-item { | |||||
| position: relative; | |||||
| width: calc(25% - 15px); | |||||
| padding: 20px; | |||||
| background: white; | |||||
| border: 1px solid #eaeaea; | |||||
| border-radius: 4px; | |||||
| cursor: pointer; | |||||
| @media screen and (max-width: 1860px) { | |||||
| & { | |||||
| width: calc(33.33% - 13.33px); | |||||
| } | |||||
| } | |||||
| &__name { | |||||
| position: relative; | |||||
| display: inline-block; | |||||
| height: 24px; | |||||
| margin: 0 10px 0 0 !important; | |||||
| color: @text-color; | |||||
| font-size: 16px; | |||||
| } | |||||
| &__description { | |||||
| height: 44px; | |||||
| margin-bottom: 20px; | |||||
| color: @text-color-secondary; | |||||
| font-size: 14px; | |||||
| .multiLine(2); | |||||
| } | |||||
| &__time { | |||||
| display: flex; | |||||
| flex: 0 1 content; | |||||
| align-items: center; | |||||
| width: 100%; | |||||
| color: #808080; | |||||
| font-size: 13px; | |||||
| } | |||||
| &:hover { | |||||
| border-color: @primary-color; | |||||
| box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1); | |||||
| .resource-item__name { | |||||
| color: @primary-color; | |||||
| } | |||||
| } | |||||
| } | |||||
| .resource-item__name { | |||||
| &::after { | |||||
| position: absolute; | |||||
| top: 14px; | |||||
| left: 0; | |||||
| width: 100%; | |||||
| height: 6px; | |||||
| background: linear-gradient(to right, rgba(22, 100, 255, 0.3) 0, rgba(22, 100, 255, 0) 100%); | |||||
| content: ''; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,54 @@ | |||||
| import clock from '@/assets/img/clock.png'; | |||||
| import creatByImg from '@/assets/img/creatBy.png'; | |||||
| import KFIcon from '@/components/KFIcon'; | |||||
| import { formatDate } from '@/utils/date'; | |||||
| import { Button, Flex, Typography } from 'antd'; | |||||
| import { ResourceData } from '../../type'; | |||||
| import styles from './index.less'; | |||||
| type ResourceItemProps = { | |||||
| item: ResourceData; | |||||
| isPublic: boolean; | |||||
| onRemove: (item: ResourceData) => void; | |||||
| onClick: (item: ResourceData) => void; | |||||
| }; | |||||
| function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) { | |||||
| return ( | |||||
| <div className={styles['resource-item']} onClick={() => onClick(item)}> | |||||
| <Flex justify="space-between" align="center" style={{ marginBottom: '20px', height: '32px' }}> | |||||
| <Typography.Paragraph | |||||
| className={styles['resource-item__name']} | |||||
| ellipsis={{ tooltip: item.name }} | |||||
| > | |||||
| {item.name} | |||||
| </Typography.Paragraph> | |||||
| {!isPublic && ( | |||||
| <Button | |||||
| type="text" | |||||
| shape="circle" | |||||
| onClick={(e) => { | |||||
| e.stopPropagation(); | |||||
| onRemove(item); | |||||
| }} | |||||
| > | |||||
| <KFIcon type="icon-shanchu" font={17} /> | |||||
| </Button> | |||||
| )} | |||||
| </Flex> | |||||
| <div className={styles['resource-item__description']}>{item.description}</div> | |||||
| <Flex justify="space-between"> | |||||
| <div className={styles['resource-item__time']}> | |||||
| <img style={{ width: '17px', marginRight: '6px' }} src={creatByImg} alt="" /> | |||||
| <span>{item.create_by}</span> | |||||
| </div> | |||||
| <div className={styles['resource-item__time']}> | |||||
| <img style={{ width: '12px', marginRight: '5px' }} src={clock} alt="" /> | |||||
| <span>最近更新: {formatDate(item.update_time, 'YYYY-MM-DD')}</span> | |||||
| </div> | |||||
| </Flex> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default ResourceItem; | |||||
| @@ -1,5 +1,6 @@ | |||||
| import { getAccessToken } from '@/access'; | import { getAccessToken } from '@/access'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import KFModal from '@/components/KFModal'; | |||||
| import { | import { | ||||
| addDatasetVersionDetail, | addDatasetVersionDetail, | ||||
| deleteDatasetVersion, | deleteDatasetVersion, | ||||
| @@ -8,9 +9,10 @@ import { | |||||
| getDatasetVersionsById, | getDatasetVersionsById, | ||||
| } from '@/services/dataset/index.js'; | } from '@/services/dataset/index.js'; | ||||
| import { downLoadZip } from '@/utils/downloadfile'; | import { downLoadZip } from '@/utils/downloadfile'; | ||||
| import { modalConfirm } from '@/utils/ui'; | |||||
| import { UploadOutlined } from '@ant-design/icons'; | import { UploadOutlined } from '@ant-design/icons'; | ||||
| import { useParams, useSearchParams } from '@umijs/max'; | 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 moment from 'moment'; | ||||
| import { useEffect, useRef, useState } from 'react'; | import { useEffect, useRef, useState } from 'react'; | ||||
| import Styles from './index.less'; | import Styles from './index.less'; | ||||
| @@ -79,6 +81,9 @@ const Dataset = () => { | |||||
| ); | ); | ||||
| setVersion(ret.data[0]); | setVersion(ret.data[0]); | ||||
| getDatasetVersions({ version: ret.data[0], dataset_id: locationParams.id }); | getDatasetVersions({ version: ret.data[0], dataset_id: locationParams.id }); | ||||
| } else { | |||||
| setVersion(null); | |||||
| setWordList([]); | |||||
| } | } | ||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -104,21 +109,9 @@ const Dataset = () => { | |||||
| downLoadZip(`/api/mmp/dataset/downloadAllFiles`, { dataset_id: locationParams.id, version }); | downLoadZip(`/api/mmp/dataset/downloadAllFiles`, { dataset_id: locationParams.id, version }); | ||||
| }; | }; | ||||
| const deleteDataset = () => { | const deleteDataset = () => { | ||||
| Modal.confirm({ | |||||
| title: ( | |||||
| <div> | |||||
| <img | |||||
| src="/assets/images/delete-icon.png" | |||||
| style={{ width: '120px', marginBottom: '24px' }} | |||||
| alt="" | |||||
| /> | |||||
| <div style={{ color: '#1d1d20', fontSize: '16px' }}>删除后,该数据集版本将不可恢复</div> | |||||
| </div> | |||||
| ), | |||||
| content: <div style={{ color: '#1d1d20', fontSize: '15px' }}>是否确认删除?</div>, | |||||
| okText: '确认', | |||||
| cancelText: '取消', | |||||
| modalConfirm({ | |||||
| title: '删除后,该数据集版本将不可恢复', | |||||
| content: '是否确认删除?', | |||||
| onOk: () => { | onOk: () => { | ||||
| deleteDatasetVersion({ dataset_id: locationParams.id, version }).then((ret) => { | deleteDatasetVersion({ dataset_id: locationParams.id, version }).then((ret) => { | ||||
| getDatasetVersionList(); | getDatasetVersionList(); | ||||
| @@ -299,17 +292,10 @@ const Dataset = () => { | |||||
| </TabPane> | </TabPane> | ||||
| </Tabs> | </Tabs> | ||||
| </div> | </div> | ||||
| <Modal | |||||
| title={ | |||||
| <div style={{ display: 'flex', alignItems: 'center', fontWeight: 500 }}> | |||||
| <img | |||||
| style={{ width: '20px', marginRight: '10px' }} | |||||
| src={`/assets/images/pipeline-edit-icon.png`} | |||||
| alt="" | |||||
| /> | |||||
| {dialogTitle} | |||||
| </div> | |||||
| } | |||||
| <KFModal | |||||
| title={dialogTitle} | |||||
| width={825} | |||||
| image={require('@/assets/img/create-experiment.png')} | |||||
| open={isModalOpen} | open={isModalOpen} | ||||
| className={Styles.modal} | className={Styles.modal} | ||||
| okButtonProps={{ | okButtonProps={{ | ||||
| @@ -397,7 +383,7 @@ const Dataset = () => { | |||||
| </Upload> | </Upload> | ||||
| </Form.Item> | </Form.Item> | ||||
| </Form> | </Form> | ||||
| </Modal> | |||||
| </KFModal> | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -1,90 +1,7 @@ | |||||
| import { getDatasetList } from '@/services/dataset/index.js'; | |||||
| 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'; | |||||
| const { Search } = Input; | |||||
| const { TabPane } = Tabs; | |||||
| const leftdataList = [1, 2, 3]; | |||||
| import ResourcePage from './components/ResourcePage'; | |||||
| import { ResourceType } from './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 getDatasetlist = () => { | |||||
| getDatasetList(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(() => { | |||||
| //getDatasetlist(); | |||||
| return () => {}; | |||||
| }, []); | |||||
| return ( | |||||
| <div className={Styles.datasetBox}> | |||||
| <div className={Styles.datasetTopBox}></div> | |||||
| <div className={Styles.datasetAllBox}> | |||||
| <Tabs defaultActiveKey="1"> | |||||
| <TabPane | |||||
| tab="数据广场" | |||||
| key="1" | |||||
| icon={ | |||||
| <svg className="icon" style={{ width: '14px', height: '14px' }} aria-hidden="true"> | |||||
| <use xlinkHref="#icon-shujujiguangchang"></use> | |||||
| </svg> | |||||
| } | |||||
| > | |||||
| <PublicData /> | |||||
| </TabPane> | |||||
| <TabPane | |||||
| tab="个人数据" | |||||
| key="2" | |||||
| icon={ | |||||
| <svg className="icon" style={{ width: '14px', height: '14px' }} aria-hidden="true"> | |||||
| <use xlinkHref="#icon-gerenshujuji"></use> | |||||
| </svg> | |||||
| } | |||||
| > | |||||
| <PersonalData /> | |||||
| </TabPane> | |||||
| </Tabs> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| const DatasetPage = () => { | |||||
| return <ResourcePage resourceType={ResourceType.Dataset} />; | |||||
| }; | }; | ||||
| export default Dataset; | |||||
| export default DatasetPage; | |||||
| @@ -1,14 +1,3 @@ | |||||
| .datasetTopBox { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| width: 100%; | |||||
| height: 49px; | |||||
| padding: 0 30px; | |||||
| padding-right: 30px; | |||||
| font-family: 'Alibaba'; | |||||
| background-image: url(/assets/images/pipeline-back.png); | |||||
| background-size: 100% 100%; | |||||
| } | |||||
| .datasetIntroTopBox { | .datasetIntroTopBox { | ||||
| display: flex; | display: flex; | ||||
| flex-direction: column; | flex-direction: column; | ||||
| @@ -68,7 +57,6 @@ | |||||
| font-size: 14px; | font-size: 14px; | ||||
| } | } | ||||
| .datasetBox { | .datasetBox { | ||||
| font-family: 'Alibaba'; | |||||
| background: #f9fafb; | background: #f9fafb; | ||||
| :global { | :global { | ||||
| @@ -80,258 +68,12 @@ | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| .datasetAllBox { | |||||
| :global { | |||||
| .ant-tabs-nav .ant-tabs-nav-wrap { | |||||
| margin: -48px 0 0 30px; | |||||
| } | |||||
| } | |||||
| } | |||||
| .plusButton { | .plusButton { | ||||
| margin: 0 18px 0 20px; | margin: 0 18px 0 20px; | ||||
| } | } | ||||
| .datasetCneterBox { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| width: 100%; | |||||
| height: 87.5vh; | |||||
| :global { | |||||
| .ant-btn { | |||||
| color: #1d1d20; | |||||
| font-size: 14px; | |||||
| } | |||||
| } | |||||
| .datasetCneterLeftBox { | |||||
| width: 340px; | |||||
| height: 100%; | |||||
| margin-right: 10px; | |||||
| padding-top: 15px; | |||||
| font-family: 'Alibaba'; | |||||
| 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; | |||||
| font-family: 'Alibaba'; | |||||
| .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; | |||||
| } | |||||
| } | |||||
| .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%; | |||||
| overflow-y: auto; | |||||
| padding: 22px 30px 26px 30px; | |||||
| 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; | |||||
| right: 20px; | |||||
| top: 15px; | |||||
| } | |||||
| .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{ | |||||
| color: #c73131; | |||||
| .tipContent { | |||||
| margin-top: 5px; | margin-top: 5px; | ||||
| } | |||||
| .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: 40px; | |||||
| font-size: 18px; | |||||
| background: rgba(22, 100, 255, 0.06); | |||||
| border-color: transparent; | |||||
| border-radius: 10px; | |||||
| } | |||||
| .ant-btn-primary { | |||||
| background: #1664ff; | |||||
| } | |||||
| } | |||||
| color: #c73131; | |||||
| } | } | ||||
| @@ -1,479 +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 { | |||||
| addDatesetAndVesion, | |||||
| deleteDataset, | |||||
| getAssetIcon, | |||||
| getDatasetList, | |||||
| } from '@/services/dataset/index.js'; | |||||
| import { getDictSelectOption } from '@/services/system/dict'; | |||||
| import { modalConfirm } from '@/utils/ui'; | |||||
| import { UploadOutlined } from '@ant-design/icons'; | |||||
| import { Button, Form, Input, Modal, Pagination, Radio, Select, Upload } from 'antd'; | |||||
| import moment from 'moment'; | |||||
| import React, { useEffect, useState } from 'react'; | |||||
| import { useNavigate } from 'react-router-dom'; | |||||
| import './index.less'; | |||||
| import Styles from './index.less'; | |||||
| const { Search } = Input; | |||||
| const leftdataList = [1, 2, 3]; | |||||
| const PublicData = (React.FC = () => { | |||||
| 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({ | |||||
| dataset_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 [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 [clusterOptions, setClusterOptions] = 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 getDatasetlist = (queryFlow) => { | |||||
| getDatasetList(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) { | |||||
| setDatasetTypeList(ret.data.content.filter((item) => item.category_id == 1)); | |||||
| setDatasetDirectionList(ret.data.content.filter((item) => item.category_id == 2)); | |||||
| } else { | |||||
| setDatasetTypeList([]); | |||||
| setDatasetDirectionList([]); | |||||
| } | |||||
| }); | |||||
| }; | |||||
| const onSearch = (values) => { | |||||
| console.log(values); | |||||
| getAssetIconList({ ...iconParams, name: values }); | |||||
| }; | |||||
| const nameSearch = (values) => { | |||||
| console.log(values); | |||||
| getDatasetlist({ ...queryFlow, name: values }); | |||||
| }; | |||||
| 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 onFinish = (values) => { | |||||
| addDatesetAndVesion(values).then((ret) => { | |||||
| console.log(ret); | |||||
| setIsModalOpen(false); | |||||
| getDatasetlist(queryFlow); | |||||
| }); | |||||
| }; | |||||
| const routeToIntro = (e, record) => { | |||||
| e.stopPropagation(); | |||||
| console.log(record); | |||||
| navgite({ pathname: `/dataset/dataset/${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 }); | |||||
| getDatasetlist({ ...queryFlow, page: pageNum - 1, size: pageSize }); | |||||
| }; | |||||
| useEffect(() => { | |||||
| getDictSelectOption('available_cluster').then((data) => { | |||||
| setClusterOptions(data); | |||||
| }); | |||||
| getAssetIconList(iconParams); | |||||
| getDatasetlist(queryFlow); | |||||
| return () => {}; | |||||
| }, []); | |||||
| return ( | |||||
| <> | |||||
| <div className={Styles.datasetCneterBox}> | |||||
| <div className={Styles.datasetCneterLeftBox}> | |||||
| <div className={Styles.leftContentBox}> | |||||
| <Search | |||||
| placeholder="搜索" | |||||
| allowClear | |||||
| onSearch={onSearch} | |||||
| style={{ | |||||
| width: 300, | |||||
| marginBottom: '15px', | |||||
| }} | |||||
| /> | |||||
| <div className={Styles.itemTitle}>分类</div> | |||||
| <div className={Styles.itemBox}> | |||||
| {datasetTypeList && datasetTypeList.length > 0 | |||||
| ? datasetTypeList.map((item) => { | |||||
| return ( | |||||
| <div> | |||||
| <div | |||||
| className={[ | |||||
| Styles.messageBox, | |||||
| item.id === activeType ? Styles.active : null, | |||||
| ].join(' ')} | |||||
| onClick={(e) => { | |||||
| chooseDatasetType(e, item); | |||||
| }} | |||||
| > | |||||
| <img | |||||
| className={Styles.ptIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/dataset/${item.path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| <img | |||||
| className={Styles.hoverIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/dataset/${item.path}-hover.png`} | |||||
| alt="" | |||||
| /> | |||||
| <span className={Styles.messageText}>{item.name}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| </div> | |||||
| <div className={Styles.itemTitle}>研究方向/应用领域</div> | |||||
| <div className={Styles.itemBox}> | |||||
| {datasetDirectionList && datasetDirectionList.length > 0 | |||||
| ? datasetDirectionList.map((item) => { | |||||
| return ( | |||||
| <div> | |||||
| <div | |||||
| className={[ | |||||
| Styles.messageBox, | |||||
| item.id === activeTag ? Styles.active : null, | |||||
| ].join(' ')} | |||||
| onClick={(e) => { | |||||
| chooseDatasetTag(e, item); | |||||
| }} | |||||
| > | |||||
| <img | |||||
| className={Styles.ptIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/dataset/${item.path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| <img | |||||
| className={Styles.hoverIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/dataset/${item.path}-hover.png`} | |||||
| alt="" | |||||
| /> | |||||
| <span className={Styles.messageText}>{item.name}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div className={Styles.datasetCneterRightBox}> | |||||
| <div className={Styles.dataSource}> | |||||
| <span>数据总数:{total}个</span> | |||||
| <div> | |||||
| <Search | |||||
| placeholder="按数据名称筛选" | |||||
| allowClear | |||||
| onSearch={nameSearch} | |||||
| style={{ | |||||
| width: 300, | |||||
| }} | |||||
| /> | |||||
| <Button | |||||
| type="default" | |||||
| style={{ marginLeft: '20px' }} | |||||
| onClick={showModal} | |||||
| icon={<KFIcon type="icon-xinjian2" />} | |||||
| > | |||||
| 新建数据集 | |||||
| </Button> | |||||
| </div> | |||||
| </div> | |||||
| <div className={Styles.dataContent}> | |||||
| {datasetList && datasetList.length > 0 | |||||
| ? datasetList.map((item) => { | |||||
| return ( | |||||
| <div className={Styles.dataItem} onClick={(e) => routeToIntro(e, item)}> | |||||
| <span className={Styles.itemText}>{item.name}</span> | |||||
| <img | |||||
| onClick={(e) => { | |||||
| e.stopPropagation(); | |||||
| modalConfirm({ | |||||
| title: '确定删除该条数据集实例吗?', | |||||
| onOk: () => { | |||||
| deleteDataset(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="" | |||||
| /> | |||||
| <div className={Styles.itemDescripition}>{item.description}</div> | |||||
| <div className={Styles.itemTime}> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '6px' }} | |||||
| src={creatByImg} | |||||
| alt="" | |||||
| /> | |||||
| <span>{item.create_by}</span> | |||||
| </div> | |||||
| <div className={Styles.itemIcon}> | |||||
| <img style={{ width: '12px', marginRight: '5px' }} src={clock} alt="" /> | |||||
| <span>最近更新: {moment(item.update_time).format('YYYY-MM-DD')}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| {/* <Select.Option value="demo">Demo</Select.Option> */} | |||||
| </div> | |||||
| <Pagination | |||||
| total={total} | |||||
| showSizeChanger | |||||
| defaultPageSize={20} | |||||
| pageSizeOptions={[20, 40, 60, 80, 100]} | |||||
| showQuickJumper | |||||
| onChange={onPageChange} | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <Modal | |||||
| title={ | |||||
| <div style={{ display: 'flex', alignItems: 'center', fontWeight: 500 }}> | |||||
| <img | |||||
| style={{ width: '20px', marginRight: '10px' }} | |||||
| src={`/assets/images/pipeline-edit-icon.png`} | |||||
| alt="" | |||||
| /> | |||||
| {dialogTitle} | |||||
| </div> | |||||
| } | |||||
| open={isModalOpen} | |||||
| className={Styles.modal} | |||||
| okButtonProps={{ | |||||
| htmlType: 'submit', | |||||
| form: 'form', | |||||
| }} | |||||
| onCancel={handleCancel} | |||||
| > | |||||
| <Form | |||||
| name="form" | |||||
| form={form} | |||||
| layout="vertical" | |||||
| initialValues={{ | |||||
| remember: true, | |||||
| }} | |||||
| onFinish={onFinish} | |||||
| onFinishFailed={onFinishFailed} | |||||
| autoComplete="off" | |||||
| > | |||||
| <Form.Item | |||||
| label="数据名称" | |||||
| name="name" | |||||
| required | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入数据名称e!', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input placeholder="请输入数据名称" showCount maxLength={64} /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="数据集版本" | |||||
| name="version" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入数据集版本!', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input placeholder="请输入数据集版本" showCount maxLength={64} /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="数据集分类" | |||||
| name="data_type" | |||||
| rules={ | |||||
| [ | |||||
| // { | |||||
| // required: true, | |||||
| // message: 'Please input your username!', | |||||
| // }, | |||||
| ] | |||||
| } | |||||
| > | |||||
| <Select | |||||
| allowClear | |||||
| placeholder="请选择数据集分类" | |||||
| options={datasetTypeList.map((item) => { | |||||
| return { value: item.id, label: item.name }; | |||||
| })} | |||||
| /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="研究方向/应用领域" | |||||
| name="data_tag" | |||||
| rules={ | |||||
| [ | |||||
| // { | |||||
| // required: true, | |||||
| // message: 'Please input your username!', | |||||
| // }, | |||||
| ] | |||||
| } | |||||
| > | |||||
| <Select | |||||
| allowClear | |||||
| placeholder="请选择研究方向/应用领域" | |||||
| options={datasetDirectionList.map((item) => { | |||||
| return { value: item.id, label: item.name }; | |||||
| })} | |||||
| /> | |||||
| </Form.Item> | |||||
| <Form.Item label="集群版本" name="available_cluster"> | |||||
| <Select allowClear placeholder="请选择集群版本" options={clusterOptions} /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="数据简介" | |||||
| name="description" | |||||
| rules={ | |||||
| [ | |||||
| // { | |||||
| // required: true, | |||||
| // message: 'Please input your username!', | |||||
| // }, | |||||
| ] | |||||
| } | |||||
| > | |||||
| <Input placeholder="请输入数据简介" showCount maxLength={256} /> | |||||
| </Form.Item> | |||||
| <Form.Item label="选择流水线" name="range"> | |||||
| <Radio.Group> | |||||
| <Radio value="0">仅自己可见</Radio> | |||||
| <Radio value="1">工作空间可见</Radio> | |||||
| </Radio.Group> | |||||
| </Form.Item> | |||||
| <Form.Item label="数据文件" name="dataset_version_vos"> | |||||
| <Upload {...props} data={{ uuid: uuid }} accept=".zip,.tgz"> | |||||
| <Button | |||||
| style={{ | |||||
| fontSize: '14px', | |||||
| border: '1px solid', | |||||
| borderColor: '#1664ff', | |||||
| background: '#fff', | |||||
| }} | |||||
| icon={<UploadOutlined style={{ color: '#1664ff', fontSize: '14px' }} />} | |||||
| > | |||||
| 上传文件 | |||||
| </Button> | |||||
| <div className={Styles.tipContent}>只允许上传.zip,.tgz格式文件</div> | |||||
| </Upload> | |||||
| </Form.Item> | |||||
| </Form> | |||||
| </Modal> | |||||
| </> | |||||
| ); | |||||
| }); | |||||
| export default PublicData; | |||||
| @@ -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 ( | |||||
| <> | |||||
| <div className={Styles.datasetCneterBox}> | |||||
| <div className={Styles.datasetCneterLeftBox}> | |||||
| <div className={Styles.leftContentBox}> | |||||
| <Search | |||||
| placeholder="搜索" | |||||
| allowClear | |||||
| onSearch={onSearch} | |||||
| style={{ | |||||
| width: 300, | |||||
| marginBottom: '15px', | |||||
| }} | |||||
| /> | |||||
| <div className={Styles.itemTitle}>分类</div> | |||||
| <div className={Styles.itemBox}> | |||||
| {datasetTypeList && datasetTypeList.length > 0 | |||||
| ? datasetTypeList.map((item) => { | |||||
| return ( | |||||
| <div> | |||||
| <div | |||||
| className={[ | |||||
| Styles.messageBox, | |||||
| item.id === activeType ? Styles.active : null, | |||||
| ].join(' ')} | |||||
| onClick={(e) => { | |||||
| chooseDatasetType(e, item); | |||||
| }} | |||||
| > | |||||
| <img | |||||
| className={Styles.ptIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/dataset/${item.path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| <img | |||||
| className={Styles.hoverIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/dataset/${item.path}-hover.png`} | |||||
| alt="" | |||||
| /> | |||||
| <span className={Styles.messageText}>{item.name}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| </div> | |||||
| <div className={Styles.itemTitle}>研究方向/应用领域</div> | |||||
| <div className={Styles.itemBox}> | |||||
| {datasetDirectionList && datasetDirectionList.length > 0 | |||||
| ? datasetDirectionList.map((item) => { | |||||
| return ( | |||||
| <div> | |||||
| <div | |||||
| className={[ | |||||
| Styles.messageBox, | |||||
| item.id === activeTag ? Styles.active : null, | |||||
| ].join(' ')} | |||||
| onClick={(e) => { | |||||
| chooseDatasetTag(e, item); | |||||
| }} | |||||
| > | |||||
| <img | |||||
| className={Styles.ptIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/dataset/${item.path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| <img | |||||
| className={Styles.hoverIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/dataset/${item.path}-hover.png`} | |||||
| alt="" | |||||
| /> | |||||
| <span className={Styles.messageText}>{item.name}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div className={Styles.datasetCneterRightBox}> | |||||
| <div className={Styles.dataSource}> | |||||
| <span>数据总数:{total}个</span> | |||||
| <div> | |||||
| <Search | |||||
| placeholder="按数据名称筛选" | |||||
| allowClear | |||||
| onSearch={nameSearch} | |||||
| style={{ | |||||
| width: 300, | |||||
| }} | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <div className={Styles.dataContent}> | |||||
| {datasetList && datasetList.length > 0 | |||||
| ? datasetList.map((item) => { | |||||
| return ( | |||||
| <div className={Styles.dataItem} onClick={(e) => routeToIntro(e, item)}> | |||||
| <span className={Styles.itemText}>{item.name}</span> | |||||
| <div className={Styles.itemDescripition}>{item.description}</div> | |||||
| <div className={Styles.itemTime}> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '6px' }} | |||||
| src={creatByImg} | |||||
| alt="" | |||||
| /> | |||||
| <span>{item.create_by}</span> | |||||
| </div> | |||||
| <div className={Styles.itemIcon}> | |||||
| <img style={{ width: '12px', marginRight: '5px' }} src={clock} alt="" /> | |||||
| <span>最近更新: {moment(item.update_time).format('YYYY-MM-DD')}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| {/* <Select.Option value="demo">Demo</Select.Option> */} | |||||
| </div> | |||||
| <Pagination | |||||
| total={total} | |||||
| showSizeChanger | |||||
| defaultPageSize={20} | |||||
| pageSizeOptions={[20, 40, 60, 80, 100]} | |||||
| showQuickJumper | |||||
| onChange={onPageChange} | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default PublicData; | |||||
| @@ -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<any>; | |||||
| getVersions: (params: any) => Promise<any>; | |||||
| getFiles: (params: any) => Promise<any>; | |||||
| deleteRecord: (params: any) => Promise<any>; | |||||
| 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<ResourceTypeValues, ResourceTypeInfo> = { | |||||
| [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: <KFIcon type="icon-shujuguangchang" />, | |||||
| }, | |||||
| { | |||||
| key: CommonTabKeys.Private, | |||||
| label: '个人数据', | |||||
| icon: <KFIcon type="icon-gerenshuju" />, | |||||
| }, | |||||
| ], | |||||
| 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: <KFIcon type="icon-moxingguangchang" />, | |||||
| }, | |||||
| { | |||||
| key: CommonTabKeys.Private, | |||||
| label: '个人模型', | |||||
| icon: <KFIcon type="icon-gerenmoxing" />, | |||||
| }, | |||||
| ], | |||||
| 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; | |||||
| }; | |||||
| @@ -132,7 +132,6 @@ function AddExperimentModal({ | |||||
| {...layout} | {...layout} | ||||
| labelAlign="left" | labelAlign="left" | ||||
| labelWrap | labelWrap | ||||
| size="large" | |||||
| > | > | ||||
| <Form.Item | <Form.Item | ||||
| label="实验名称" | label="实验名称" | ||||
| @@ -3,7 +3,7 @@ | |||||
| &__tabs-container { | &__tabs-container { | ||||
| height: 50px; | height: 50px; | ||||
| padding-left: 27px; | padding-left: 27px; | ||||
| background-image: url('../../assets/img/page-title-bg.png'); | |||||
| background-image: url(@/assets/img/page-title-bg.png); | |||||
| } | } | ||||
| &__content { | &__content { | ||||
| @@ -233,12 +233,7 @@ function MirrorList() { | |||||
| return ( | return ( | ||||
| <div className={styles['mirror-list']}> | <div className={styles['mirror-list']}> | ||||
| <div className={styles['mirror-list__tabs-container']}> | <div className={styles['mirror-list__tabs-container']}> | ||||
| <Tabs | |||||
| activeKey={activeTab} | |||||
| items={mirrorTabItems} | |||||
| onChange={hanleTabChange} | |||||
| className={styles['model-tabs']} | |||||
| /> | |||||
| <Tabs activeKey={activeTab} items={mirrorTabItems} onChange={hanleTabChange} /> | |||||
| </div> | </div> | ||||
| <div className={styles['mirror-list__content']}> | <div className={styles['mirror-list__content']}> | ||||
| <div className={styles['mirror-list__content__filter']}> | <div className={styles['mirror-list__content__filter']}> | ||||
| @@ -0,0 +1,4 @@ | |||||
| .upload-button { | |||||
| height: 48px; | |||||
| font-size: 15px; | |||||
| } | |||||
| @@ -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 ( | |||||
| <KFModal | |||||
| {...rest} | |||||
| title="新建模型" | |||||
| image={require('@/assets/img/create-experiment.png')} | |||||
| width={825} | |||||
| okButtonProps={{ | |||||
| htmlType: 'submit', | |||||
| form: 'form', | |||||
| }} | |||||
| > | |||||
| <Form | |||||
| name="form" | |||||
| layout="vertical" | |||||
| initialValues={{ | |||||
| remember: true, | |||||
| }} | |||||
| onFinish={onFinish} | |||||
| autoComplete="off" | |||||
| > | |||||
| <Form.Item | |||||
| label="模型名称" | |||||
| name="name" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入模型名称!', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input placeholder="请输入模型名称" showCount allowClear maxLength={64} /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="模型版本" | |||||
| name="version" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入模型版本', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input placeholder="请输入模型版本" allowClear maxLength={64} /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="模型简介" | |||||
| name="description" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入模型简介', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input.TextArea | |||||
| placeholder="请输入模型简介" | |||||
| showCount | |||||
| maxLength={256} | |||||
| autoSize={{ minRows: 2, maxRows: 6 }} | |||||
| allowClear | |||||
| /> | |||||
| </Form.Item> | |||||
| {/* <Form.Item label="可见范围" name="available_range"> | |||||
| <Radio.Group> | |||||
| <Radio value="0">仅自己可见</Radio> | |||||
| <Radio value="1">工作空间可见</Radio> | |||||
| </Radio.Group> | |||||
| </Form.Item> */} | |||||
| <Form.Item label="模型框架" name="model_type"> | |||||
| <Select | |||||
| allowClear | |||||
| placeholder="请选择模型类型" | |||||
| options={typeList} | |||||
| fieldNames={{ label: 'name', value: 'id' }} | |||||
| /> | |||||
| </Form.Item> | |||||
| <Form.Item label="模型能力" name="model_tag"> | |||||
| <Select | |||||
| allowClear | |||||
| placeholder="请选择模型标签" | |||||
| options={tagList} | |||||
| fieldNames={{ label: 'name', value: 'id' }} | |||||
| /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="模型文件" | |||||
| name="fileList" | |||||
| valuePropName="fileList" | |||||
| getValueFromEvent={getFileListFromEvent} | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请上传模型文件', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Upload {...uploadProps} data={{ uuid: uuid }}> | |||||
| <Button | |||||
| className={styles['upload-button']} | |||||
| type="default" | |||||
| icon={<KFIcon type="icon-shangchuan" />} | |||||
| > | |||||
| 上传文件 | |||||
| </Button> | |||||
| </Upload> | |||||
| </Form.Item> | |||||
| </Form> | |||||
| </KFModal> | |||||
| ); | |||||
| } | |||||
| export default AddModelModal; | |||||
| @@ -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 ( | |||||
| <div className={Styles.datasetBox}> | |||||
| <div className={Styles.datasetTopBox}></div> | |||||
| <div className={Styles.datasetAllBox}> | |||||
| <Tabs defaultActiveKey="1"> | |||||
| <TabPane | |||||
| tab="模型广场" | |||||
| key="1" | |||||
| icon={ | |||||
| <svg className="icon" style={{ width: '14px', height: '14px' }} aria-hidden="true"> | |||||
| <use xlinkHref="#icon-shujujiguangchang"></use> | |||||
| </svg> | |||||
| } | |||||
| > | |||||
| <PublicData /> | |||||
| </TabPane> | |||||
| <TabPane | |||||
| tab="个人模型" | |||||
| key="2" | |||||
| icon={ | |||||
| <svg className="icon" style={{ width: '14px', height: '14px' }} aria-hidden="true"> | |||||
| <use xlinkHref="#icon-gerenshujuji"></use> | |||||
| </svg> | |||||
| } | |||||
| > | |||||
| <PersonalData /> | |||||
| </TabPane> | |||||
| </Tabs> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| const ModelPage = () => { | |||||
| return <ResourcePage resourceType={ResourceType.Model} />; | |||||
| }; | }; | ||||
| export default Dataset; | |||||
| export default ModelPage; | |||||
| @@ -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 { | .datasetIntroTopBox { | ||||
| display: flex; | display: flex; | ||||
| flex-direction: column; | flex-direction: column; | ||||
| @@ -78,250 +68,11 @@ | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| .datasetAllBox { | |||||
| :global { | |||||
| .ant-tabs-nav .ant-tabs-nav-wrap { | |||||
| margin: -48px 0 0 30px; | |||||
| } | |||||
| } | |||||
| } | |||||
| .plusButton { | .plusButton { | ||||
| margin: 0 18px 0 20px; | 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 { | .tipContent { | ||||
| margin-top: 5px; | margin-top: 5px; | ||||
| color: #c73131; | 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; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,5 +1,6 @@ | |||||
| import { getAccessToken } from '@/access'; | import { getAccessToken } from '@/access'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import KFModal from '@/components/KFModal'; | |||||
| import { | import { | ||||
| addModelsVersionDetail, | addModelsVersionDetail, | ||||
| deleteModelVersion, | deleteModelVersion, | ||||
| @@ -8,9 +9,10 @@ import { | |||||
| getModelVersionsById, | getModelVersionsById, | ||||
| } from '@/services/dataset/index.js'; | } from '@/services/dataset/index.js'; | ||||
| import { downLoadZip } from '@/utils/downloadfile'; | import { downLoadZip } from '@/utils/downloadfile'; | ||||
| import { modalConfirm } from '@/utils/ui'; | |||||
| import { UploadOutlined } from '@ant-design/icons'; | import { UploadOutlined } from '@ant-design/icons'; | ||||
| import { useParams, useSearchParams } from '@umijs/max'; | 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 moment from 'moment'; | ||||
| import { useEffect, useRef, useState } from 'react'; | import { useEffect, useRef, useState } from 'react'; | ||||
| import Styles from './index.less'; | import Styles from './index.less'; | ||||
| @@ -78,6 +80,9 @@ const Dataset = () => { | |||||
| ); | ); | ||||
| setVersion(ret.data[0]); | setVersion(ret.data[0]); | ||||
| getModelVersions({ version: ret.data[0], models_id: locationParams.id }); | getModelVersions({ version: ret.data[0], models_id: locationParams.id }); | ||||
| } else { | |||||
| setVersion(null); | |||||
| setWordList([]); | |||||
| } | } | ||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -98,18 +103,9 @@ const Dataset = () => { | |||||
| setIsModalOpen(false); | setIsModalOpen(false); | ||||
| }; | }; | ||||
| const deleteDataset = () => { | const deleteDataset = () => { | ||||
| Modal.confirm({ | |||||
| title: ( | |||||
| <div> | |||||
| <img | |||||
| src="/assets/images/delete-icon.png" | |||||
| style={{ width: '120px', marginBottom: '24px' }} | |||||
| alt="" | |||||
| /> | |||||
| <div style={{ color: '#1d1d20', fontSize: '16px' }}>删除后,该模型版本将不可恢复</div> | |||||
| </div> | |||||
| ), | |||||
| content: <div style={{ color: '#1d1d20', fontSize: '15px' }}>是否确认删除?</div>, | |||||
| modalConfirm({ | |||||
| title: '删除后,该版本将不可恢复', | |||||
| content: '是否确认删除?', | |||||
| okText: '确认', | okText: '确认', | ||||
| cancelText: '取消', | cancelText: '取消', | ||||
| @@ -298,19 +294,11 @@ const Dataset = () => { | |||||
| </TabPane> | </TabPane> | ||||
| </Tabs> | </Tabs> | ||||
| </div> | </div> | ||||
| <Modal | |||||
| title={ | |||||
| <div style={{ display: 'flex', alignItems: 'center', fontWeight: 500 }}> | |||||
| <img | |||||
| style={{ width: '20px', marginRight: '10px' }} | |||||
| src={`/assets/images/pipeline-edit-icon.png`} | |||||
| alt="" | |||||
| /> | |||||
| {dialogTitle} | |||||
| </div> | |||||
| } | |||||
| <KFModal | |||||
| title={dialogTitle} | |||||
| image={require('@/assets/img/create-experiment.png')} | |||||
| width={825} | |||||
| open={isModalOpen} | open={isModalOpen} | ||||
| className={Styles.modal} | |||||
| okButtonProps={{ | okButtonProps={{ | ||||
| htmlType: 'submit', | htmlType: 'submit', | ||||
| form: 'form', | form: 'form', | ||||
| @@ -395,7 +383,7 @@ const Dataset = () => { | |||||
| </Upload> | </Upload> | ||||
| </Form.Item> | </Form.Item> | ||||
| </Form> | </Form> | ||||
| </Modal> | |||||
| </KFModal> | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -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 ( | |||||
| <> | |||||
| <div className={Styles.datasetCneterBox}> | |||||
| <div className={Styles.datasetCneterLeftBox}> | |||||
| <div className={Styles.leftContentBox}> | |||||
| <Search | |||||
| placeholder="搜索" | |||||
| allowClear | |||||
| onSearch={onSearch} | |||||
| style={{ | |||||
| width: 300, | |||||
| marginBottom: '15px', | |||||
| }} | |||||
| /> | |||||
| <div className={Styles.itemTitle}>模型框架</div> | |||||
| <div className={Styles.itemBox}> | |||||
| {modelTypeList && modelTypeList.length > 0 | |||||
| ? modelTypeList.map((item) => { | |||||
| return ( | |||||
| <div> | |||||
| <div | |||||
| className={[ | |||||
| Styles.messageBox, | |||||
| item.id === activeType ? Styles.active : null, | |||||
| ].join(' ')} | |||||
| onClick={(e) => { | |||||
| chooseModelType(e, item); | |||||
| }} | |||||
| > | |||||
| <img | |||||
| className={Styles.ptIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/model/${item.path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| <img | |||||
| className={Styles.hoverIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/model/${item.path}-hover.png`} | |||||
| alt="" | |||||
| /> | |||||
| <span | |||||
| className={Styles.messageText} | |||||
| onClick={(e) => { | |||||
| chooseModelTag(e, item); | |||||
| }} | |||||
| > | |||||
| {item.name} | |||||
| </span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| </div> | |||||
| <div className={Styles.itemTitle}>模型能力</div> | |||||
| <div className={Styles.itemBox}> | |||||
| {modelDirectionList && modelDirectionList.length > 0 | |||||
| ? modelDirectionList.map((item) => { | |||||
| return ( | |||||
| <div> | |||||
| <div | |||||
| className={[ | |||||
| Styles.messageBox, | |||||
| item.id === activeTag ? Styles.active : null, | |||||
| ].join(' ')} | |||||
| onClick={(e) => { | |||||
| chooseModelTag(e, item); | |||||
| }} | |||||
| > | |||||
| <img | |||||
| className={Styles.ptIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/model/${item.path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| <img | |||||
| className={Styles.hoverIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/model/${item.path}-hover.png`} | |||||
| alt="" | |||||
| /> | |||||
| <span className={Styles.messageText}>{item.name}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div className={Styles.datasetCneterRightBox}> | |||||
| <div className={Styles.dataSource}> | |||||
| <span>数据总数:{total}个</span> | |||||
| <div> | |||||
| <Search | |||||
| placeholder="按模型名称筛选" | |||||
| allowClear | |||||
| onSearch={nameSearch} | |||||
| style={{ | |||||
| width: 300, | |||||
| }} | |||||
| /> | |||||
| <Button | |||||
| type="default" | |||||
| style={{ marginLeft: '20px' }} | |||||
| onClick={showModal} | |||||
| icon={<KFIcon type="icon-xinjian2" />} | |||||
| > | |||||
| 模型注册 | |||||
| </Button> | |||||
| </div> | |||||
| </div> | |||||
| <div className={Styles.dataContent}> | |||||
| {datasetList && datasetList.length > 0 | |||||
| ? datasetList.map((item) => { | |||||
| return ( | |||||
| <div | |||||
| className={Styles.dataItem} | |||||
| onClick={(e) => { | |||||
| routeToIntro(e, item); | |||||
| }} | |||||
| > | |||||
| <span className={Styles.itemText}>{item.name}</span> | |||||
| <img | |||||
| onClick={(e) => { | |||||
| 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="" | |||||
| /> | |||||
| {/* <Dropdown | |||||
| className={Styles.dropdown} | |||||
| key={item.name} | |||||
| menu={{ | |||||
| items: [ | |||||
| { | |||||
| label: '详情', | |||||
| key: 'detail', | |||||
| }, | |||||
| { | |||||
| label: '删除', | |||||
| key: 'delete', | |||||
| }, | |||||
| ], | |||||
| onClick: (e) => { | |||||
| 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); | |||||
| } | |||||
| }); | |||||
| }, | |||||
| }); | |||||
| } | |||||
| }, | |||||
| }} | |||||
| > | |||||
| <div> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '6px' }} | |||||
| src={moreBack} | |||||
| alt="" | |||||
| /> | |||||
| </div> | |||||
| </Dropdown> */} | |||||
| <div className={Styles.itemDescripition}>{item.description}</div> | |||||
| <div className={Styles.itemTime}> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '6px' }} | |||||
| src={creatByImg} | |||||
| alt="" | |||||
| /> | |||||
| <span>{item.create_by}</span> | |||||
| </div> | |||||
| <div className={Styles.itemIcon}> | |||||
| <img style={{ width: '12px', marginRight: '5px' }} src={clock} alt="" /> | |||||
| <span>最近更新: {moment(item.update_time).format('YYYY-MM-DD')}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| {/* <Select.Option value="demo">Demo</Select.Option> */} | |||||
| </div> | |||||
| <Pagination | |||||
| total={total} | |||||
| showSizeChanger | |||||
| defaultPageSize={20} | |||||
| pageSizeOptions={[20, 40, 60, 80, 100]} | |||||
| showQuickJumper | |||||
| onChange={onPageChange} | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <Modal | |||||
| title={ | |||||
| <div style={{ display: 'flex', alignItems: 'center', fontWeight: 500 }}> | |||||
| <img | |||||
| style={{ width: '20px', marginRight: '10px' }} | |||||
| src={`/assets/images/pipeline-edit-icon.png`} | |||||
| alt="" | |||||
| /> | |||||
| {dialogTitle} | |||||
| </div> | |||||
| } | |||||
| open={isModalOpen} | |||||
| className={Styles.modal} | |||||
| okButtonProps={{ | |||||
| htmlType: 'submit', | |||||
| form: 'form', | |||||
| }} | |||||
| onCancel={handleCancel} | |||||
| > | |||||
| <Form | |||||
| name="form" | |||||
| form={form} | |||||
| layout="vertical" | |||||
| initialValues={{ | |||||
| remember: true, | |||||
| }} | |||||
| onFinish={onFinish} | |||||
| onFinishFailed={onFinishFailed} | |||||
| autoComplete="off" | |||||
| > | |||||
| <Form.Item | |||||
| label="模型名称" | |||||
| name="name" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入模型名称!', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input placeholder="请输入模型名称" showCount maxLength={64} /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="模型版本" | |||||
| name="version" | |||||
| rules={ | |||||
| [ | |||||
| // { | |||||
| // required: true, | |||||
| // message: 'Please input your username!', | |||||
| // }, | |||||
| ] | |||||
| } | |||||
| > | |||||
| <Input placeholder="请输入模型版本" /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="模型描述" | |||||
| name="description" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入模型描述!', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input placeholder="请输入模型描述" showCount maxLength={256} /> | |||||
| </Form.Item> | |||||
| {/* <Form.Item label="可见范围" name="available_range"> | |||||
| <Radio.Group> | |||||
| <Radio value="0">仅自己可见</Radio> | |||||
| <Radio value="1">工作空间可见</Radio> | |||||
| </Radio.Group> | |||||
| </Form.Item> */} | |||||
| <Form.Item | |||||
| label="模型框架" | |||||
| name="model_type" | |||||
| rules={ | |||||
| [ | |||||
| // { | |||||
| // required: true, | |||||
| // message: 'Please input your username!', | |||||
| // }, | |||||
| ] | |||||
| } | |||||
| > | |||||
| <Select | |||||
| allowClear | |||||
| placeholder="请选择模型类型" | |||||
| options={modelTypeList.map((item) => { | |||||
| return { value: item.id, label: item.name }; | |||||
| })} | |||||
| /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="模型能力" | |||||
| name="model_tag" | |||||
| rules={ | |||||
| [ | |||||
| // { | |||||
| // required: true, | |||||
| // message: 'Please input your username!', | |||||
| // }, | |||||
| ] | |||||
| } | |||||
| > | |||||
| <Select | |||||
| allowClear | |||||
| placeholder="请选择模型标签" | |||||
| options={modelDirectionList.map((item) => { | |||||
| return { value: item.id, label: item.name }; | |||||
| })} | |||||
| /> | |||||
| </Form.Item> | |||||
| <Form.Item label="模型文件" name="models_version_vos"> | |||||
| <Upload {...props} data={{ uuid: uuid }}> | |||||
| <Button | |||||
| style={{ | |||||
| fontSize: '14px', | |||||
| border: '1px solid', | |||||
| borderColor: '#1664ff', | |||||
| background: '#fff', | |||||
| }} | |||||
| icon={<UploadOutlined style={{ color: '#1664ff', fontSize: '14px' }} />} | |||||
| > | |||||
| 上传文件 | |||||
| </Button> | |||||
| </Upload> | |||||
| </Form.Item> | |||||
| </Form> | |||||
| </Modal> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default PublicData; | |||||
| @@ -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 ( | |||||
| <> | |||||
| <div className={Styles.datasetCneterBox}> | |||||
| <div className={Styles.datasetCneterLeftBox}> | |||||
| <div className={Styles.leftContentBox}> | |||||
| <Search | |||||
| placeholder="搜索" | |||||
| allowClear | |||||
| onSearch={onSearch} | |||||
| style={{ | |||||
| width: 300, | |||||
| marginBottom: '15px', | |||||
| }} | |||||
| /> | |||||
| <div className={Styles.itemTitle}>模型框架</div> | |||||
| <div className={Styles.itemBox}> | |||||
| {datasetTypeList && datasetTypeList.length > 0 | |||||
| ? datasetTypeList.map((item) => { | |||||
| return ( | |||||
| <div> | |||||
| <div | |||||
| className={[ | |||||
| Styles.messageBox, | |||||
| item.id === activeType ? Styles.active : null, | |||||
| ].join(' ')} | |||||
| onClick={(e) => { | |||||
| chooseModelType(e, item); | |||||
| }} | |||||
| > | |||||
| <img | |||||
| className={Styles.ptIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/model/${item.path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| <img | |||||
| className={Styles.hoverIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/model/${item.path}-hover.png`} | |||||
| alt="" | |||||
| /> | |||||
| <span className={Styles.messageText}>{item.name}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| </div> | |||||
| <div className={Styles.itemTitle}>模型能力</div> | |||||
| <div className={Styles.itemBox}> | |||||
| {datasetDirectionList && datasetDirectionList.length > 0 | |||||
| ? datasetDirectionList.map((item) => { | |||||
| return ( | |||||
| <div> | |||||
| <div | |||||
| className={[ | |||||
| Styles.messageBox, | |||||
| item.id === activeTag ? Styles.active : null, | |||||
| ].join(' ')} | |||||
| onClick={(e) => { | |||||
| chooseModelTag(e, item); | |||||
| }} | |||||
| > | |||||
| <img | |||||
| className={Styles.ptIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/model/${item.path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| <img | |||||
| className={Styles.hoverIcon} | |||||
| style={{ width: '22px' }} | |||||
| src={`/assets/images/model/${item.path}-hover.png`} | |||||
| alt="" | |||||
| /> | |||||
| <span className={Styles.messageText}>{item.name}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div className={Styles.datasetCneterRightBox}> | |||||
| <div className={Styles.dataSource}> | |||||
| <span>数据总数:{total}个</span> | |||||
| <div> | |||||
| <Search | |||||
| placeholder="按数据名称筛选" | |||||
| allowClear | |||||
| onSearch={nameSearch} | |||||
| style={{ | |||||
| width: 300, | |||||
| }} | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <div className={Styles.dataContent}> | |||||
| {datasetList && datasetList.length > 0 | |||||
| ? datasetList.map((item) => { | |||||
| return ( | |||||
| <div | |||||
| className={Styles.dataItem} | |||||
| onClick={(e) => { | |||||
| routeToIntro(e, item); | |||||
| }} | |||||
| > | |||||
| <span className={Styles.itemText}>{item.name}</span> | |||||
| <div className={Styles.itemDescripition}>{item.description}</div> | |||||
| <div className={Styles.itemTime}> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '6px' }} | |||||
| src={creatByImg} | |||||
| alt="" | |||||
| /> | |||||
| <span>{item.create_by}</span> | |||||
| </div> | |||||
| <div className={Styles.itemIcon}> | |||||
| <img style={{ width: '12px', marginRight: '5px' }} src={clock} alt="" /> | |||||
| <span>最近更新: {moment(item.update_time).format('YYYY-MM-DD')}</span> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }) | |||||
| : ''} | |||||
| {/* <Select.Option value="demo">Demo</Select.Option> */} | |||||
| </div> | |||||
| <Pagination | |||||
| total={total} | |||||
| showSizeChanger | |||||
| defaultPageSize={20} | |||||
| pageSizeOptions={[20, 40, 60, 80, 100]} | |||||
| showQuickJumper | |||||
| onChange={onPageChange} | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <Modal | |||||
| title={ | |||||
| <div style={{ display: 'flex', alignItems: 'center', fontWeight: 500 }}> | |||||
| <img | |||||
| style={{ width: '20px', marginRight: '10px' }} | |||||
| src={`/assets/images/pipeline-edit-icon.png`} | |||||
| alt="" | |||||
| /> | |||||
| {dialogTitle} | |||||
| </div> | |||||
| } | |||||
| open={isModalOpen} | |||||
| className={Styles.modal} | |||||
| okButtonProps={{ | |||||
| htmlType: 'submit', | |||||
| form: 'form', | |||||
| }} | |||||
| onCancel={handleCancel} | |||||
| > | |||||
| <Form | |||||
| name="form" | |||||
| form={form} | |||||
| layout="vertical" | |||||
| initialValues={{ | |||||
| remember: true, | |||||
| }} | |||||
| onFinish={onFinish} | |||||
| onFinishFailed={onFinishFailed} | |||||
| autoComplete="off" | |||||
| > | |||||
| <Form.Item | |||||
| label="数据名称" | |||||
| name="name" | |||||
| rules={ | |||||
| [ | |||||
| // { | |||||
| // required: true, | |||||
| // message: 'Please input your username!', | |||||
| // }, | |||||
| ] | |||||
| } | |||||
| > | |||||
| <Input placeholder="请输入数据名称" /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="数据集版本" | |||||
| name="data_type" | |||||
| rules={ | |||||
| [ | |||||
| // { | |||||
| // required: true, | |||||
| // message: 'Please input your username!', | |||||
| // }, | |||||
| ] | |||||
| } | |||||
| > | |||||
| <Input placeholder="请输入数据集版本" /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="数据描述" | |||||
| name="description" | |||||
| rules={ | |||||
| [ | |||||
| // { | |||||
| // required: true, | |||||
| // message: 'Please input your username!', | |||||
| // }, | |||||
| ] | |||||
| } | |||||
| > | |||||
| <Input placeholder="请输入数据描述" /> | |||||
| </Form.Item> | |||||
| <Form.Item label="选择流水线" name="description1"> | |||||
| <Radio.Group> | |||||
| <Radio value="0">仅自己可见</Radio> | |||||
| <Radio value="1">工作空间可见</Radio> | |||||
| </Radio.Group> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="数据标签" | |||||
| name="description3" | |||||
| rules={ | |||||
| [ | |||||
| // { | |||||
| // required: true, | |||||
| // message: 'Please input your username!', | |||||
| // }, | |||||
| ] | |||||
| } | |||||
| > | |||||
| <Input placeholder="请输入数据标签" /> | |||||
| </Form.Item> | |||||
| </Form> | |||||
| </Modal> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default PublicData; | |||||
| @@ -40,7 +40,6 @@ export type SelectorTypeInfo = { | |||||
| getFiles: (params: any) => Promise<any>; | getFiles: (params: any) => Promise<any>; | ||||
| handleVersionResponse: (res: any) => any[]; | handleVersionResponse: (res: any) => any[]; | ||||
| modalIcon: string; | modalIcon: string; | ||||
| buttonIcon: string; | |||||
| name: string; | name: string; | ||||
| litReqParamKey: 'available_range' | 'image_type'; | litReqParamKey: 'available_range' | 'image_type'; | ||||
| fileReqParamKey: 'models_id' | 'dataset_id'; | fileReqParamKey: 'models_id' | 'dataset_id'; | ||||
| @@ -71,7 +70,6 @@ export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeIn | |||||
| handleVersionResponse: (res) => res.data || [], | handleVersionResponse: (res) => res.data || [], | ||||
| name: '模型', | name: '模型', | ||||
| modalIcon: modelImg, | modalIcon: modelImg, | ||||
| buttonIcon: 'local:model-select-button', | |||||
| litReqParamKey: 'available_range', | litReqParamKey: 'available_range', | ||||
| fileReqParamKey: 'models_id', | fileReqParamKey: 'models_id', | ||||
| tabItems: [ | tabItems: [ | ||||
| @@ -92,7 +90,6 @@ export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeIn | |||||
| handleVersionResponse: (res) => res.data || [], | handleVersionResponse: (res) => res.data || [], | ||||
| name: '数据集', | name: '数据集', | ||||
| modalIcon: datasetImg, | modalIcon: datasetImg, | ||||
| buttonIcon: 'local:dataset-select-button', | |||||
| litReqParamKey: 'available_range', | litReqParamKey: 'available_range', | ||||
| fileReqParamKey: 'dataset_id', | fileReqParamKey: 'dataset_id', | ||||
| tabItems: [ | tabItems: [ | ||||
| @@ -115,7 +112,6 @@ export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeIn | |||||
| [], | [], | ||||
| name: '镜像', | name: '镜像', | ||||
| modalIcon: mirrorImg, | modalIcon: mirrorImg, | ||||
| buttonIcon: 'local:mirror-select-button', | |||||
| litReqParamKey: 'image_type', | litReqParamKey: 'image_type', | ||||
| fileReqParamKey: 'dataset_id', | fileReqParamKey: 'dataset_id', | ||||
| tabItems: [ | tabItems: [ | ||||
| @@ -142,41 +142,37 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| type = ResourceSelectorType.Mirror; | type = ResourceSelectorType.Mirror; | ||||
| break; | 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 { | } 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 | // 获取选择数据集、模型后面按钮 icon | ||||
| @@ -170,7 +170,7 @@ const Pipeline = () => { | |||||
| key="clone" | key="clone" | ||||
| icon={<KFIcon type="icon-fuzhi" />} | icon={<KFIcon type="icon-fuzhi" />} | ||||
| onClick={async () => { | onClick={async () => { | ||||
| Modal.confirm({ | |||||
| modalConfirm({ | |||||
| title: '复制', | title: '复制', | ||||
| content: '确定复制该条流水线吗?', | content: '确定复制该条流水线吗?', | ||||
| okText: '确认', | okText: '确认', | ||||
| @@ -140,6 +140,7 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) { | |||||
| itemStyle: { | itemStyle: { | ||||
| borderRadius: 3, | borderRadius: 3, | ||||
| }, | }, | ||||
| minAngle: 5, | |||||
| label: { | label: { | ||||
| show: false, | show: false, | ||||
| }, | }, | ||||
| @@ -152,11 +153,11 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) { | |||||
| show: false, | show: false, | ||||
| }, | }, | ||||
| data: [ | 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: '运行中' }, | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -36,15 +36,15 @@ | |||||
| } | } | ||||
| &__duration { | &__duration { | ||||
| width: 25%; | |||||
| width: 30%; | |||||
| } | } | ||||
| &__date { | &__date { | ||||
| width: 35%; | |||||
| width: calc(50% - 60px); | |||||
| } | } | ||||
| &__operation { | &__operation { | ||||
| width: 20%; | |||||
| width: 60px; | |||||
| :global { | :global { | ||||
| .ant-btn-link { | .ant-btn-link { | ||||
| @@ -40,7 +40,7 @@ export const requestConfig: RequestConfig = { | |||||
| clearSessionToken(); | clearSessionToken(); | ||||
| setRemoteMenu(null); | setRemoteMenu(null); | ||||
| gotoLoginPage(false); | gotoLoginPage(false); | ||||
| message.error(data?.msg ?? '请重新登录'); | |||||
| message.error('请重新登录'); | |||||
| return Promise.reject(response); | return Promise.reject(response); | ||||
| } else { | } else { | ||||
| message.error(data?.msg ?? '请求失败'); | message.error(data?.msg ?? '请求失败'); | ||||
| @@ -47,6 +47,22 @@ | |||||
| @result: rgba(@red, @green, @blue, @alpha); | @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 { | :export { | ||||
| primaryColor: @primary-color; | primaryColor: @primary-color; | ||||
| @@ -6,7 +6,7 @@ | |||||
| import { PageEnum } from '@/enums/pagesEnums'; | import { PageEnum } from '@/enums/pagesEnums'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { history } from '@umijs/max'; | import { history } from '@umijs/max'; | ||||
| import { Modal, type ModalFuncProps, type UploadFile } from 'antd'; | |||||
| import { Modal, message, type ModalFuncProps, type UploadFile } from 'antd'; | |||||
| // 自定义 Confirm 弹框 | // 自定义 Confirm 弹框 | ||||
| export function modalConfirm({ title, content, onOk, ...rest }: ModalFuncProps) { | export function modalConfirm({ title, content, onOk, ...rest }: ModalFuncProps) { | ||||
| @@ -54,6 +54,7 @@ export const gotoLoginPage = (toHome: boolean = true) => { | |||||
| const urlParams = new URLSearchParams(); | const urlParams = new URLSearchParams(); | ||||
| urlParams.append('redirect', pathname + search); | urlParams.append('redirect', pathname + search); | ||||
| const newSearch = toHome && pathname && pathname !== PageEnum.LOGIN ? '' : urlParams.toString(); | const newSearch = toHome && pathname && pathname !== PageEnum.LOGIN ? '' : urlParams.toString(); | ||||
| console.log('gotoLoginPage', pathname, search); | |||||
| if (window.location.pathname !== PageEnum.LOGIN) { | if (window.location.pathname !== PageEnum.LOGIN) { | ||||
| history.replace({ | history.replace({ | ||||
| pathname: PageEnum.LOGIN, | 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; | |||||
| }; | |||||