| @@ -19,7 +19,7 @@ const Settings: ProLayoutProps & { | |||||
| title: '复杂智能软件', | title: '复杂智能软件', | ||||
| pwa: true, | pwa: true, | ||||
| logo: '/assets/images/left-top-logo.png', | logo: '/assets/images/left-top-logo.png', | ||||
| iconfontUrl: '', | |||||
| iconfontUrl: '//at.alicdn.com/t/c/font_4509211_dfghcwme8ki.js', | |||||
| token: { | token: { | ||||
| // 参见ts声明,demo 见文档,通过token 修改样式 | // 参见ts声明,demo 见文档,通过token 修改样式 | ||||
| //https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F | //https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F | ||||
| @@ -16,8 +16,8 @@ export default { | |||||
| '/api/': { | '/api/': { | ||||
| // 要代理的地址 | // 要代理的地址 | ||||
| // target: 'http://172.20.32.181:31205', | // target: 'http://172.20.32.181:31205', | ||||
| target: 'http://172.20.32.98:8082', | |||||
| // target: 'http://172.20.32.150:8082', | |||||
| // target: 'http://172.20.32.98:8082', | |||||
| target: 'http://172.20.32.150:8082', | |||||
| // 配置了这个可以从 http 代理到 https | // 配置了这个可以从 http 代理到 https | ||||
| // 依赖 origin 的功能可能需要这个,比如 cookie | // 依赖 origin 的功能可能需要这个,比如 cookie | ||||
| changeOrigin: true, | changeOrigin: true, | ||||
| @@ -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', | ||||
| @@ -0,0 +1 @@ | |||||
| <svg xmlns="http://www.w3.org/2000/svg" width="13.545" height="15.046" viewBox="0 0 13.545 15.046"><defs><style>.a{fill:#1664ff;}</style></defs><path class="a" d="M143.094,101.214h5.614v3.363a1.006,1.006,0,0,0,1.139,1.139h3.363v7.115a.559.559,0,0,1-.075.248,1.247,1.247,0,0,1-.195.267.832.832,0,0,1-.542.3h-9.3a.832.832,0,0,1-.542-.3,1.247,1.247,0,0,1-.195-.267.559.559,0,0,1-.075-.248V102.025h0a.559.559,0,0,1,.075-.248,1.247,1.247,0,0,1,.195-.267.832.832,0,0,1,.542-.3Zm3.546,6.358.344-.344a.577.577,0,1,0-.816-.816l-1.315,1.315h0l0,0a.577.577,0,0,0,.4,1h3.037a.818.818,0,1,1,0,1.635H146.91a.577.577,0,0,0,0,1.154h1.374a1.971,1.971,0,1,0,0-3.943Zm3.327-7.4a.889.889,0,0,0-.634-.265h-6.239a2.116,2.116,0,0,0-2.12,2.12v10.805a2.116,2.116,0,0,0,2.12,2.12h9.3a2.166,2.166,0,0,0,2.12-2.12v-7.673a.889.889,0,0,0-.256-.624Zm.05,4.237v-2.317l2.317,2.317h-2.317Z" transform="translate(-140.974 -99.905)"/></svg> | |||||
| @@ -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; | |||||
| @@ -80,8 +80,8 @@ a { | |||||
| .ant-pro-layout .ant-pro-layout-container { | .ant-pro-layout .ant-pro-layout-container { | ||||
| height: 98vh; | height: 98vh; | ||||
| } | } | ||||
| .ant-modal-confirm .ant-modal-confirm-paragraph { | |||||
| margin: 40px 0 auto; | |||||
| .ant-modal-confirm .ant-modal-confirm-paragraph{ | |||||
| margin: 54px 0 auto; | |||||
| text-align: center; | text-align: center; | ||||
| } | } | ||||
| .ant-modal-confirm-confirm .ant-modal-confirm-body > .anticon { | .ant-modal-confirm-confirm .ant-modal-confirm-body > .anticon { | ||||
| @@ -91,25 +91,37 @@ a { | |||||
| margin-top: 30px; | margin-top: 30px; | ||||
| text-align: center; | text-align: center; | ||||
| } | } | ||||
| .ant-modal-confirm-btns .ant-btn-default { | |||||
| width: 91px; | |||||
| height: 42px; | |||||
| margin-right: 10px; | |||||
| color: #1d1d20; | |||||
| font-size: 16px; | |||||
| background: rgba(22, 100, 255, 0.06); | |||||
| border-radius: 10px; | |||||
| .ant-modal-confirm-btns .ant-btn-default{ | |||||
| width:110px; | |||||
| height:40px; | |||||
| background:rgba(22, 100, 255, 0.06); | |||||
| border-radius:10px; | |||||
| color:#1d1d20; | |||||
| font-size:18px; | |||||
| margin-right: 10px; | |||||
| border-color: transparent; | |||||
| } | } | ||||
| .ant-modal-confirm-btns .ant-btn-default:hover { | .ant-modal-confirm-btns .ant-btn-default:hover { | ||||
| background: rgba(22, 100, 255, 0.06); | background: rgba(22, 100, 255, 0.06); | ||||
| border-color: transparent; | border-color: transparent; | ||||
| } | } | ||||
| .ant-modal-confirm-btns .ant-btn-primary { | |||||
| width: 91px; | |||||
| height: 42px; | |||||
| font-size: 16px; | |||||
| background: #1664ff; | |||||
| border-radius: 10px; | |||||
| .ant-modal-confirm-btns .ant-btn-primary{ | |||||
| width:110px; | |||||
| height:40px; | |||||
| background:#1664ff; | |||||
| border-radius:10px; | |||||
| font-size: 18px; | |||||
| } | |||||
| .ant-modal .ant-input-affix-wrapper{ | |||||
| height: 46px; | |||||
| padding: 1px 11px; | |||||
| } | |||||
| .ant-modal .ant-select-single{ | |||||
| height: 46px; | |||||
| } | |||||
| .ant-modal .ant-select-single .ant-select-selector .ant-select-selection-placeholder{ | |||||
| line-height: 46px; | |||||
| } | } | ||||
| .ant-modal .ant-modal-close-x { | .ant-modal .ant-modal-close-x { | ||||
| width: 26px; | width: 26px; | ||||
| @@ -125,13 +137,17 @@ a { | |||||
| .ant-modal .ant-modal-content { | .ant-modal .ant-modal-content { | ||||
| padding: 0; | padding: 0; | ||||
| } | } | ||||
| .ant-modal-confirm-body-wrapper { | |||||
| height: 303px; | |||||
| background-image: url(/assets/images/modal-back.png); | |||||
| background-repeat: no-repeat; | |||||
| background-position: top center; | |||||
| background-size: 100%; | |||||
| border-radius: 21px; | |||||
| .ant-modal-confirm-body-wrapper{ | |||||
| height:303px; | |||||
| border-radius:21px; | |||||
| background-image: url(/assets/images/modal-back.png); | |||||
| background-repeat:no-repeat; | |||||
| background-size:100%; | |||||
| background-position: top center; | |||||
| border-radius: 0; | |||||
| } | |||||
| .ant-modal .ant-modal-content { | |||||
| border-radius: 20px; | |||||
| } | } | ||||
| .ant-modal .ant-modal-close:hover { | .ant-modal .ant-modal-close:hover { | ||||
| background-color: transparent; | background-color: transparent; | ||||
| @@ -194,6 +210,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: '整型', | ||||
| @@ -168,6 +168,32 @@ function ExperimentText() { | |||||
| }, [message]); | }, [message]); | ||||
| const initGraph = () => { | const initGraph = () => { | ||||
| const fittingString = (str, maxWidth, fontSize) => { | |||||
| const ellipsis = '...'; | |||||
| const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0]; | |||||
| let currentWidth = 0; | |||||
| let res = str; | |||||
| const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters | |||||
| str.split('').forEach((letter, i) => { | |||||
| if (currentWidth > maxWidth - ellipsisLength) return; | |||||
| if (pattern.test(letter)) { | |||||
| // Chinese charactors | |||||
| currentWidth += fontSize; | |||||
| } else { | |||||
| // get the width of single letter according to the fontSize | |||||
| currentWidth += G6.Util.getLetterWidth(letter, fontSize); | |||||
| } | |||||
| if (currentWidth > maxWidth - ellipsisLength) { | |||||
| res = `${str.substr(0, i)}${ellipsis}`; | |||||
| } | |||||
| }); | |||||
| return res; | |||||
| }; | |||||
| // 获取文本的长度 | |||||
| const getTextSize = (str, maxWidth, fontSize) => { | |||||
| let width = G6.Util.getTextSize(str, fontSize)[0]; | |||||
| return width > maxWidth ? maxWidth : width; | |||||
| }; | |||||
| G6.registerNode( | G6.registerNode( | ||||
| 'rect-node', | 'rect-node', | ||||
| { | { | ||||
| @@ -194,19 +220,21 @@ function ExperimentText() { | |||||
| }, | }, | ||||
| draggable: true, | draggable: true, | ||||
| }); | }); | ||||
| // if (cfg.label) { | |||||
| // group.addShape('text', { | |||||
| // attrs: { | |||||
| // x: 0, | |||||
| // y: cfg.height / 2 - 5, | |||||
| // textAlign: 'center', | |||||
| // textBaseline: 'middle', | |||||
| // text: cfg.label, | |||||
| // fill: '#fff', | |||||
| // }, | |||||
| // draggable: true, | |||||
| // }); | |||||
| // } | |||||
| if (cfg.label) { | |||||
| group.addShape('text', { | |||||
| attrs: { | |||||
| text: fittingString(cfg.label, 70, 10), | |||||
| x: -20, | |||||
| y: 0, | |||||
| fontSize: 10, | |||||
| textAlign: 'left', | |||||
| textBaseline: 'middle', | |||||
| fill: '#000', | |||||
| }, | |||||
| name: 'text-shape', | |||||
| draggable: true, | |||||
| }); | |||||
| } | |||||
| const bbox = group.getBBox(); | const bbox = group.getBBox(); | ||||
| const anchorPoints = this.getAnchorPoints(cfg); | const anchorPoints = this.getAnchorPoints(cfg); | ||||
| // console.log(anchorPoints); | // console.log(anchorPoints); | ||||
| @@ -357,8 +385,8 @@ function ExperimentText() { | |||||
| }, | }, | ||||
| }, | }, | ||||
| // linkCenter: true, | // linkCenter: true, | ||||
| fitView: false, | |||||
| fitViewPadding: [60, 60, 60, 80], | |||||
| fitView: true, | |||||
| fitViewPadding: [320, 320, 220, 320], | |||||
| }); | }); | ||||
| graph.on('dblclick', handlerClick); | graph.on('dblclick', handlerClick); | ||||
| @@ -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> | ||||
| ))} | ))} | ||||
| @@ -390,6 +390,7 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| <Drawer | <Drawer | ||||
| title="任务执行详情" | title="任务执行详情" | ||||
| placement="right" | placement="right" | ||||
| rootStyle={{ marginTop: '45px' }} | |||||
| closeIcon={false} | closeIcon={false} | ||||
| onClose={onClose} | onClose={onClose} | ||||
| afterOpenChange={afterOpenChange} | afterOpenChange={afterOpenChange} | ||||
| @@ -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,5 +1,6 @@ | |||||
| import { ReactComponent as ParameterIcon } from '@/assets/svg/parameter.svg'; | import { ReactComponent as ParameterIcon } from '@/assets/svg/parameter.svg'; | ||||
| import { useVisible } from '@/hooks'; | |||||
| import { ReactComponent as SaveAndReturn } from '@/assets/svg/save--return.svg'; | |||||
| import { useAntdModal } from '@/hooks'; | |||||
| import { getWorkflowById, saveWorkflow } from '@/services/pipeline/index.js'; | import { getWorkflowById, saveWorkflow } from '@/services/pipeline/index.js'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { SaveOutlined } from '@ant-design/icons'; | import { SaveOutlined } from '@ant-design/icons'; | ||||
| @@ -607,8 +608,8 @@ const EditPipeline = () => { | |||||
| }, | }, | ||||
| }, | }, | ||||
| // linkCenter: true, | // linkCenter: true, | ||||
| fitView: false, | |||||
| fitViewPadding: [60, 60, 60, 80], | |||||
| fitView: true, | |||||
| fitViewPadding: [320, 320, 220, 320], | |||||
| }); | }); | ||||
| graph.on('dblclick', (e) => { | graph.on('dblclick', (e) => { | ||||
| console.log(e.item); | console.log(e.item); | ||||
| @@ -723,7 +724,13 @@ const EditPipeline = () => { | |||||
| </Button> | </Button> | ||||
| <Button | <Button | ||||
| type="primary" | type="primary" | ||||
| icon={<SaveOutlined />} | |||||
| style={{ | |||||
| border: '1px solid', | |||||
| borderColor: '#1664ff', | |||||
| background: '#fff', | |||||
| color: '#1664ff', | |||||
| }} | |||||
| icon={<SaveAndReturn />} | |||||
| onClick={() => { | onClick={() => { | ||||
| savePipeline(true); | savePipeline(true); | ||||
| }} | }} | ||||
| @@ -24,7 +24,7 @@ const modelMenus = ({ onParDragEnd }) => { | |||||
| useEffect(() => { | useEffect(() => { | ||||
| getComponentAll().then((ret) => { | getComponentAll().then((ret) => { | ||||
| console.log(ret); | console.log(ret); | ||||
| if (ret.code == 200) { | |||||
| if (ret.code === 200) { | |||||
| setModelMenusList(ret.data); | setModelMenusList(ret.data); | ||||
| } | } | ||||
| }); | }); | ||||
| @@ -43,32 +43,38 @@ const modelMenus = ({ onParDragEnd }) => { | |||||
| return ( | return ( | ||||
| <div style={{ width: '250px', height: '99%' }} className={Styles.collapse}> | <div style={{ width: '250px', height: '99%' }} className={Styles.collapse}> | ||||
| <div className={Styles.modelMenusTitle}>组件库</div> | <div className={Styles.modelMenusTitle}>组件库</div> | ||||
| <Collapse collapsible="header" defaultActiveKey={['1']} expandIconPosition="end"> | |||||
| {modelMenusList && modelMenusList.length > 0 | |||||
| ? modelMenusList.map((item) => ( | |||||
| <Panel header={<div>{item.name}</div>} key={item.key}> | |||||
| {item.value && item.value.length > 0 | |||||
| ? item.value.map((ele) => ( | |||||
| <div | |||||
| draggable="true" | |||||
| onDragEnd={(e) => { | |||||
| dragEnd(e, ele); | |||||
| }} | |||||
| className={Styles.collapseItem} | |||||
| > | |||||
| <img | |||||
| style={{ height: '16px', marginRight: '15px' }} | |||||
| src={`/assets/images/${ele.icon_path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| {ele.component_label} | |||||
| </div> | |||||
| )) | |||||
| : ''} | |||||
| </Panel> | |||||
| )) | |||||
| : ''} | |||||
| </Collapse> | |||||
| {modelMenusList && modelMenusList.length > 0 ? ( | |||||
| <Collapse | |||||
| collapsible="header" | |||||
| defaultActiveKey={modelMenusList.map((item) => item.key + '')} | |||||
| expandIconPosition="end" | |||||
| > | |||||
| {modelMenusList && modelMenusList.length > 0 | |||||
| ? modelMenusList.map((item) => ( | |||||
| <Panel header={<div>{item.name}</div>} key={item.key}> | |||||
| {item.value && item.value.length > 0 | |||||
| ? item.value.map((ele) => ( | |||||
| <div | |||||
| draggable="true" | |||||
| onDragEnd={(e) => { | |||||
| dragEnd(e, ele); | |||||
| }} | |||||
| className={Styles.collapseItem} | |||||
| > | |||||
| <img | |||||
| style={{ height: '16px', marginRight: '15px' }} | |||||
| src={`/assets/images/${ele.icon_path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| {ele.component_label} | |||||
| </div> | |||||
| )) | |||||
| : ''} | |||||
| </Panel> | |||||
| )) | |||||
| : ''} | |||||
| </Collapse> | |||||
| ) : null} | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -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,59 +115,53 @@ 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 | ||||
| title="编辑任务" | title="编辑任务" | ||||
| placement="right" | placement="right" | ||||
| rootStyle={{ marginTop: '45px' }} | |||||
| getContainer={false} | getContainer={false} | ||||
| closeIcon={false} | closeIcon={false} | ||||
| onClose={onClose} | onClose={onClose} | ||||
| afterOpenChange={afterOpenChange} | afterOpenChange={afterOpenChange} | ||||
| open={open} | open={open} | ||||
| width={540} | |||||
| > | > | ||||
| <Form | <Form | ||||
| name="form" | name="form" | ||||
| form={form} | form={form} | ||||
| layout="vertical" | |||||
| labelCol={{ | |||||
| span: 24, | |||||
| }} | |||||
| wrapperCol={{ | |||||
| span: 24, | |||||
| }} | |||||
| style={{ | |||||
| maxWidth: 600, | |||||
| }} | |||||
| // layout="vertical" | |||||
| labelCol={{ span: 10 }} | |||||
| wrapperCol={{ span: 20 }} | |||||
| // initialValues={{ global_param: globalParam }} | |||||
| labelAlign="left" | |||||
| initialValues={{ | initialValues={{ | ||||
| remember: true, | remember: true, | ||||
| }} | }} | ||||
| @@ -254,11 +246,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 +261,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 +300,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 +313,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, | |||||
| // }); | |||||
| // } | |||||
| @@ -2,13 +2,15 @@ import { createIcon } from '@/utils/IconUtil'; | |||||
| import { MenuDataItem } from '@ant-design/pro-components'; | import { MenuDataItem } from '@ant-design/pro-components'; | ||||
| import { request } from '@umijs/max'; | import { request } from '@umijs/max'; | ||||
| import React, { lazy } from 'react'; | import React, { lazy } from 'react'; | ||||
| import { createFromIconfontCN } from '@ant-design/icons'; | |||||
| let remoteMenu: any = null; | let remoteMenu: any = null; | ||||
| export function getRemoteMenu() { | export function getRemoteMenu() { | ||||
| return remoteMenu; | return remoteMenu; | ||||
| } | } | ||||
| const IconFont = createFromIconfontCN({ | |||||
| scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js', // 在 iconfont.cn 上生成 | |||||
| }); | |||||
| export function setRemoteMenu(data: any) { | export function setRemoteMenu(data: any) { | ||||
| remoteMenu = data; | remoteMenu = data; | ||||
| } | } | ||||
| @@ -100,7 +102,8 @@ export function convertCompatRouters(childrens: API.RoutersMenuItem[]): any[] { | |||||
| return childrens.map((item: API.RoutersMenuItem) => { | return childrens.map((item: API.RoutersMenuItem) => { | ||||
| return { | return { | ||||
| path: item.path, | path: item.path, | ||||
| icon: createIcon(item.meta.icon), | |||||
| // icon:'icon-a-057_fenlei', | |||||
| icon: 'icon-'+item.meta.icon, | |||||
| // icon: item.meta.icon, | // icon: item.meta.icon, | ||||
| name: item.meta.title, | name: item.meta.title, | ||||
| routes: item.children ? convertCompatRouters(item.children) : undefined, | routes: item.children ? convertCompatRouters(item.children) : undefined, | ||||
| @@ -14,6 +14,7 @@ export function createIcon(icon: string | any): React.ReactNode | string { | |||||
| } | } | ||||
| const ele = allIcons[icon]; | const ele = allIcons[icon]; | ||||
| if (ele) { | if (ele) { | ||||
| return React.createElement(allIcons[icon]); | return React.createElement(allIcons[icon]); | ||||
| } | } | ||||
| return ''; | return ''; | ||||
| @@ -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, | |||||
| }; | }; | ||||
| }; | }; | ||||