| @@ -19,7 +19,7 @@ const Settings: ProLayoutProps & { | |||
| title: '复杂智能软件', | |||
| pwa: true, | |||
| logo: '/assets/images/left-top-logo.png', | |||
| iconfontUrl: '', | |||
| iconfontUrl: '//at.alicdn.com/t/c/font_4509211_dfghcwme8ki.js', | |||
| 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 | |||
| @@ -16,8 +16,8 @@ export default { | |||
| '/api/': { | |||
| // 要代理的地址 | |||
| // 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 | |||
| // 依赖 origin 的功能可能需要这个,比如 cookie | |||
| changeOrigin: true, | |||
| @@ -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', | |||
| @@ -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 { 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; | |||
| @@ -80,8 +80,8 @@ a { | |||
| .ant-pro-layout .ant-pro-layout-container { | |||
| 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; | |||
| } | |||
| .ant-modal-confirm-confirm .ant-modal-confirm-body > .anticon { | |||
| @@ -91,25 +91,37 @@ a { | |||
| margin-top: 30px; | |||
| 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 { | |||
| background: rgba(22, 100, 255, 0.06); | |||
| 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 { | |||
| width: 26px; | |||
| @@ -125,13 +137,17 @@ a { | |||
| .ant-modal .ant-modal-content { | |||
| 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 { | |||
| background-color: transparent; | |||
| @@ -194,6 +210,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: '整型', | |||
| @@ -168,6 +168,32 @@ function ExperimentText() { | |||
| }, [message]); | |||
| 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( | |||
| 'rect-node', | |||
| { | |||
| @@ -194,19 +220,21 @@ function ExperimentText() { | |||
| }, | |||
| 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 anchorPoints = this.getAnchorPoints(cfg); | |||
| // console.log(anchorPoints); | |||
| @@ -357,8 +385,8 @@ function ExperimentText() { | |||
| }, | |||
| }, | |||
| // linkCenter: true, | |||
| fitView: false, | |||
| fitViewPadding: [60, 60, 60, 80], | |||
| fitView: true, | |||
| fitViewPadding: [320, 320, 220, 320], | |||
| }); | |||
| graph.on('dblclick', handlerClick); | |||
| @@ -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> | |||
| ))} | |||
| @@ -390,6 +390,7 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||
| <Drawer | |||
| title="任务执行详情" | |||
| placement="right" | |||
| rootStyle={{ marginTop: '45px' }} | |||
| closeIcon={false} | |||
| onClose={onClose} | |||
| 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'; | |||
| .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,5 +1,6 @@ | |||
| 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 { to } from '@/utils/promise'; | |||
| import { SaveOutlined } from '@ant-design/icons'; | |||
| @@ -607,8 +608,8 @@ const EditPipeline = () => { | |||
| }, | |||
| }, | |||
| // linkCenter: true, | |||
| fitView: false, | |||
| fitViewPadding: [60, 60, 60, 80], | |||
| fitView: true, | |||
| fitViewPadding: [320, 320, 220, 320], | |||
| }); | |||
| graph.on('dblclick', (e) => { | |||
| console.log(e.item); | |||
| @@ -723,7 +724,13 @@ const EditPipeline = () => { | |||
| </Button> | |||
| <Button | |||
| type="primary" | |||
| icon={<SaveOutlined />} | |||
| style={{ | |||
| border: '1px solid', | |||
| borderColor: '#1664ff', | |||
| background: '#fff', | |||
| color: '#1664ff', | |||
| }} | |||
| icon={<SaveAndReturn />} | |||
| onClick={() => { | |||
| savePipeline(true); | |||
| }} | |||
| @@ -24,7 +24,7 @@ const modelMenus = ({ onParDragEnd }) => { | |||
| useEffect(() => { | |||
| getComponentAll().then((ret) => { | |||
| console.log(ret); | |||
| if (ret.code == 200) { | |||
| if (ret.code === 200) { | |||
| setModelMenusList(ret.data); | |||
| } | |||
| }); | |||
| @@ -43,32 +43,38 @@ const modelMenus = ({ onParDragEnd }) => { | |||
| return ( | |||
| <div style={{ width: '250px', height: '99%' }} className={Styles.collapse}> | |||
| <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> | |||
| ); | |||
| }; | |||
| @@ -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,59 +115,53 @@ 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 | |||
| title="编辑任务" | |||
| placement="right" | |||
| rootStyle={{ marginTop: '45px' }} | |||
| getContainer={false} | |||
| closeIcon={false} | |||
| onClose={onClose} | |||
| afterOpenChange={afterOpenChange} | |||
| open={open} | |||
| width={540} | |||
| > | |||
| <Form | |||
| name="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={{ | |||
| remember: true, | |||
| }} | |||
| @@ -254,11 +246,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 +261,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 +300,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 +313,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, | |||
| // }); | |||
| // } | |||
| @@ -2,13 +2,15 @@ import { createIcon } from '@/utils/IconUtil'; | |||
| import { MenuDataItem } from '@ant-design/pro-components'; | |||
| import { request } from '@umijs/max'; | |||
| import React, { lazy } from 'react'; | |||
| import { createFromIconfontCN } from '@ant-design/icons'; | |||
| let remoteMenu: any = null; | |||
| export function getRemoteMenu() { | |||
| return remoteMenu; | |||
| } | |||
| const IconFont = createFromIconfontCN({ | |||
| scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js', // 在 iconfont.cn 上生成 | |||
| }); | |||
| export function setRemoteMenu(data: any) { | |||
| remoteMenu = data; | |||
| } | |||
| @@ -100,7 +102,8 @@ export function convertCompatRouters(childrens: API.RoutersMenuItem[]): any[] { | |||
| return childrens.map((item: API.RoutersMenuItem) => { | |||
| return { | |||
| path: item.path, | |||
| icon: createIcon(item.meta.icon), | |||
| // icon:'icon-a-057_fenlei', | |||
| icon: 'icon-'+item.meta.icon, | |||
| // icon: item.meta.icon, | |||
| name: item.meta.title, | |||
| routes: item.children ? convertCompatRouters(item.children) : undefined, | |||
| @@ -14,6 +14,7 @@ export function createIcon(icon: string | any): React.ReactNode | string { | |||
| } | |||
| const ele = allIcons[icon]; | |||
| if (ele) { | |||
| return React.createElement(allIcons[icon]); | |||
| } | |||
| 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 }); | |||
| return { | |||
| destroy: close, | |||
| close, | |||
| }; | |||
| }; | |||