| @@ -135,6 +135,17 @@ export default [ | |||
| path: '/dataset/datasetIntro/:id', | |||
| component: './Dataset/datasetIntro', | |||
| }, | |||
| { | |||
| name: '镜像', | |||
| path: 'mirror', | |||
| routes: [ | |||
| { | |||
| name: '镜像列表', | |||
| path: '', | |||
| component: './Mirror/list', | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| name: '模型管理', | |||
| path: '/dataset/modelIndex', | |||
| @@ -5,22 +5,31 @@ | |||
| */ | |||
| import ModalTitle from '@/components/ModalTitle'; | |||
| import { Modal, type ModalProps } from 'antd'; | |||
| import { ConfigProvider, Modal, theme, type ModalProps } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useAntdConfig } from 'umi'; | |||
| import './index.less'; | |||
| const { useToken } = theme; | |||
| export interface KFModalProps extends ModalProps { | |||
| image: string; | |||
| } | |||
| function KFModal({ title, image, children, className = '', ...rest }: KFModalProps) { | |||
| const { token } = useToken(); | |||
| console.log('token', token); | |||
| const antdConfig = useAntdConfig(); | |||
| console.log('antdConfig', antdConfig); | |||
| 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; | |||
| } | |||
| @@ -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 | |||
| const getParamType = (param: PipelineGlobalParam): string => { | |||
| export const getParamType = (param: PipelineGlobalParam): string => { | |||
| const paramTypes: Readonly<Record<number, string>> = { | |||
| 1: '字符串', | |||
| 2: '整型', | |||
| @@ -1,6 +1,7 @@ | |||
| .params_container { | |||
| max-height: 230px; | |||
| padding: 15px 15px 0; | |||
| overflow-y: auto; | |||
| border: 1px solid #e6e6e6; | |||
| 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 KFModal from '@/components/KFModal'; | |||
| import { type PipelineGlobalParam } from '@/types'; | |||
| import { getParamType } from './addExperimentModal'; | |||
| import styles from './paramsModal.less'; | |||
| type ParamsModalProps = { | |||
| @@ -22,7 +28,7 @@ function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) { | |||
| <div className={styles.params_container}> | |||
| {globalParam.map((item) => ( | |||
| <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> | |||
| </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'; | |||
| .model-tabs { | |||
| margin-left: 8px; | |||
| :global { | |||
| .ant-tabs-tab { | |||
| padding-top: 0 !important; | |||
| padding-top: 0; | |||
| } | |||
| .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; | |||
| align-items: flex-start; | |||
| :global { | |||
| .ant-input-affix-wrapper .ant-input-prefix { | |||
| margin-inline-end: 12px; | |||
| } | |||
| } | |||
| &__left { | |||
| width: 488px; | |||
| height: 398px; | |||
| @@ -27,20 +38,11 @@ | |||
| &__search { | |||
| margin-bottom: 14px; | |||
| padding-left: 0; | |||
| background-color: transparent; | |||
| border-width: 0; | |||
| border-bottom: 1px solid @border-color-second; | |||
| border-radius: 0; | |||
| // &:hover { | |||
| // background-color: transparent; | |||
| // } | |||
| // &:active { | |||
| // background-color: transparent; | |||
| // } | |||
| // &:focus { | |||
| // background-color: transparent; | |||
| // } | |||
| } | |||
| } | |||
| @@ -16,9 +16,10 @@ import { | |||
| getModelVersionsById, | |||
| } from '@/services/dataset/index.js'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Icon } from '@umijs/max'; | |||
| import type { GetRef, ModalProps, TabsProps, TreeDataNode, TreeProps } 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'; | |||
| export enum ResourceSelectorType { | |||
| @@ -30,7 +31,7 @@ type ResourceSelectorTypeKeys = keyof typeof ResourceSelectorType; | |||
| type ResourceSelectorTypeValues = (typeof ResourceSelectorType)[ResourceSelectorTypeKeys]; | |||
| type GetModelFilesReqParam = { | |||
| model_id: number; | |||
| models_id: number; | |||
| version: string; | |||
| }; | |||
| @@ -52,6 +53,11 @@ export type SelectorTypeInfo = { | |||
| tabItems: TabsProps['items']; | |||
| }; | |||
| enum TabItemKeys { | |||
| Private = 'Private', // 我的 | |||
| Public = 'Public', // 公开 | |||
| } | |||
| export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeInfo> = { | |||
| Model: { | |||
| getList: getModelList, | |||
| @@ -63,11 +69,11 @@ export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeIn | |||
| fileReqParamKey: 'models_id', | |||
| tabItems: [ | |||
| { | |||
| key: '0', | |||
| key: TabItemKeys.Private, | |||
| label: '我的模型', | |||
| }, | |||
| { | |||
| key: '1', | |||
| key: TabItemKeys.Public, | |||
| label: '公开模型', | |||
| }, | |||
| ], | |||
| @@ -82,11 +88,11 @@ export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeIn | |||
| fileReqParamKey: 'dataset_id', | |||
| tabItems: [ | |||
| { | |||
| key: '0', | |||
| key: TabItemKeys.Private, | |||
| label: '我的数据集', | |||
| }, | |||
| { | |||
| key: '1', | |||
| key: TabItemKeys.Public, | |||
| label: '公开数据集', | |||
| }, | |||
| ], | |||
| @@ -94,31 +100,33 @@ export const selectorTypeData: Record<ResourceSelectorTypeValues, SelectorTypeIn | |||
| }; | |||
| 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'> { | |||
| type: ResourceSelectorType; // 模型 | 数据集 | |||
| defaultExpandedKeys: React.Key[]; | |||
| defaultCheckedKeys: React.Key[]; | |||
| defaultActiveTab: string; | |||
| defaultActiveTab: TabItemKeys; | |||
| onOk?: (params: ResourceSelectorResponse | null) => void; | |||
| } | |||
| type ResourceGroup = { | |||
| id: number; | |||
| name: string; | |||
| id: number; // 数据集或者模型 id | |||
| name: string; // 数据集或者模型 id | |||
| }; | |||
| type ResourceFile = { | |||
| id: number; | |||
| file_name: string; | |||
| id: number; // 文件 id | |||
| file_name: string; // 文件 name | |||
| }; | |||
| type TreeRef = GetRef<typeof Tree<TreeDataNode>>; | |||
| // list 转成 treeData | |||
| const convertToTreeData = (list: ResourceGroup[]): TreeDataNode[] => { | |||
| return list.map((v) => ({ | |||
| @@ -129,6 +137,7 @@ const convertToTreeData = (list: ResourceGroup[]): TreeDataNode[] => { | |||
| })); | |||
| }; | |||
| // 更新树形结构的 children | |||
| const updateChildren = (parentId: number, children: TreeDataNode[]) => { | |||
| return (node: TreeDataNode) => { | |||
| 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({ | |||
| type, | |||
| defaultExpandedKeys = [], | |||
| defaultCheckedKeys = [], | |||
| defaultActiveTab = '0', | |||
| defaultActiveTab = TabItemKeys.Private, | |||
| onOk, | |||
| ...rest | |||
| }: ResourceSelectorModalProps) { | |||
| const [activeTab, setActiveTab] = useState(defaultActiveTab); | |||
| const [activeTab, setActiveTab] = useState<string>(defaultActiveTab); | |||
| const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]); | |||
| const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]); | |||
| const [loadedKeys, setLoadedKeys] = useState<React.Key[]>([]); | |||
| const [originTreeData, setOriginTreeData] = useState<TreeDataNode[]>([]); | |||
| const [treeData, setTreeData] = useState<TreeDataNode[]>([]); | |||
| const [files, setFiles] = useState<ResourceFile[]>([]); | |||
| const [versionPath, setVersionPath] = useState(''); | |||
| const [searchText, setSearchText] = useState(''); | |||
| @@ -174,19 +191,21 @@ function ResourceSelectorModal({ | |||
| getTreeData(); | |||
| }, [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 available_range = activeTab === TabItemKeys.Private ? '0' : '1'; | |||
| const params = { | |||
| page: 0, | |||
| size: 200, | |||
| available_range: activeTab, | |||
| available_range: available_range, | |||
| }; | |||
| const getListReq = selectorTypeData[type].getList; | |||
| const [res] = await to(getListReq(params)); | |||
| @@ -255,7 +274,7 @@ function ResourceSelectorModal({ | |||
| // 扩展 | |||
| const onExpand: TreeProps['onExpand'] = (expandedKeysValue) => { | |||
| const lastKeys = (expandedKeysValue as React.Key[]).slice(-1); | |||
| const lastKeys = expandedKeysValue.slice(-1); | |||
| setExpandedKeys(lastKeys); | |||
| }; | |||
| @@ -265,9 +284,7 @@ function ResourceSelectorModal({ | |||
| setCheckedKeys(lastKeys); | |||
| if (lastKeys.length) { | |||
| 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); | |||
| } else { | |||
| setFiles([]); | |||
| @@ -295,9 +312,7 @@ function ResourceSelectorModal({ | |||
| const restoreLastCheck = (parentId: number) => { | |||
| if (!fisrtLoadVersions && defaultCheckedKeys.length > 0) { | |||
| 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 是否一致 | |||
| if (id === parentId) { | |||
| setTimeout(() => { | |||
| @@ -319,16 +334,14 @@ function ResourceSelectorModal({ | |||
| const handleOk = () => { | |||
| if (checkedKeys.length > 0) { | |||
| 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 res = { | |||
| id, | |||
| name, | |||
| path: versionPath, | |||
| version, | |||
| activeTab, | |||
| activeTab: activeTab as TabItemKeys, | |||
| }; | |||
| onOk?.(res); | |||
| } else { | |||
| @@ -344,7 +357,7 @@ function ResourceSelectorModal({ | |||
| return ( | |||
| <KFModal {...rest} title={title} image={titleImg} onOk={handleOk} width={920} destroyOnClose> | |||
| <div className={styles}> | |||
| <div> | |||
| <Tabs | |||
| activeKey={activeTab} | |||
| items={tabItems} | |||
| @@ -360,6 +373,7 @@ function ResourceSelectorModal({ | |||
| variant="borderless" | |||
| value={searchText} | |||
| onChange={(e) => setSearchText(e.target.value)} | |||
| prefix={<Icon icon="local:magnifying-glass" style={{ height: '15px' }} />} | |||
| /> | |||
| <Tree | |||
| ref={treeRef} | |||
| @@ -1,3 +1,4 @@ | |||
| import { pick } from '@/utils/index'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { Icon } from '@umijs/max'; | |||
| import { Button, Drawer, Form, Input } from 'antd'; | |||
| @@ -9,17 +10,14 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||
| const [form] = Form.useForm(); | |||
| const [stagingItem, setStagingItem] = useState({}); | |||
| 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 [selectedDataset, setSelectedDataset] = useState(undefined); | |||
| const afterOpenChange = () => { | |||
| if (!open) { | |||
| console.log(111, open); | |||
| console.log(stagingItem, form.getFieldsValue()); | |||
| // 禁止校验 guard-for-in | |||
| /* eslint-disable */ | |||
| for (let i in form.getFieldsValue()) { | |||
| for (let j in stagingItem.in_parameters) { | |||
| if (i == j) { | |||
| @@ -38,6 +36,7 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||
| } | |||
| } | |||
| } | |||
| /* eslint-enable */ | |||
| // setStagingItem({...stagingItem,}) | |||
| console.log(stagingItem.control_strategy); | |||
| onParentChange({ | |||
| @@ -86,14 +85,12 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||
| }, | |||
| })); | |||
| // 选择数据集、模型 | |||
| const selectResource = (name, item) => { | |||
| // setFormItemName(name); | |||
| // setSelectorType(item.item_type === 'dataset' ? SelectorType.Dataset : SelectorType.Model); | |||
| // openModelSelector(); | |||
| const type = | |||
| item.item_type === 'dataset' ? ResourceSelectorType.Dataset : ResourceSelectorType.Model; | |||
| const resource = type === ResourceSelectorType.Dataset ? selectedDataset : selectedModel; | |||
| const { destroy } = openAntdModal( | |||
| const { close } = openAntdModal( | |||
| ResourceSelectorModal, | |||
| { | |||
| type, | |||
| @@ -102,7 +99,8 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||
| defaultActiveTab: resource?.activeTab, | |||
| onOk: (res) => { | |||
| if (res) { | |||
| const value = JSON.stringify(res); | |||
| const jsonObj = pick(res, ['id', 'version', 'path']); | |||
| const value = JSON.stringify(jsonObj); | |||
| form.setFieldValue(name, value); | |||
| if (type === ResourceSelectorType.Dataset) { | |||
| setSelectedDataset(res); | |||
| @@ -117,35 +115,32 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||
| } | |||
| form.setFieldValue(name, ''); | |||
| } | |||
| destroy(); | |||
| close(); | |||
| }, | |||
| }, | |||
| 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 type = item.item_type; | |||
| 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') { | |||
| return <Icon icon="local:model-select-button" className="local-svg" />; | |||
| return <Icon icon="local:model-select-button" className="umi-local-svg" />; | |||
| } 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 ( | |||
| <> | |||
| <Drawer | |||
| @@ -254,11 +249,9 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||
| <Form.Item label="环境变量" name="env_variables"> | |||
| <TextArea /> | |||
| </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 /> | |||
| </Form.Item> | |||
| )) | |||
| @@ -271,32 +264,33 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||
| /> | |||
| 输入参数 | |||
| </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 | |||
| 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']}> | |||
| <Form.Item | |||
| name={item} | |||
| noStyle | |||
| rules={[{ required: stagingItem.in_parameters[item].require ? true : false }]} | |||
| rules={[{ required: inParameters[item].require ? true : false }]} | |||
| > | |||
| <Input /> | |||
| </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> | |||
| </Form.Item> | |||
| )) | |||
| @@ -309,14 +303,12 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||
| /> | |||
| 输出参数 | |||
| </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 | |||
| 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} | |||
| > | |||
| <Input /> | |||
| @@ -324,18 +316,6 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||
| )) | |||
| : ''} | |||
| </Form> | |||
| {/* {modelSelectorOpen && ( | |||
| <ResourceSelectorModal | |||
| open={modelSelectorOpen} | |||
| onCancel={closeModelSelector} | |||
| defaultExpandedKeys={selectedModel ? [selectedModel.id] : []} | |||
| defaultCheckedKeys={ | |||
| selectedModel ? [`${selectedModel.id}-${selectedModel.version}`] : [] | |||
| } | |||
| type={selectorType} | |||
| onOk={handleModelSelect} | |||
| /> | |||
| )} */} | |||
| </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 }); | |||
| return { | |||
| destroy: close, | |||
| close, | |||
| }; | |||
| }; | |||