| @@ -135,6 +135,17 @@ export default [ | |||||
| path: '/dataset/datasetIntro/:id', | path: '/dataset/datasetIntro/:id', | ||||
| component: './Dataset/datasetIntro', | component: './Dataset/datasetIntro', | ||||
| }, | }, | ||||
| { | |||||
| name: '镜像', | |||||
| path: 'mirror', | |||||
| routes: [ | |||||
| { | |||||
| name: '镜像列表', | |||||
| path: '', | |||||
| component: './Mirror/list', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | { | ||||
| name: '模型管理', | name: '模型管理', | ||||
| path: '/dataset/modelIndex', | path: '/dataset/modelIndex', | ||||
| @@ -5,22 +5,31 @@ | |||||
| */ | */ | ||||
| import ModalTitle from '@/components/ModalTitle'; | import ModalTitle from '@/components/ModalTitle'; | ||||
| import { Modal, type ModalProps } from 'antd'; | |||||
| import { ConfigProvider, Modal, theme, type ModalProps } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useAntdConfig } from 'umi'; | |||||
| import './index.less'; | import './index.less'; | ||||
| const { useToken } = theme; | |||||
| export interface KFModalProps extends ModalProps { | export interface KFModalProps extends ModalProps { | ||||
| image: string; | image: string; | ||||
| } | } | ||||
| function KFModal({ title, image, children, className = '', ...rest }: KFModalProps) { | function KFModal({ title, image, children, className = '', ...rest }: KFModalProps) { | ||||
| const { token } = useToken(); | |||||
| console.log('token', token); | |||||
| const antdConfig = useAntdConfig(); | |||||
| console.log('antdConfig', antdConfig); | |||||
| return ( | return ( | ||||
| <Modal | |||||
| className={classNames(['kf-modal', className])} | |||||
| {...rest} | |||||
| title={<ModalTitle title={title} image={image}></ModalTitle>} | |||||
| > | |||||
| {children} | |||||
| </Modal> | |||||
| <ConfigProvider {...antdConfig}> | |||||
| <Modal | |||||
| className={classNames(['kf-modal', className])} | |||||
| {...rest} | |||||
| title={<ModalTitle title={title} image={image}></ModalTitle>} | |||||
| > | |||||
| {children} | |||||
| </Modal> | |||||
| </ConfigProvider> | |||||
| ); | ); | ||||
| } | } | ||||
| @@ -0,0 +1,32 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-16 14:55:40 | |||||
| * @Description: | |||||
| */ | |||||
| // import { useEffect, useState } from 'react'; | |||||
| // import styles from './index.less'; | |||||
| // import { Tabs } from "antd" | |||||
| // function KFTabs() { | |||||
| // const [iframeUrl, setIframeUrl] = useState(''); | |||||
| // useEffect(() => { | |||||
| // }, []); | |||||
| // return ( | |||||
| // <div className={styles.container}> | |||||
| // <div className={Styles.datasetAllBox}> | |||||
| // <Tabs defaultActiveKey="1"> | |||||
| // <TabPane tab="数据广场" key="1"> | |||||
| // <PublicData /> | |||||
| // </TabPane> | |||||
| // <TabPane tab="个人数据" key="2"> | |||||
| // <PersonalData /> | |||||
| // </TabPane> | |||||
| // </Tabs> | |||||
| // </div> | |||||
| // </div> | |||||
| // ); | |||||
| // } | |||||
| // export default KFTabs; | |||||
| @@ -194,6 +194,6 @@ ol { | |||||
| } | } | ||||
| } | } | ||||
| .local-svg { | |||||
| .umi-local-svg { | |||||
| vertical-align: -1px; | vertical-align: -1px; | ||||
| } | } | ||||
| @@ -0,0 +1,3 @@ | |||||
| <svg xmlns="http://www.w3.org/2000/svg" width="15.429" height="15.429" viewBox="0 0 15.429 15.429"> | |||||
| <path id="搜索" d="M98.21,97.392a7.248,7.248,0,1,0-3.422,2.26.579.579,0,1,0-.338-1.107,6.218,6.218,0,1,1,2.6-1.6.669.669,0,0,0,.007.939l2.712,2.712a.579.579,0,0,0,.818-.818Z" transform="translate(-85.333 -85.333)" fill="rgba(22,100,255,0.4)"/> | |||||
| </svg> | |||||
| @@ -62,7 +62,7 @@ export const getParamRules = (paramType: number, required: boolean = false): For | |||||
| }; | }; | ||||
| // 根据参数设置 label | // 根据参数设置 label | ||||
| const getParamType = (param: PipelineGlobalParam): string => { | |||||
| export const getParamType = (param: PipelineGlobalParam): string => { | |||||
| const paramTypes: Readonly<Record<number, string>> = { | const paramTypes: Readonly<Record<number, string>> = { | ||||
| 1: '字符串', | 1: '字符串', | ||||
| 2: '整型', | 2: '整型', | ||||
| @@ -1,6 +1,7 @@ | |||||
| .params_container { | .params_container { | ||||
| max-height: 230px; | max-height: 230px; | ||||
| padding: 15px 15px 0; | padding: 15px 15px 0; | ||||
| overflow-y: auto; | |||||
| border: 1px solid #e6e6e6; | border: 1px solid #e6e6e6; | ||||
| border-radius: 8px; | border-radius: 8px; | ||||
| @@ -1,6 +1,12 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-09 15:59:14 | |||||
| * @Description: 查看实验使用的参数 | |||||
| */ | |||||
| import parameterImg from '@/assets/img/modal-parameter.png'; | import parameterImg from '@/assets/img/modal-parameter.png'; | ||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { type PipelineGlobalParam } from '@/types'; | import { type PipelineGlobalParam } from '@/types'; | ||||
| import { getParamType } from './addExperimentModal'; | |||||
| import styles from './paramsModal.less'; | import styles from './paramsModal.less'; | ||||
| type ParamsModalProps = { | type ParamsModalProps = { | ||||
| @@ -22,7 +28,7 @@ function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) { | |||||
| <div className={styles.params_container}> | <div className={styles.params_container}> | ||||
| {globalParam.map((item) => ( | {globalParam.map((item) => ( | ||||
| <div key={item.param_name} className={styles.params_container_line}> | <div key={item.param_name} className={styles.params_container_line}> | ||||
| <span className={styles.params_container_line_label}>{item.param_name}</span> | |||||
| <span className={styles.params_container_line_label}>{getParamType(item)}</span> | |||||
| <span className={styles.params_container_line_value}>{item.param_value}</span> | <span className={styles.params_container_line_value}>{item.param_value}</span> | ||||
| </div> | </div> | ||||
| ))} | ))} | ||||
| @@ -0,0 +1,42 @@ | |||||
| .mirror-list { | |||||
| height: 100%; | |||||
| background-color: #f9fafb; | |||||
| &__tabs-container { | |||||
| height: 59px; | |||||
| background-image: url('../../assets/img/mirror-tabs-bg.png'); | |||||
| } | |||||
| &__content { | |||||
| height: calc(100% - 69px); | |||||
| margin-top: 10px; | |||||
| padding: 20px 30px 0; | |||||
| background-color: white; | |||||
| border-radius: 10px; | |||||
| &__filter { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: space-between; | |||||
| padding: 0 10px; | |||||
| } | |||||
| &__table { | |||||
| height: calc(100% - 44px); | |||||
| margin-top: 12px; | |||||
| :global { | |||||
| .ant-table-wrapper { | |||||
| height: 100%; | |||||
| .ant-spin-nested-loading { | |||||
| height: 100%; | |||||
| } | |||||
| .ant-spin-container { | |||||
| height: 100%; | |||||
| } | |||||
| .ant-table { | |||||
| height: calc(100% - 74px); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,155 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-16 13:58:08 | |||||
| * @Description: 镜像列表 | |||||
| */ | |||||
| import { getMirrorListReq } from '@/services/mirror'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { Button, Input, Table } from 'antd'; | |||||
| import dayjs from 'dayjs'; | |||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| import styles from './list.less'; | |||||
| const tabItems = [ | |||||
| { | |||||
| key: 'Public', | |||||
| label: '公共镜像', | |||||
| }, | |||||
| { | |||||
| key: 'Private', | |||||
| label: '个人镜像', | |||||
| }, | |||||
| ]; | |||||
| function MirrorList() { | |||||
| const [activeTab, setActiveTab] = useState('Public'); | |||||
| const [tableData, setTableData] = useState([]); | |||||
| const contentRef = useRef<HTMLDivElement>(null); | |||||
| const [tableHeight, setTableHeight] = useState(0); | |||||
| const [pagination, setPagination] = useState({ | |||||
| showSizeChanger: true, | |||||
| showQuickJumper: true, | |||||
| current: 1, | |||||
| pageSize: 10, | |||||
| total: 0, | |||||
| }); | |||||
| useEffect(() => { | |||||
| getMirrorList(''); | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| const setTableScollHeight = () => { | |||||
| if (contentRef.current) { | |||||
| setTableHeight(contentRef.current.offsetHeight - 74 - 55); | |||||
| } | |||||
| }; | |||||
| setTableScollHeight(); | |||||
| window.addEventListener('resize', setTableScollHeight); | |||||
| return () => { | |||||
| window.removeEventListener('resize', setTableScollHeight); | |||||
| }; | |||||
| }, [contentRef]); | |||||
| const columns = [ | |||||
| { | |||||
| title: '镜像名称', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '10%', | |||||
| }, | |||||
| { | |||||
| title: '版本数据', | |||||
| dataIndex: 'version_count', | |||||
| key: 'version_count', | |||||
| width: '10%', | |||||
| }, | |||||
| { | |||||
| title: '镜像描述', | |||||
| dataIndex: 'description', | |||||
| key: 'description', | |||||
| width: '50%', | |||||
| }, | |||||
| { | |||||
| title: '创建时间', | |||||
| dataIndex: 'create_time', | |||||
| key: 'create_time', | |||||
| width: '20%', | |||||
| render: (text: string) => <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>, | |||||
| }, | |||||
| { | |||||
| title: '操作', | |||||
| dataIndex: 'operation', | |||||
| width: '100px', | |||||
| key: 'operation', | |||||
| render: (_: any, record: any) => [ | |||||
| <Button | |||||
| type="link" | |||||
| size="small" | |||||
| key="download" | |||||
| // onClick={(e) => downloadAlone(e, record)} | |||||
| > | |||||
| 查看详情 | |||||
| </Button>, | |||||
| ], | |||||
| }, | |||||
| ]; | |||||
| const getMirrorList = async (name: string) => { | |||||
| const params = { | |||||
| page: pagination.current - 1, | |||||
| size: pagination.pageSize, | |||||
| name, | |||||
| image_type: 1, | |||||
| }; | |||||
| const [res] = await to(getMirrorListReq(params)); | |||||
| if (res && res.data) { | |||||
| const { content = [], totalElements = 0 } = res.data; | |||||
| console.log(res); | |||||
| setTableData(content); | |||||
| setPagination((prev) => ({ | |||||
| ...prev, | |||||
| total: totalElements, | |||||
| })); | |||||
| } | |||||
| }; | |||||
| const onSearch = (value: string) => { | |||||
| getMirrorList(value); | |||||
| }; | |||||
| return ( | |||||
| <div className={styles['mirror-list']}> | |||||
| <div className={styles['mirror-list__tabs-container']}> | |||||
| {/* <Tabs | |||||
| activeKey={activeTab} | |||||
| items={tabItems} | |||||
| onChange={setActiveTab} | |||||
| className={styles['model-tabs']} | |||||
| /> */} | |||||
| </div> | |||||
| <div className={styles['mirror-list__content']}> | |||||
| <div className={styles['mirror-list__content__filter']}> | |||||
| <Input.Search | |||||
| placeholder="按数据集名称筛选" | |||||
| allowClear | |||||
| onSearch={onSearch} | |||||
| style={{ width: 300 }} | |||||
| /> | |||||
| <Button type="default">刷新</Button> | |||||
| </div> | |||||
| <div className={styles['mirror-list__content__table']} ref={contentRef}> | |||||
| <Table | |||||
| dataSource={tableData} | |||||
| columns={columns} | |||||
| scroll={{ y: tableHeight }} | |||||
| pagination={pagination} | |||||
| rowKey="id" | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default MirrorList; | |||||
| @@ -1,13 +1,18 @@ | |||||
| @import '@/styles/theme.less'; | @import '@/styles/theme.less'; | ||||
| .model-tabs { | .model-tabs { | ||||
| margin-left: 8px; | |||||
| :global { | :global { | ||||
| .ant-tabs-tab { | .ant-tabs-tab { | ||||
| padding-top: 0 !important; | |||||
| padding-top: 0; | |||||
| } | } | ||||
| .ant-tabs-nav::before, | .ant-tabs-nav::before, | ||||
| div > .ant-tabs-nav::before { | div > .ant-tabs-nav::before { | ||||
| border: none !important; | |||||
| border: none; | |||||
| } | |||||
| .ant-tabs-nav { | |||||
| margin-bottom: 0; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -16,6 +21,12 @@ | |||||
| display: flex; | display: flex; | ||||
| align-items: flex-start; | align-items: flex-start; | ||||
| :global { | |||||
| .ant-input-affix-wrapper .ant-input-prefix { | |||||
| margin-inline-end: 12px; | |||||
| } | |||||
| } | |||||
| &__left { | &__left { | ||||
| width: 488px; | width: 488px; | ||||
| height: 398px; | height: 398px; | ||||
| @@ -27,20 +38,11 @@ | |||||
| &__search { | &__search { | ||||
| margin-bottom: 14px; | margin-bottom: 14px; | ||||
| padding-left: 0; | |||||
| background-color: transparent; | background-color: transparent; | ||||
| border-width: 0; | border-width: 0; | ||||
| border-bottom: 1px solid @border-color-second; | border-bottom: 1px solid @border-color-second; | ||||
| border-radius: 0; | border-radius: 0; | ||||
| // &:hover { | |||||
| // background-color: transparent; | |||||
| // } | |||||
| // &:active { | |||||
| // background-color: transparent; | |||||
| // } | |||||
| // &:focus { | |||||
| // background-color: transparent; | |||||
| // } | |||||
| } | } | ||||
| } | } | ||||
| @@ -16,9 +16,10 @@ import { | |||||
| getModelVersionsById, | getModelVersionsById, | ||||
| } from '@/services/dataset/index.js'; | } from '@/services/dataset/index.js'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { Icon } from '@umijs/max'; | |||||
| import type { GetRef, ModalProps, TabsProps, TreeDataNode, TreeProps } from 'antd'; | import type { GetRef, ModalProps, TabsProps, TreeDataNode, TreeProps } from 'antd'; | ||||
| import { Input, Tabs, Tree } from 'antd'; | import { Input, Tabs, Tree } from 'antd'; | ||||
| import React, { useEffect, useRef, useState } from 'react'; | |||||
| import React, { useEffect, useMemo, useRef, useState } from 'react'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| export enum ResourceSelectorType { | export enum ResourceSelectorType { | ||||
| @@ -30,7 +31,7 @@ type ResourceSelectorTypeKeys = keyof typeof ResourceSelectorType; | |||||
| type ResourceSelectorTypeValues = (typeof ResourceSelectorType)[ResourceSelectorTypeKeys]; | type ResourceSelectorTypeValues = (typeof ResourceSelectorType)[ResourceSelectorTypeKeys]; | ||||
| type GetModelFilesReqParam = { | type GetModelFilesReqParam = { | ||||
| model_id: number; | |||||
| models_id: number; | |||||
| version: string; | version: string; | ||||
| }; | }; | ||||
| @@ -52,6 +53,11 @@ export type SelectorTypeInfo = { | |||||
| tabItems: TabsProps['items']; | tabItems: TabsProps['items']; | ||||
| }; | }; | ||||
| enum TabItemKeys { | |||||
| Private = 'Private', // 我的 | |||||
| Public = 'Public', // 公开 | |||||
| } | |||||
| export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeInfo> = { | export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeInfo> = { | ||||
| Model: { | Model: { | ||||
| getList: getModelList, | getList: getModelList, | ||||
| @@ -63,11 +69,11 @@ export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeIn | |||||
| fileReqParamKey: 'models_id', | fileReqParamKey: 'models_id', | ||||
| tabItems: [ | tabItems: [ | ||||
| { | { | ||||
| key: '0', | |||||
| key: TabItemKeys.Private, | |||||
| label: '我的模型', | label: '我的模型', | ||||
| }, | }, | ||||
| { | { | ||||
| key: '1', | |||||
| key: TabItemKeys.Public, | |||||
| label: '公开模型', | label: '公开模型', | ||||
| }, | }, | ||||
| ], | ], | ||||
| @@ -82,11 +88,11 @@ export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeIn | |||||
| fileReqParamKey: 'dataset_id', | fileReqParamKey: 'dataset_id', | ||||
| tabItems: [ | tabItems: [ | ||||
| { | { | ||||
| key: '0', | |||||
| key: TabItemKeys.Private, | |||||
| label: '我的数据集', | label: '我的数据集', | ||||
| }, | }, | ||||
| { | { | ||||
| key: '1', | |||||
| key: TabItemKeys.Public, | |||||
| label: '公开数据集', | label: '公开数据集', | ||||
| }, | }, | ||||
| ], | ], | ||||
| @@ -94,31 +100,33 @@ export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeIn | |||||
| }; | }; | ||||
| type ResourceSelectorResponse = { | type ResourceSelectorResponse = { | ||||
| id: number; | |||||
| name: string; | |||||
| path: string; | |||||
| version: string; | |||||
| activeTab: string; | |||||
| id: number; // 数据集或者模型 id | |||||
| name: string; // 数据集或者模型 name | |||||
| version: string; // 数据集或者模型版本 | |||||
| path: string; // 数据集或者模型版本路径 | |||||
| activeTab: TabItemKeys; // 是我的还是公开的 | |||||
| }; | }; | ||||
| interface ResourceSelectorModalProps extends Omit<ModalProps, 'onOk'> { | interface ResourceSelectorModalProps extends Omit<ModalProps, 'onOk'> { | ||||
| type: ResourceSelectorType; // 模型 | 数据集 | type: ResourceSelectorType; // 模型 | 数据集 | ||||
| defaultExpandedKeys: React.Key[]; | defaultExpandedKeys: React.Key[]; | ||||
| defaultCheckedKeys: React.Key[]; | defaultCheckedKeys: React.Key[]; | ||||
| defaultActiveTab: string; | |||||
| defaultActiveTab: TabItemKeys; | |||||
| onOk?: (params: ResourceSelectorResponse | null) => void; | onOk?: (params: ResourceSelectorResponse | null) => void; | ||||
| } | } | ||||
| type ResourceGroup = { | type ResourceGroup = { | ||||
| id: number; | |||||
| name: string; | |||||
| id: number; // 数据集或者模型 id | |||||
| name: string; // 数据集或者模型 id | |||||
| }; | }; | ||||
| type ResourceFile = { | type ResourceFile = { | ||||
| id: number; | |||||
| file_name: string; | |||||
| id: number; // 文件 id | |||||
| file_name: string; // 文件 name | |||||
| }; | }; | ||||
| type TreeRef = GetRef<typeof Tree<TreeDataNode>>; | |||||
| // list 转成 treeData | // list 转成 treeData | ||||
| const convertToTreeData = (list: ResourceGroup[]): TreeDataNode[] => { | const convertToTreeData = (list: ResourceGroup[]): TreeDataNode[] => { | ||||
| return list.map((v) => ({ | return list.map((v) => ({ | ||||
| @@ -129,6 +137,7 @@ const convertToTreeData = (list: ResourceGroup[]): TreeDataNode[] => { | |||||
| })); | })); | ||||
| }; | }; | ||||
| // 更新树形结构的 children | |||||
| const updateChildren = (parentId: number, children: TreeDataNode[]) => { | const updateChildren = (parentId: number, children: TreeDataNode[]) => { | ||||
| return (node: TreeDataNode) => { | return (node: TreeDataNode) => { | ||||
| if (node.key === parentId) { | if (node.key === parentId) { | ||||
| @@ -141,22 +150,30 @@ const updateChildren = (parentId: number, children: TreeDataNode[]) => { | |||||
| }; | }; | ||||
| }; | }; | ||||
| type TreeRef = GetRef<typeof Tree<TreeDataNode>>; | |||||
| // 得到数据集或者模型 id 和下属版本号 | |||||
| const getIdAndVersion = (versionKey: string) => { | |||||
| const index = versionKey.indexOf('-'); | |||||
| const id = Number(versionKey.slice(0, index)); | |||||
| const version = versionKey.slice(index + 1); | |||||
| return { | |||||
| id, | |||||
| version, | |||||
| }; | |||||
| }; | |||||
| function ResourceSelectorModal({ | function ResourceSelectorModal({ | ||||
| type, | type, | ||||
| defaultExpandedKeys = [], | defaultExpandedKeys = [], | ||||
| defaultCheckedKeys = [], | defaultCheckedKeys = [], | ||||
| defaultActiveTab = '0', | |||||
| defaultActiveTab = TabItemKeys.Private, | |||||
| onOk, | onOk, | ||||
| ...rest | ...rest | ||||
| }: ResourceSelectorModalProps) { | }: ResourceSelectorModalProps) { | ||||
| const [activeTab, setActiveTab] = useState(defaultActiveTab); | |||||
| const [activeTab, setActiveTab] = useState<string>(defaultActiveTab); | |||||
| const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]); | const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]); | ||||
| const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]); | const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]); | ||||
| const [loadedKeys, setLoadedKeys] = useState<React.Key[]>([]); | const [loadedKeys, setLoadedKeys] = useState<React.Key[]>([]); | ||||
| const [originTreeData, setOriginTreeData] = useState<TreeDataNode[]>([]); | const [originTreeData, setOriginTreeData] = useState<TreeDataNode[]>([]); | ||||
| const [treeData, setTreeData] = useState<TreeDataNode[]>([]); | |||||
| const [files, setFiles] = useState<ResourceFile[]>([]); | const [files, setFiles] = useState<ResourceFile[]>([]); | ||||
| const [versionPath, setVersionPath] = useState(''); | const [versionPath, setVersionPath] = useState(''); | ||||
| const [searchText, setSearchText] = useState(''); | const [searchText, setSearchText] = useState(''); | ||||
| @@ -174,19 +191,21 @@ function ResourceSelectorModal({ | |||||
| getTreeData(); | getTreeData(); | ||||
| }, [activeTab, type]); | }, [activeTab, type]); | ||||
| useEffect(() => { | |||||
| const treeData = originTreeData.filter((v) => | |||||
| (v.title as string).toLowerCase()?.includes(searchText.toLowerCase()), | |||||
| ); | |||||
| setTreeData(treeData); | |||||
| }, [originTreeData, searchText]); | |||||
| const treeData = useMemo( | |||||
| () => | |||||
| originTreeData.filter((v) => | |||||
| (v.title as string).toLowerCase()?.includes(searchText.toLowerCase()), | |||||
| ), | |||||
| [originTreeData, searchText], | |||||
| ); | |||||
| // 获取数据集或模型列表 | // 获取数据集或模型列表 | ||||
| const getTreeData = async () => { | const getTreeData = async () => { | ||||
| const available_range = activeTab === TabItemKeys.Private ? '0' : '1'; | |||||
| const params = { | const params = { | ||||
| page: 0, | page: 0, | ||||
| size: 200, | size: 200, | ||||
| available_range: activeTab, | |||||
| available_range: available_range, | |||||
| }; | }; | ||||
| const getListReq = selectorTypeData[type].getList; | const getListReq = selectorTypeData[type].getList; | ||||
| const [res] = await to(getListReq(params)); | const [res] = await to(getListReq(params)); | ||||
| @@ -255,7 +274,7 @@ function ResourceSelectorModal({ | |||||
| // 扩展 | // 扩展 | ||||
| const onExpand: TreeProps['onExpand'] = (expandedKeysValue) => { | const onExpand: TreeProps['onExpand'] = (expandedKeysValue) => { | ||||
| const lastKeys = (expandedKeysValue as React.Key[]).slice(-1); | |||||
| const lastKeys = expandedKeysValue.slice(-1); | |||||
| setExpandedKeys(lastKeys); | setExpandedKeys(lastKeys); | ||||
| }; | }; | ||||
| @@ -265,9 +284,7 @@ function ResourceSelectorModal({ | |||||
| setCheckedKeys(lastKeys); | setCheckedKeys(lastKeys); | ||||
| if (lastKeys.length) { | if (lastKeys.length) { | ||||
| const last = lastKeys[0] as string; | const last = lastKeys[0] as string; | ||||
| const index = last.indexOf('-'); | |||||
| const id = Number(last.slice(0, index)); | |||||
| const version = last.slice(index + 1); | |||||
| const { id, version } = getIdAndVersion(last); | |||||
| getFiles(id, version); | getFiles(id, version); | ||||
| } else { | } else { | ||||
| setFiles([]); | setFiles([]); | ||||
| @@ -295,9 +312,7 @@ function ResourceSelectorModal({ | |||||
| const restoreLastCheck = (parentId: number) => { | const restoreLastCheck = (parentId: number) => { | ||||
| if (!fisrtLoadVersions && defaultCheckedKeys.length > 0) { | if (!fisrtLoadVersions && defaultCheckedKeys.length > 0) { | ||||
| const last = defaultCheckedKeys[0] as string; | const last = defaultCheckedKeys[0] as string; | ||||
| const index = last.indexOf('-'); | |||||
| const id = Number(last.slice(0, index)); | |||||
| const version = last.slice(index + 1); | |||||
| const { id, version } = getIdAndVersion(last); | |||||
| // 判断正在打开的 id 和 defaultCheckedKeys 的 id 是否一致 | // 判断正在打开的 id 和 defaultCheckedKeys 的 id 是否一致 | ||||
| if (id === parentId) { | if (id === parentId) { | ||||
| setTimeout(() => { | setTimeout(() => { | ||||
| @@ -319,16 +334,14 @@ function ResourceSelectorModal({ | |||||
| const handleOk = () => { | const handleOk = () => { | ||||
| if (checkedKeys.length > 0) { | if (checkedKeys.length > 0) { | ||||
| const last = checkedKeys[0] as string; | const last = checkedKeys[0] as string; | ||||
| const index = last.indexOf('-'); | |||||
| const id = Number(last.slice(0, index)); | |||||
| const version = last.slice(index + 1); | |||||
| const { id, version } = getIdAndVersion(last); | |||||
| const name = (treeData.find((v) => Number(v.key) === id)?.title ?? '') as string; | const name = (treeData.find((v) => Number(v.key) === id)?.title ?? '') as string; | ||||
| const res = { | const res = { | ||||
| id, | id, | ||||
| name, | name, | ||||
| path: versionPath, | path: versionPath, | ||||
| version, | version, | ||||
| activeTab, | |||||
| activeTab: activeTab as TabItemKeys, | |||||
| }; | }; | ||||
| onOk?.(res); | onOk?.(res); | ||||
| } else { | } else { | ||||
| @@ -344,7 +357,7 @@ function ResourceSelectorModal({ | |||||
| return ( | return ( | ||||
| <KFModal {...rest} title={title} image={titleImg} onOk={handleOk} width={920} destroyOnClose> | <KFModal {...rest} title={title} image={titleImg} onOk={handleOk} width={920} destroyOnClose> | ||||
| <div className={styles}> | |||||
| <div> | |||||
| <Tabs | <Tabs | ||||
| activeKey={activeTab} | activeKey={activeTab} | ||||
| items={tabItems} | items={tabItems} | ||||
| @@ -360,6 +373,7 @@ function ResourceSelectorModal({ | |||||
| variant="borderless" | variant="borderless" | ||||
| value={searchText} | value={searchText} | ||||
| onChange={(e) => setSearchText(e.target.value)} | onChange={(e) => setSearchText(e.target.value)} | ||||
| prefix={<Icon icon="local:magnifying-glass" style={{ height: '15px' }} />} | |||||
| /> | /> | ||||
| <Tree | <Tree | ||||
| ref={treeRef} | ref={treeRef} | ||||
| @@ -1,3 +1,4 @@ | |||||
| import { pick } from '@/utils/index'; | |||||
| import { openAntdModal } from '@/utils/modal'; | import { openAntdModal } from '@/utils/modal'; | ||||
| import { Icon } from '@umijs/max'; | import { Icon } from '@umijs/max'; | ||||
| import { Button, Drawer, Form, Input } from 'antd'; | import { Button, Drawer, Form, Input } from 'antd'; | ||||
| @@ -9,17 +10,14 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const [stagingItem, setStagingItem] = useState({}); | const [stagingItem, setStagingItem] = useState({}); | ||||
| const [open, setOpen] = useState(false); | const [open, setOpen] = useState(false); | ||||
| // const [modelSelectorOpen, openModelSelector, closeModelSelector] = useVisible(false); | |||||
| // const [selectorType, setSelectorType] = useState(SelectorType.Model); | |||||
| // const [formItemName, setFormItemName] = useState(''); | |||||
| const [selectedModel, setSelectedModel] = useState(undefined); | const [selectedModel, setSelectedModel] = useState(undefined); | ||||
| const [selectedDataset, setSelectedDataset] = useState(undefined); | const [selectedDataset, setSelectedDataset] = useState(undefined); | ||||
| const afterOpenChange = () => { | const afterOpenChange = () => { | ||||
| if (!open) { | if (!open) { | ||||
| console.log(111, open); | |||||
| console.log(stagingItem, form.getFieldsValue()); | console.log(stagingItem, form.getFieldsValue()); | ||||
| // 禁止校验 guard-for-in | |||||
| /* eslint-disable */ | |||||
| for (let i in form.getFieldsValue()) { | for (let i in form.getFieldsValue()) { | ||||
| for (let j in stagingItem.in_parameters) { | for (let j in stagingItem.in_parameters) { | ||||
| if (i == j) { | if (i == j) { | ||||
| @@ -38,6 +36,7 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* eslint-enable */ | |||||
| // setStagingItem({...stagingItem,}) | // setStagingItem({...stagingItem,}) | ||||
| console.log(stagingItem.control_strategy); | console.log(stagingItem.control_strategy); | ||||
| onParentChange({ | onParentChange({ | ||||
| @@ -86,14 +85,12 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| }, | }, | ||||
| })); | })); | ||||
| // 选择数据集、模型 | |||||
| const selectResource = (name, item) => { | const selectResource = (name, item) => { | ||||
| // setFormItemName(name); | |||||
| // setSelectorType(item.item_type === 'dataset' ? SelectorType.Dataset : SelectorType.Model); | |||||
| // openModelSelector(); | |||||
| const type = | const type = | ||||
| item.item_type === 'dataset' ? ResourceSelectorType.Dataset : ResourceSelectorType.Model; | item.item_type === 'dataset' ? ResourceSelectorType.Dataset : ResourceSelectorType.Model; | ||||
| const resource = type === ResourceSelectorType.Dataset ? selectedDataset : selectedModel; | const resource = type === ResourceSelectorType.Dataset ? selectedDataset : selectedModel; | ||||
| const { destroy } = openAntdModal( | |||||
| const { close } = openAntdModal( | |||||
| ResourceSelectorModal, | ResourceSelectorModal, | ||||
| { | { | ||||
| type, | type, | ||||
| @@ -102,7 +99,8 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| defaultActiveTab: resource?.activeTab, | defaultActiveTab: resource?.activeTab, | ||||
| onOk: (res) => { | onOk: (res) => { | ||||
| if (res) { | if (res) { | ||||
| const value = JSON.stringify(res); | |||||
| const jsonObj = pick(res, ['id', 'version', 'path']); | |||||
| const value = JSON.stringify(jsonObj); | |||||
| form.setFieldValue(name, value); | form.setFieldValue(name, value); | ||||
| if (type === ResourceSelectorType.Dataset) { | if (type === ResourceSelectorType.Dataset) { | ||||
| setSelectedDataset(res); | setSelectedDataset(res); | ||||
| @@ -117,35 +115,32 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| } | } | ||||
| form.setFieldValue(name, ''); | form.setFieldValue(name, ''); | ||||
| } | } | ||||
| destroy(); | |||||
| close(); | |||||
| }, | }, | ||||
| }, | }, | ||||
| true, | true, | ||||
| ); | ); | ||||
| }; | }; | ||||
| const handleModelSelect = (obj) => { | |||||
| if (obj) { | |||||
| const value = JSON.stringify(obj); | |||||
| setSelectedModel(obj); | |||||
| form.setFieldValue(formItemName, value); | |||||
| } else { | |||||
| form.setFieldValue(formItemName, ''); | |||||
| } | |||||
| closeModelSelector(); | |||||
| }; | |||||
| // 获取选择数据集、模型后面按钮 icon | |||||
| const getSelectBtnIcon = (item) => { | const getSelectBtnIcon = (item) => { | ||||
| const type = item.item_type; | const type = item.item_type; | ||||
| if (type === 'dataset') { | if (type === 'dataset') { | ||||
| return <Icon icon="local:dataset-select-button" className="local-svg" />; | |||||
| return <Icon icon="local:dataset-select-button" className="umi-local-svg" />; | |||||
| } else if (type === 'model') { | } else if (type === 'model') { | ||||
| return <Icon icon="local:model-select-button" className="local-svg" />; | |||||
| return <Icon icon="local:model-select-button" className="umi-local-svg" />; | |||||
| } else { | } else { | ||||
| return <Icon icon="local:mirror-select-button" className="local-svg" />; | |||||
| return <Icon icon="local:mirror-select-button" className="umi-local-svg" />; | |||||
| } | } | ||||
| }; | }; | ||||
| // 控制策略 | |||||
| const controlStrategy = stagingItem.control_strategy; | |||||
| // 输入参数 | |||||
| const inParameters = stagingItem.in_parameters; | |||||
| // 输出参数 | |||||
| const outParameters = stagingItem.out_parameters; | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Drawer | <Drawer | ||||
| @@ -254,11 +249,9 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| <Form.Item label="环境变量" name="env_variables"> | <Form.Item label="环境变量" name="env_variables"> | ||||
| <TextArea /> | <TextArea /> | ||||
| </Form.Item> | </Form.Item> | ||||
| {stagingItem.control_strategy && | |||||
| Object.keys(stagingItem.control_strategy) && | |||||
| Object.keys(stagingItem.control_strategy).length > 0 | |||||
| ? Object.keys(stagingItem.control_strategy).map((item) => ( | |||||
| <Form.Item key={item} label={stagingItem.control_strategy[item].label} name={item}> | |||||
| {controlStrategy && Object.keys(controlStrategy).length > 0 | |||||
| ? Object.keys(controlStrategy).map((item) => ( | |||||
| <Form.Item key={item} label={controlStrategy[item].label} name={item}> | |||||
| <Input /> | <Input /> | ||||
| </Form.Item> | </Form.Item> | ||||
| )) | )) | ||||
| @@ -271,32 +264,33 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| /> | /> | ||||
| 输入参数 | 输入参数 | ||||
| </div> | </div> | ||||
| {stagingItem.in_parameters && | |||||
| Object.keys(stagingItem.in_parameters) && | |||||
| Object.keys(stagingItem.in_parameters).length > 0 | |||||
| ? Object.keys(stagingItem.in_parameters).map((item) => ( | |||||
| {inParameters && Object.keys(inParameters).length > 0 | |||||
| ? Object.keys(inParameters).map((item) => ( | |||||
| <Form.Item | <Form.Item | ||||
| key={item} | key={item} | ||||
| label={stagingItem.in_parameters[item].label + '(' + item + ')'} | |||||
| label={inParameters[item].label + '(' + item + ')'} | |||||
| required={inParameters[item].require ? true : false} | |||||
| > | > | ||||
| <div className={Styles['ref-row']}> | <div className={Styles['ref-row']}> | ||||
| <Form.Item | <Form.Item | ||||
| name={item} | name={item} | ||||
| noStyle | noStyle | ||||
| rules={[{ required: stagingItem.in_parameters[item].require ? true : false }]} | |||||
| rules={[{ required: inParameters[item].require ? true : false }]} | |||||
| > | > | ||||
| <Input /> | <Input /> | ||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item noStyle> | |||||
| <Button | |||||
| type="link" | |||||
| icon={getSelectBtnIcon(stagingItem.in_parameters[item])} | |||||
| onClick={() => selectResource(item, stagingItem.in_parameters[item])} | |||||
| className={Styles['select-button']} | |||||
| > | |||||
| {stagingItem.in_parameters[item].label} | |||||
| </Button> | |||||
| </Form.Item> | |||||
| {inParameters[item].type === 'ref' && ( | |||||
| <Form.Item noStyle> | |||||
| <Button | |||||
| type="link" | |||||
| icon={getSelectBtnIcon(inParameters[item])} | |||||
| onClick={() => selectResource(item, inParameters[item])} | |||||
| className={Styles['select-button']} | |||||
| > | |||||
| {inParameters[item].label} | |||||
| </Button> | |||||
| </Form.Item> | |||||
| )} | |||||
| </div> | </div> | ||||
| </Form.Item> | </Form.Item> | ||||
| )) | )) | ||||
| @@ -309,14 +303,12 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| /> | /> | ||||
| 输出参数 | 输出参数 | ||||
| </div> | </div> | ||||
| {stagingItem.out_parameters && | |||||
| Object.keys(stagingItem.out_parameters) && | |||||
| Object.keys(stagingItem.out_parameters).length > 0 | |||||
| ? Object.keys(stagingItem.out_parameters).map((item) => ( | |||||
| {outParameters && Object.keys(outParameters).length > 0 | |||||
| ? Object.keys(outParameters).map((item) => ( | |||||
| <Form.Item | <Form.Item | ||||
| key={item} | key={item} | ||||
| label={stagingItem.out_parameters[item].label + '(' + item + ')'} | |||||
| rules={[{ required: stagingItem.out_parameters[item].require ? true : false }]} | |||||
| label={outParameters[item].label + '(' + item + ')'} | |||||
| rules={[{ required: outParameters[item].require ? true : false }]} | |||||
| name={item} | name={item} | ||||
| > | > | ||||
| <Input /> | <Input /> | ||||
| @@ -324,18 +316,6 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| )) | )) | ||||
| : ''} | : ''} | ||||
| </Form> | </Form> | ||||
| {/* {modelSelectorOpen && ( | |||||
| <ResourceSelectorModal | |||||
| open={modelSelectorOpen} | |||||
| onCancel={closeModelSelector} | |||||
| defaultExpandedKeys={selectedModel ? [selectedModel.id] : []} | |||||
| defaultCheckedKeys={ | |||||
| selectedModel ? [`${selectedModel.id}-${selectedModel.version}`] : [] | |||||
| } | |||||
| type={selectorType} | |||||
| onOk={handleModelSelect} | |||||
| /> | |||||
| )} */} | |||||
| </Drawer> | </Drawer> | ||||
| </> | </> | ||||
| ); | ); | ||||
| @@ -0,0 +1,22 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-16 14:29:44 | |||||
| * @Description: | |||||
| */ | |||||
| import { request } from '@umijs/max'; | |||||
| // 分页查询镜像列表 | |||||
| export function getMirrorListReq(params: any) { | |||||
| return request(`/api/mmp/image/`, { | |||||
| method: 'GET', | |||||
| params, | |||||
| }); | |||||
| } | |||||
| // // 分页查询镜像列表 | |||||
| // export function getMirrorList(params: any) { | |||||
| // return request(`/image/`, { | |||||
| // method: 'GET', | |||||
| // params, | |||||
| // }); | |||||
| // } | |||||
| @@ -1,12 +0,0 @@ | |||||
| export function s8() { | |||||
| return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1); | |||||
| } | |||||
| export function getNameByCode(list, code) { | |||||
| let name = ''; | |||||
| list.forEach((item) => { | |||||
| if (item.dictValue === code) name = item.dictLabel; | |||||
| }); | |||||
| return name; | |||||
| } | |||||
| @@ -0,0 +1,50 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-03-25 13:52:54 | |||||
| * @Description: | |||||
| */ | |||||
| export function s8() { | |||||
| return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1); | |||||
| } | |||||
| export function getNameByCode(list: any[], code: any) { | |||||
| let name = ''; | |||||
| list.forEach((item) => { | |||||
| if (item.dictValue === code) name = item.dictLabel; | |||||
| }); | |||||
| return name; | |||||
| } | |||||
| /** | |||||
| * Picks specified properties from an object and returns a new object with only those properties. | |||||
| * | |||||
| * @param obj - The object to pick properties from. | |||||
| * @param properties - An array of property names to pick from the object. | |||||
| * @return A new object with only the picked properties. | |||||
| */ | |||||
| export function pick<T extends object, K extends keyof T>(obj: T, properties: K[]): Pick<T, K> { | |||||
| const picked: Partial<T> = {}; | |||||
| for (const key of properties) { | |||||
| if (Object.prototype.hasOwnProperty.call(obj, key)) { | |||||
| picked[key] = obj[key]; | |||||
| } | |||||
| } | |||||
| return picked as Pick<T, K>; | |||||
| } | |||||
| /** | |||||
| * Omit properties from an object and return a new object without those properties. | |||||
| * | |||||
| * @param obj - The object to omit properties from. | |||||
| * @param properties - An array of property names to omit from the object. | |||||
| * @return A new object without the omitted properties. | |||||
| */ | |||||
| export function omit<T extends object, K extends keyof T>(obj: T, properties: K[]): Omit<T, K> { | |||||
| const omitted: Partial<T> = { ...obj }; | |||||
| for (const key of properties) { | |||||
| if (Object.prototype.hasOwnProperty.call(omitted, key)) { | |||||
| delete omitted[key]; | |||||
| } | |||||
| } | |||||
| return omitted as Omit<T, K>; | |||||
| } | |||||
| @@ -56,7 +56,7 @@ export const openAntdModal = <T extends ModalProps>( | |||||
| render({ ...modalProps, open: true }); | render({ ...modalProps, open: true }); | ||||
| return { | return { | ||||
| destroy: close, | |||||
| close, | |||||
| }; | }; | ||||
| }; | }; | ||||