| @@ -78,7 +78,7 @@ export default defineConfig({ | |||
| */ | |||
| title: '智能软件开发平台', | |||
| layout: { | |||
| locale: true, | |||
| locale: false, | |||
| ...defaultSettings, | |||
| }, | |||
| // keepalive: [/./], | |||
| @@ -97,10 +97,8 @@ export default defineConfig({ | |||
| * @doc https://umijs.org/docs/max/i18n | |||
| */ | |||
| locale: { | |||
| // default zh-CN | |||
| default: 'zh-CN', | |||
| antd: true, | |||
| // default true, when it is true, will use `navigator.language` overwrite default | |||
| baseNavigator: true, | |||
| }, | |||
| /** | |||
| @@ -13,7 +13,7 @@ | |||
| export default [ | |||
| { | |||
| path: '/', | |||
| redirect: '/account/center', | |||
| redirect: '/workspace', | |||
| }, | |||
| { | |||
| path: '*', | |||
| @@ -88,13 +88,6 @@ export default [ | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| name: 'experiment', | |||
| path: '/experiment', | |||
| routes: [ | |||
| ], | |||
| }, | |||
| { | |||
| name: 'developmentEnvironment', | |||
| path: '/developmentEnvironment', | |||
| @@ -169,7 +162,7 @@ export default [ | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| name: 'workspace', | |||
| path: '/workspace', | |||
| @@ -177,7 +170,8 @@ export default [ | |||
| { | |||
| name: '工作空间', | |||
| path: '', | |||
| component: './missingPage.jsx', | |||
| key: 'workspace', | |||
| component: './Workspace/index', | |||
| }, | |||
| ], | |||
| }, | |||
| @@ -188,11 +182,11 @@ export default [ | |||
| { | |||
| name: '模型部署', | |||
| path: '', | |||
| key: 'modelDseployment', | |||
| component: './missingPage.jsx', | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| name: 'appsDeployment', | |||
| path: '/appsDeployment', | |||
| @@ -200,6 +194,7 @@ export default [ | |||
| { | |||
| name: '应用开发', | |||
| path: '', | |||
| key: 'appsDeployment', | |||
| component: './missingPage.jsx', | |||
| }, | |||
| ], | |||
| @@ -211,6 +206,7 @@ export default [ | |||
| { | |||
| name: '监控运维', | |||
| path: '', | |||
| key: 'see', | |||
| component: './missingPage.jsx', | |||
| }, | |||
| ], | |||
| @@ -242,4 +238,16 @@ export default [ | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| name: 'docs', | |||
| path: '/docs', | |||
| routes: [ | |||
| { | |||
| name: '使用指南', | |||
| path: '', | |||
| key: 'docs', | |||
| component: './Docs/index', | |||
| }, | |||
| ], | |||
| }, | |||
| ]; | |||
| @@ -60,6 +60,7 @@ | |||
| "@umijs/route-utils": "^4.0.1", | |||
| "antd": "^5.4.4", | |||
| "classnames": "^2.3.2", | |||
| "echarts": "^5.5.0", | |||
| "fabric": "^5.3.0", | |||
| "highlight.js": "^11.7.0", | |||
| "lodash": "^4.17.21", | |||
| @@ -70,6 +71,7 @@ | |||
| "rc-menu": "^9.8.4", | |||
| "rc-util": "^5.30.0", | |||
| "react": "^18.2.0", | |||
| "react-activation": "^0.12.4", | |||
| "react-cropper": "^2.3.3", | |||
| "react-dev-inspector": "^1.8.1", | |||
| "react-dom": "^18.2.0", | |||
| @@ -35,13 +35,12 @@ export async function getInitialState(): Promise<{ | |||
| const response = await getUserInfo({ | |||
| skipErrorHandler: true, | |||
| }); | |||
| response.user.avatar = | |||
| response.user.avatar || | |||
| 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png'; | |||
| return { | |||
| ...response.user, | |||
| avatar: response.user.avatar || require('@/assets/img/avatar-default.png'), | |||
| permissions: response.permissions, | |||
| roles: response.roles, | |||
| roleNames: response.user.roles, | |||
| } as API.CurrentUser; | |||
| } catch (error) { | |||
| console.log(error); | |||
| @@ -128,7 +127,7 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => { | |||
| // </Link>, | |||
| // ] | |||
| // : [], | |||
| menuHeaderRender: undefined, | |||
| menuHeaderRender: false, | |||
| // 自定义 403 页面 | |||
| // unAccessible: <div>unAccessible</div>, | |||
| // 增加一个 loading 的状态 | |||
| @@ -138,10 +137,40 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => { | |||
| }, | |||
| menuProps: { | |||
| onClick: () => { | |||
| // 点击菜单项,删除所有的页面 state 缓存 | |||
| removeAllPageCacheState(); | |||
| }, | |||
| // onSelect: (e) => { | |||
| // console.log(e); | |||
| // }, | |||
| }, | |||
| ...initialState?.settings, | |||
| token: { | |||
| sider: { | |||
| colorTextMenu: themes['textColor'], | |||
| colorTextMenuSelected: themes['primaryColor'], | |||
| colorTextMenuActive: themes['primaryColor'], | |||
| colorBgMenuItemSelected: 'rgba(197, 232, 255, 0.8)', | |||
| colorMenuBackground: themes['siderBGColor'], | |||
| }, | |||
| }, | |||
| // menuItemRender: (itemProps, defaultDom, props) => { | |||
| // console.log('menuItemProps', itemProps); | |||
| // console.log('defaultDom', defaultDom); | |||
| // console.log('props', props); | |||
| // const { pathname } = window.location; | |||
| // const isSelected = pathname === itemProps.path; | |||
| // // 根据菜单项的状态动态显示不同的 icon | |||
| // const icon = isSelected ? 'icon-developmentEnvironment-icon' : 'icon-kaifahuanjing'; | |||
| // return ( | |||
| // <Link to={itemProps.path || ''} target={itemProps.target}> | |||
| // <KFIcon type={icon} /> | |||
| // {itemProps.name} | |||
| // </Link> | |||
| // ); | |||
| // }, | |||
| }; | |||
| }; | |||
| @@ -1,3 +1,9 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-28 14:18:11 | |||
| * @Description: 自定义 Table 单元格,没有数据时展示 -- | |||
| */ | |||
| function CommonTableCell(text?: string | null) { | |||
| return <span>{text ?? '--'}</span>; | |||
| } | |||
| @@ -1,3 +1,9 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-28 14:18:11 | |||
| * @Description: 自定义 Table 日期类单元格 | |||
| */ | |||
| import dayjs from 'dayjs'; | |||
| function DateTableCell(text?: string | null) { | |||
| @@ -5,7 +11,7 @@ function DateTableCell(text?: string | null) { | |||
| return <span>--</span>; | |||
| } | |||
| if (!dayjs(text).isValid()) { | |||
| return <span>日期无效</span>; | |||
| return <span>无效的日期</span>; | |||
| } | |||
| return <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>; | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-17 16:59:42 | |||
| * @Description: 自定义Radio | |||
| * @Description: 自定义 Radio | |||
| */ | |||
| import classNames from 'classnames'; | |||
| @@ -1,3 +1,9 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-28 14:18:11 | |||
| * @Description: 自定义 Modal Title | |||
| */ | |||
| import classNames from 'classnames'; | |||
| import React from 'react'; | |||
| import styles from './index.less'; | |||
| @@ -1,6 +1,7 @@ | |||
| html, | |||
| body, | |||
| #root { | |||
| min-width: 1440px; | |||
| height: 100%; | |||
| margin: 0; | |||
| padding: 0; | |||
| @@ -20,9 +21,6 @@ body, | |||
| .ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed { | |||
| left: unset; | |||
| } | |||
| .ant-layout-sider-children { | |||
| margin-top: 60px !important; | |||
| } | |||
| canvas { | |||
| display: block; | |||
| } | |||
| @@ -33,58 +31,29 @@ body { | |||
| -moz-osx-font-smoothing: grayscale; | |||
| } | |||
| .ant-pro-layout .ant-pro-layout-content { | |||
| padding: 10px; | |||
| padding: 0 10px 10px; | |||
| background-color: transparent; | |||
| } | |||
| .ant-pro-layout .ant-pro-layout-bg-list { | |||
| background: #f9fafb; | |||
| background: @background-color; | |||
| } | |||
| .ant-menu-light .ant-menu-item-selected { | |||
| background: rgba(197, 232, 255, 0.8) !important; | |||
| } | |||
| .ant-menu-light .ant-menu-item-selected .ant-pro-base-menu-inline-item-text { | |||
| // color: #1664ff; | |||
| } | |||
| .ant-pro-layout .ant-pro-sider .ant-layout-sider-children { | |||
| background: #f2f5f7; | |||
| } | |||
| .ant-pro-base-menu-inline-item-title .ant-pro-base-menu-inline-item-text { | |||
| // color: #1d1d20; | |||
| font-size: 16px; | |||
| } | |||
| // .ant-menu-light .ant-menu-item-selected{ | |||
| // color:#1664ff; | |||
| // } | |||
| .ant-pro-layout .ant-pro-sider-menu { | |||
| padding-top: 40px; | |||
| } | |||
| .ant-table-wrapper .ant-table-container table > thead > tr:first-child > *:first-child, | |||
| .ant-table-wrapper .ant-table-tbody>tr>td:first-child { | |||
| padding: 0 30px; | |||
| } | |||
| .ant-pro-global-header-logo-mix { | |||
| width: 257px; | |||
| height: 75px; | |||
| margin-left: -16px; | |||
| padding-left: 28px; | |||
| background: #f2f5f7; | |||
| border-bottom: 1px solid rgba(233, 237, 240, 1); | |||
| border-top-right-radius: 20px; | |||
| padding-left: 12px; | |||
| } | |||
| .ant-pro-layout .ant-pro-sider .ant-layout-sider-children { | |||
| border-right: unset; | |||
| border-bottom-right-radius: 20px; | |||
| } | |||
| .ant-pro-base-menu-inline { | |||
| // height: 87vh; | |||
| background: #f2f5f7; | |||
| border-radius: 0px 20px 20px 0px; | |||
| } | |||
| .ant-pro-layout .ant-pro-layout-content { | |||
| background-color: transparent; | |||
| } | |||
| .ant-drawer .ant-drawer-body { | |||
| padding: 0; | |||
| } | |||
| @@ -102,15 +71,12 @@ body { | |||
| padding: 20px 16px; | |||
| background-color: #fff; | |||
| } | |||
| // .ant-table-wrapper .ant-table { | |||
| // height: 81vh; | |||
| // // overflow-y: auto; | |||
| // } | |||
| .ant-pro-global-header-logo img { | |||
| height: 21px; | |||
| } | |||
| .ant-pro-layout .ant-layout-sider.ant-pro-sider { | |||
| height: 94vh; | |||
| height: 100vh; | |||
| padding-top: 56px; | |||
| } | |||
| .ant-pro-layout .ant-pro-layout-container { | |||
| height: 100vh; | |||
| @@ -131,7 +97,7 @@ body { | |||
| width: 110px; | |||
| height: 40px; | |||
| margin-right: 10px; | |||
| // color: #1d1d20; | |||
| // color: @text-color; | |||
| font-size: 18px; | |||
| background: rgba(22, 100, 255, 0.06); | |||
| border-color: transparent; | |||
| @@ -161,9 +127,6 @@ body { | |||
| .ant-modal .ant-select-single .ant-select-selector .ant-select-selection-placeholder { | |||
| line-height: 46px; | |||
| } | |||
| .ant-menu-light.ant-menu-inline .ant-menu-item { | |||
| color: #575757; | |||
| } | |||
| .ant-modal .ant-modal-close-x { | |||
| width: 26px; | |||
| height: 26px; | |||
| @@ -41,7 +41,7 @@ export function useVisible(initialValue: boolean) { | |||
| setVisible(false); | |||
| }, []); | |||
| return [visible, open, close]; | |||
| return [visible, open, close] as const; | |||
| } | |||
| type Callback<T> = (state: T) => void; | |||
| @@ -92,7 +92,7 @@ export function useDomSize<T extends HTMLElement>( | |||
| setWidth(domRef.current.offsetWidth); | |||
| } | |||
| }; | |||
| const debounceFunc = debounce(setDomHeight, 200); | |||
| const debounceFunc = debounce(setDomHeight, 100); | |||
| setDomHeight(); | |||
| window.addEventListener('resize', debounceFunc); | |||
| @@ -52,3 +52,7 @@ | |||
| .ant-table-wrapper .ant-table-thead > tr > td { | |||
| background-color: #fff; | |||
| } | |||
| .ant-pro-page-container { | |||
| overflow-y: auto; | |||
| } | |||
| @@ -117,14 +117,14 @@ const PublicData = (React.FC = () => { | |||
| }; | |||
| const chooseDatasetType = (val, item) => { | |||
| console.log(val, item); | |||
| if (item.path == queryFlow.data_type) { | |||
| if (item.id == queryFlow.data_type) { | |||
| setActiveType(''); | |||
| setQueryFlow({ ...queryFlow, data_type: null }); | |||
| getDatasetlist({ ...queryFlow, data_type: null }); | |||
| } else { | |||
| setActiveType(item.path); | |||
| setQueryFlow({ ...queryFlow, data_type: item.path }); | |||
| getDatasetlist({ ...queryFlow, data_type: item.path }); | |||
| setActiveType(item.id); | |||
| setQueryFlow({ ...queryFlow, data_type: item.id }); | |||
| getDatasetlist({ ...queryFlow, data_type: item.id }); | |||
| } | |||
| // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | |||
| // getDatasetlist() | |||
| @@ -132,14 +132,14 @@ const PublicData = (React.FC = () => { | |||
| }; | |||
| const chooseDatasetTag = (val, item) => { | |||
| console.log(val, item); | |||
| if (item.path == queryFlow.data_tag) { | |||
| if (item.id == queryFlow.data_tag) { | |||
| setActiveTag(''); | |||
| setQueryFlow({ ...queryFlow, data_tag: null }); | |||
| getDatasetlist({ ...queryFlow, data_tag: null }); | |||
| } else { | |||
| setActiveTag(item.path); | |||
| setQueryFlow({ ...queryFlow, data_tag: item.path }); | |||
| getDatasetlist({ ...queryFlow, data_tag: item.path }); | |||
| setActiveTag(item.id); | |||
| setQueryFlow({ ...queryFlow, data_tag: item.id }); | |||
| getDatasetlist({ ...queryFlow, data_tag: item.id }); | |||
| } | |||
| // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | |||
| // getDatasetlist() | |||
| @@ -196,7 +196,7 @@ const PublicData = (React.FC = () => { | |||
| <div | |||
| className={[ | |||
| Styles.messageBox, | |||
| item.path === activeType ? Styles.active : null, | |||
| item.id === activeType ? Styles.active : null, | |||
| ].join(' ')} | |||
| onClick={(e) => { | |||
| chooseDatasetType(e, item); | |||
| @@ -230,7 +230,7 @@ const PublicData = (React.FC = () => { | |||
| <div | |||
| className={[ | |||
| Styles.messageBox, | |||
| item.path === activeTag ? Styles.active : null, | |||
| item.id === activeTag ? Styles.active : null, | |||
| ].join(' ')} | |||
| onClick={(e) => { | |||
| chooseDatasetTag(e, item); | |||
| @@ -76,14 +76,14 @@ const PublicData = () => { | |||
| }; | |||
| const chooseDatasetType = (val, item) => { | |||
| console.log(val, item); | |||
| if (item.path == queryFlow.data_type) { | |||
| if (item.id == queryFlow.data_type) { | |||
| setActiveType(''); | |||
| setQueryFlow({ ...queryFlow, data_type: null }); | |||
| getDatasetlist({ ...queryFlow, data_type: null }); | |||
| } else { | |||
| setActiveType(item.path); | |||
| setQueryFlow({ ...queryFlow, data_type: item.path }); | |||
| getDatasetlist({ ...queryFlow, data_type: item.path }); | |||
| setActiveType(item.id); | |||
| setQueryFlow({ ...queryFlow, data_type: item.id }); | |||
| getDatasetlist({ ...queryFlow, data_type: item.id }); | |||
| } | |||
| // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | |||
| // getDatasetlist() | |||
| @@ -91,14 +91,14 @@ const PublicData = () => { | |||
| }; | |||
| const chooseDatasetTag = (val, item) => { | |||
| console.log(val, item); | |||
| if (item.path == queryFlow.data_tag) { | |||
| if (item.id == queryFlow.data_tag) { | |||
| setActiveTag(''); | |||
| setQueryFlow({ ...queryFlow, data_tag: null }); | |||
| getDatasetlist({ ...queryFlow, data_tag: null }); | |||
| } else { | |||
| setActiveTag(item.path); | |||
| setQueryFlow({ ...queryFlow, data_tag: item.path }); | |||
| getDatasetlist({ ...queryFlow, data_tag: item.path }); | |||
| setActiveTag(item.id); | |||
| setQueryFlow({ ...queryFlow, data_tag: item.id }); | |||
| getDatasetlist({ ...queryFlow, data_tag: item.id }); | |||
| } | |||
| // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | |||
| // getDatasetlist() | |||
| @@ -146,7 +146,7 @@ const PublicData = () => { | |||
| <div | |||
| className={[ | |||
| Styles.messageBox, | |||
| item.path === activeType ? Styles.active : null, | |||
| item.id === activeType ? Styles.active : null, | |||
| ].join(' ')} | |||
| onClick={(e) => { | |||
| chooseDatasetType(e, item); | |||
| @@ -180,7 +180,7 @@ const PublicData = () => { | |||
| <div | |||
| className={[ | |||
| Styles.messageBox, | |||
| item.path === activeTag ? Styles.active : null, | |||
| item.id === activeTag ? Styles.active : null, | |||
| ].join(' ')} | |||
| onClick={(e) => { | |||
| chooseDatasetTag(e, item); | |||
| @@ -0,0 +1,9 @@ | |||
| const Docs = () => { | |||
| return ( | |||
| <iframe | |||
| style={{ width: '100%', height: '100%', border: 0 }} | |||
| src={'/assets/材料科研软件平台使用文档.pdf'} | |||
| ></iframe> | |||
| ); | |||
| }; | |||
| export default Docs; | |||
| @@ -24,7 +24,7 @@ | |||
| align-items: center; | |||
| width: 100%; | |||
| padding: 0 0 0 33px; | |||
| color: #1d1d20; | |||
| color: @text-color; | |||
| font-size: 15px; | |||
| & > div { | |||
| @@ -76,16 +76,18 @@ | |||
| .statusBox:hover .statusIcon { | |||
| visibility: visible; | |||
| } | |||
| .experimentBox{ | |||
| .experimentBox { | |||
| height: calc(100% - 20px); | |||
| .experimentTable{ | |||
| .experimentTable { | |||
| height: calc(100% - 60px); | |||
| :global{ | |||
| .ant-table-wrapper .ant-table{ | |||
| :global { | |||
| .ant-table-wrapper .ant-table { | |||
| // overflow-y: auto; | |||
| height: calc(100% - 48px); | |||
| } | |||
| .ant-table-row-expand-icon-cell { | |||
| padding: 0 30px; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -16,7 +16,7 @@ export enum ExperimentStatus { | |||
| } | |||
| type ExperimentStatusKeys = keyof typeof ExperimentStatus; | |||
| type ExperimentStatusValues = (typeof ExperimentStatus)[ExperimentStatusKeys]; | |||
| export type ExperimentStatusValues = (typeof ExperimentStatus)[ExperimentStatusKeys]; | |||
| export const experimentStatusInfo: Record<ExperimentStatusValues, StatusInfo | undefined> = { | |||
| Running: { | |||
| @@ -121,14 +121,14 @@ const PublicData = () => { | |||
| const chooseModelType = (val, item) => { | |||
| console.log(val, item); | |||
| if (item.path == queryFlow.model_type) { | |||
| if (item.id == queryFlow.model_type) { | |||
| setActiveType(''); | |||
| setQueryFlow({ ...queryFlow, model_type: null }); | |||
| getModelLists({ ...queryFlow, model_type: null }); | |||
| } else { | |||
| setActiveType(item.path); | |||
| setQueryFlow({ ...queryFlow, model_type: item.path }); | |||
| getModelLists({ ...queryFlow, model_type: item.path }); | |||
| setActiveType(item.id); | |||
| setQueryFlow({ ...queryFlow, model_type: item.id }); | |||
| getModelLists({ ...queryFlow, model_type: item.id }); | |||
| } | |||
| // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | |||
| @@ -136,14 +136,14 @@ const PublicData = () => { | |||
| // }) | |||
| }; | |||
| const chooseModelTag = (val, item) => { | |||
| if (item.path == queryFlow.model_tag) { | |||
| if (item.id == queryFlow.model_tag) { | |||
| setActiveTag(''); | |||
| setQueryFlow({ ...queryFlow, model_tag: null }); | |||
| getModelLists({ ...queryFlow, model_tag: null }); | |||
| } else { | |||
| setActiveTag(item.path); | |||
| setQueryFlow({ ...queryFlow, model_tag: item.path }); | |||
| getModelLists({ ...queryFlow, model_tag: item.path }); | |||
| setActiveTag(item.id); | |||
| setQueryFlow({ ...queryFlow, model_tag: item.id }); | |||
| getModelLists({ ...queryFlow, model_tag: item.id }); | |||
| } | |||
| // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | |||
| // getDatasetlist() | |||
| @@ -190,7 +190,7 @@ const PublicData = () => { | |||
| <div | |||
| className={[ | |||
| Styles.messageBox, | |||
| item.path === activeType ? Styles.active : null, | |||
| item.id === activeType ? Styles.active : null, | |||
| ].join(' ')} | |||
| onClick={(e) => { | |||
| chooseModelType(e, item); | |||
| @@ -231,7 +231,7 @@ const PublicData = () => { | |||
| <div | |||
| className={[ | |||
| Styles.messageBox, | |||
| item.path === activeTag ? Styles.active : null, | |||
| item.id === activeTag ? Styles.active : null, | |||
| ].join(' ')} | |||
| onClick={(e) => { | |||
| chooseModelTag(e, item); | |||
| @@ -77,14 +77,14 @@ const PublicData = () => { | |||
| }; | |||
| const chooseModelType = (val, item) => { | |||
| console.log(val, item); | |||
| if (item.path == queryFlow.model_type) { | |||
| if (item.id == queryFlow.model_type) { | |||
| setActiveType(''); | |||
| setQueryFlow({ ...queryFlow, model_type: null }); | |||
| getModelLists({ ...queryFlow, model_type: null }); | |||
| } else { | |||
| setActiveType(item.path); | |||
| setQueryFlow({ ...queryFlow, model_type: item.path }); | |||
| getModelLists({ ...queryFlow, model_type: item.path }); | |||
| setActiveType(item.id); | |||
| setQueryFlow({ ...queryFlow, model_type: item.id }); | |||
| getModelLists({ ...queryFlow, model_type: item.id }); | |||
| } | |||
| // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | |||
| @@ -92,14 +92,14 @@ const PublicData = () => { | |||
| // }) | |||
| }; | |||
| const chooseModelTag = (val, item) => { | |||
| if (item.path == queryFlow.model_tag) { | |||
| if (item.id == queryFlow.model_tag) { | |||
| setActiveTag(''); | |||
| setQueryFlow({ ...queryFlow, model_tag: null }); | |||
| getModelLists({ ...queryFlow, model_tag: null }); | |||
| } else { | |||
| setActiveTag(item.path); | |||
| setQueryFlow({ ...queryFlow, model_tag: item.path }); | |||
| getModelLists({ ...queryFlow, model_tag: item.path }); | |||
| setActiveTag(item.id); | |||
| setQueryFlow({ ...queryFlow, model_tag: item.id }); | |||
| getModelLists({ ...queryFlow, model_tag: item.id }); | |||
| } | |||
| // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | |||
| // getDatasetlist() | |||
| @@ -147,7 +147,7 @@ const PublicData = () => { | |||
| <div | |||
| className={[ | |||
| Styles.messageBox, | |||
| item.path === activeType ? Styles.active : null, | |||
| item.id === activeType ? Styles.active : null, | |||
| ].join(' ')} | |||
| onClick={(e) => { | |||
| chooseModelType(e, item); | |||
| @@ -181,7 +181,7 @@ const PublicData = () => { | |||
| <div | |||
| className={[ | |||
| Styles.messageBox, | |||
| item.path === activeTag ? Styles.active : null, | |||
| item.id === activeTag ? Styles.active : null, | |||
| ].join(' ')} | |||
| onClick={(e) => { | |||
| chooseModelTag(e, item); | |||
| @@ -22,7 +22,7 @@ | |||
| height: 398px; | |||
| margin-right: 15px; | |||
| padding: 15px; | |||
| background-color: @background-color-primay; | |||
| background-color: @background-color-primary; | |||
| border: 1px solid @border-color; | |||
| border-radius: 8px; | |||
| @@ -31,7 +31,7 @@ | |||
| padding-left: 0; | |||
| background-color: transparent; | |||
| border-width: 0; | |||
| border-bottom: 1px solid @border-color-second; | |||
| border-bottom: 1px solid @border-color-secondary; | |||
| border-radius: 0; | |||
| } | |||
| } | |||
| @@ -40,7 +40,7 @@ | |||
| width: calc(100% - 488px - 15px); | |||
| height: 398px; | |||
| padding: 15px; | |||
| background-color: @background-color-primay; | |||
| background-color: @background-color-primary; | |||
| border: 1px solid @border-color; | |||
| border-radius: 8px; | |||
| @@ -49,7 +49,7 @@ | |||
| padding: 3px 0 6px; | |||
| color: @text-color; | |||
| font-size: @font-size; | |||
| border-bottom: 1px solid @border-color-second; | |||
| border-bottom: 1px solid @border-color-secondary; | |||
| } | |||
| &__files { | |||
| height: calc(100% - 75px); | |||
| @@ -0,0 +1,54 @@ | |||
| .assets-management { | |||
| flex: 1; | |||
| width: 100%; | |||
| padding: 20px 20px 0; | |||
| background-color: white; | |||
| border-radius: 4px; | |||
| :global { | |||
| .ant-select-filled { | |||
| background-color: rgba(138, 138, 138, 0.12); | |||
| border-radius: 2px; | |||
| .ant-select-selection-item { | |||
| color: @text-color-secondary !important; | |||
| font-size: 13px; | |||
| } | |||
| } | |||
| } | |||
| &__title { | |||
| color: @text-color; | |||
| font-weight: 500; | |||
| font-size: @font-size-title; | |||
| } | |||
| &__increase { | |||
| display: inline-block; | |||
| margin-top: 12px; | |||
| margin-bottom: 30px; | |||
| padding: 2px 7px; | |||
| color: @primary-color-secondary; | |||
| font-size: 13px; | |||
| background-color: rgba(187, 210, 255, 0.29); | |||
| border-radius: 2px; | |||
| } | |||
| &__summary { | |||
| display: flex; | |||
| flex-direction: column; | |||
| width: 33.33%; | |||
| &__title { | |||
| margin-bottom: 12px; | |||
| color: @text-color-secondary; | |||
| font-size: @font-size; | |||
| } | |||
| &__value { | |||
| color: @text-color; | |||
| font-weight: 500; | |||
| font-size: 22px; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| import { CommonTabKeys } from '@/enums'; | |||
| import { getWorkspaceAssetCountReq } from '@/services/workspace'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Flex, Select } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import styles from './index.less'; | |||
| function AssetsManagement() { | |||
| const [type, setType] = useState(CommonTabKeys.Public); | |||
| const [assetCounts, setAssetCounts] = useState<{ title: string; value: number }[]>([]); | |||
| useEffect(() => { | |||
| getWorkspacAssetCount(); | |||
| }, [type]); | |||
| // 获取工作空间资产数量 | |||
| const getWorkspacAssetCount = async () => { | |||
| const params = { | |||
| isPublic: type === CommonTabKeys.Public, | |||
| }; | |||
| const [res] = await to(getWorkspaceAssetCountReq(params)); | |||
| if (res && res.data) { | |||
| const { component, dataset, image, model, workflow } = res.data; | |||
| const items = [ | |||
| { | |||
| title: '数据集', | |||
| value: dataset, | |||
| }, | |||
| { | |||
| title: '模型', | |||
| value: model, | |||
| }, | |||
| { | |||
| title: '镜像', | |||
| value: image, | |||
| }, | |||
| { | |||
| title: '组件', | |||
| value: component, | |||
| }, | |||
| { | |||
| title: '代码配置', | |||
| value: 0, | |||
| }, | |||
| { | |||
| title: '流水线模版', | |||
| value: workflow, | |||
| }, | |||
| ]; | |||
| setAssetCounts(items); | |||
| } | |||
| }; | |||
| return ( | |||
| <div className={styles['assets-management']}> | |||
| <Flex justify="space-between"> | |||
| <div className={styles['assets-management__title']}>AI资产</div> | |||
| <Select | |||
| size="small" | |||
| value={type} | |||
| style={{ width: 70 }} | |||
| onChange={setType} | |||
| variant="filled" | |||
| options={[ | |||
| { value: CommonTabKeys.Public, label: '公开' }, | |||
| { value: CommonTabKeys.Private, label: '个人' }, | |||
| ]} | |||
| /> | |||
| </Flex> | |||
| <div className={styles['assets-management__increase']}>今日新增数量:5</div> | |||
| <Flex justify="space-between" gap="22px 0" wrap="wrap"> | |||
| {assetCounts.map((item, index) => ( | |||
| <div className={styles['assets-management__summary']} key={index}> | |||
| <div className={styles['assets-management__summary__title']}>{item.title}</div> | |||
| <div className={styles['assets-management__summary__value']}>{item.value}</div> | |||
| </div> | |||
| ))} | |||
| </Flex> | |||
| </div> | |||
| ); | |||
| } | |||
| export default AssetsManagement; | |||
| @@ -0,0 +1,7 @@ | |||
| .experiment-chart { | |||
| width: 295px; | |||
| min-width: 295px; | |||
| height: 140px; | |||
| background-color: @workspace-background; | |||
| border-radius: 4px; | |||
| } | |||
| @@ -0,0 +1,214 @@ | |||
| import themes from '@/styles/theme.less'; | |||
| import * as echarts from 'echarts'; | |||
| import React, { useEffect, useRef } from 'react'; | |||
| import styles from './index.less'; | |||
| const color1 = new echarts.graphic.LinearGradient( | |||
| 0, | |||
| 0, | |||
| 0, | |||
| 1, | |||
| [ | |||
| { offset: 0, color: '#c73131' }, | |||
| { offset: 1, color: '#ff7e96' }, | |||
| ], | |||
| false, | |||
| ); | |||
| const color2 = new echarts.graphic.LinearGradient( | |||
| 0, | |||
| 0, | |||
| 0, | |||
| 1, | |||
| [ | |||
| { offset: 0, color: '#6ac21d' }, | |||
| { offset: 1, color: '#96e850' }, | |||
| ], | |||
| false, | |||
| ); | |||
| const color3 = new echarts.graphic.LinearGradient( | |||
| 0, | |||
| 0, | |||
| 0, | |||
| 1, | |||
| [ | |||
| { offset: 0, color: '#8c8c8c' }, | |||
| { offset: 1, color: '#c8c6c6' }, | |||
| ], | |||
| false, | |||
| ); | |||
| const color4 = new echarts.graphic.LinearGradient( | |||
| 0, | |||
| 0, | |||
| 0, | |||
| 1, | |||
| [ | |||
| { offset: 0, color: '#ecb934' }, | |||
| { offset: 1, color: '#f0864d' }, | |||
| ], | |||
| false, | |||
| ); | |||
| const color5 = new echarts.graphic.LinearGradient( | |||
| 0, | |||
| 0, | |||
| 0, | |||
| 1, | |||
| [ | |||
| { offset: 0, color: '#7ea9fe' }, | |||
| { offset: 1, color: '#1664ff' }, | |||
| ], | |||
| false, | |||
| ); | |||
| const color6 = new echarts.graphic.LinearGradient( | |||
| 0, | |||
| 0, | |||
| 0, | |||
| 1, | |||
| [ | |||
| { offset: 0, color: 'rgba(255, 255, 255, 0.62)' }, | |||
| { offset: 1, color: '#ebf2ff ' }, | |||
| ], | |||
| false, | |||
| ); | |||
| export type ExperimentStatistics = { | |||
| Failed: number; | |||
| Pending: number; | |||
| Running: number; | |||
| Succeeded: number; | |||
| Terminated: number; | |||
| }; | |||
| type ExperimentChartProps = { | |||
| style?: React.CSSProperties; | |||
| chartData: ExperimentStatistics; | |||
| }; | |||
| function ExperimentChart({ chartData, style }: ExperimentChartProps) { | |||
| const chartRef = useRef<HTMLDivElement>(null); | |||
| const total = | |||
| chartData.Failed + | |||
| chartData.Pending + | |||
| chartData.Running + | |||
| chartData.Succeeded + | |||
| chartData.Terminated; | |||
| const options: echarts.EChartsOption = { | |||
| title: { | |||
| show: true, | |||
| left: '29%', | |||
| top: 'center', | |||
| textAlign: 'center', | |||
| text: [`{a|${total}}`, '{b|实验状态}'].join('\n'), | |||
| textStyle: { | |||
| rich: { | |||
| a: { | |||
| color: themes['textColor'], | |||
| fontSize: 20, | |||
| fontWeight: 700, | |||
| lineHeight: 28, | |||
| }, | |||
| b: { | |||
| color: themes['textColorSecondary'], | |||
| fontSize: 10, | |||
| fontWeight: 'normal', | |||
| }, | |||
| }, | |||
| }, | |||
| }, | |||
| tooltip: { | |||
| trigger: 'item', | |||
| }, | |||
| legend: { | |||
| top: 'center', | |||
| right: '5%', | |||
| orient: 'vertical', | |||
| icon: 'circle', | |||
| itemWidth: 6, | |||
| itemGap: 20, | |||
| height: 100, | |||
| }, | |||
| color: [color1, color2, color3, color4, color5], | |||
| series: [ | |||
| { | |||
| type: 'pie', | |||
| radius: ['70%', '80%'], | |||
| center: ['30%', '50%'], | |||
| avoidLabelOverlap: false, | |||
| padAngle: 3, | |||
| itemStyle: { | |||
| borderRadius: 3, | |||
| }, | |||
| label: { | |||
| show: false, | |||
| }, | |||
| emphasis: { | |||
| label: { | |||
| show: false, | |||
| }, | |||
| }, | |||
| labelLine: { | |||
| show: false, | |||
| }, | |||
| data: [ | |||
| { value: chartData.Failed, name: '失败' }, | |||
| { value: chartData.Succeeded, name: '成功' }, | |||
| { value: chartData.Terminated, name: '中止' }, | |||
| { value: chartData.Pending, name: '等待' }, | |||
| { value: chartData.Running, name: '运行中' }, | |||
| ], | |||
| }, | |||
| { | |||
| type: 'pie', | |||
| radius: '60%', | |||
| center: ['30%', '50%'], | |||
| avoidLabelOverlap: false, | |||
| label: { | |||
| show: false, | |||
| }, | |||
| tooltip: { | |||
| show: false, | |||
| }, | |||
| emphasis: { | |||
| label: { | |||
| show: false, | |||
| }, | |||
| disabled: true, | |||
| }, | |||
| animation: false, | |||
| labelLine: { | |||
| show: false, | |||
| }, | |||
| data: [ | |||
| { | |||
| value: 100, | |||
| itemStyle: { color: color6, borderColor: 'rgba(22, 100, 255, 0.08)', borderWidth: 1 }, | |||
| }, | |||
| ], | |||
| }, | |||
| ], | |||
| }; | |||
| useEffect(() => { | |||
| // 创建一个echarts实例,返回echarts实例 | |||
| const chart = echarts.init(chartRef.current); | |||
| // 设置图表实例的配置项和数据 | |||
| chart.setOption(options); | |||
| // 组件卸载 | |||
| return () => { | |||
| // myChart.dispose() 销毁实例 | |||
| chart.dispose(); | |||
| }; | |||
| }, []); | |||
| return ( | |||
| <div className={styles['experiment-chart']} style={style}> | |||
| <div style={{ width: '100%', height: '100%' }} ref={chartRef}></div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default ExperimentChart; | |||
| @@ -0,0 +1,58 @@ | |||
| .experiment-table { | |||
| flex: 1; | |||
| min-width: 500px; | |||
| height: 140px; | |||
| padding: 12px 24px; | |||
| background-color: @workspace-background; | |||
| border-radius: 4px; | |||
| &__header { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| margin-bottom: 4px; | |||
| color: @text-color; | |||
| font-size: @font-size; | |||
| line-height: 20px; | |||
| } | |||
| &__content { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| padding: 6px 0; | |||
| color: .addAlpha(@text-color, 0.75) []; | |||
| font-size: 14px; | |||
| line-height: 20px; | |||
| border-bottom: 1px solid rgba(234, 234, 234, 0.8); | |||
| &:last-child { | |||
| border-bottom: 0; | |||
| } | |||
| } | |||
| &__status { | |||
| width: 20%; | |||
| } | |||
| &__duration { | |||
| width: 25%; | |||
| } | |||
| &__date { | |||
| width: 35%; | |||
| } | |||
| &__operation { | |||
| width: 20%; | |||
| :global { | |||
| .ant-btn-link { | |||
| height: 20px; | |||
| padding: 0; | |||
| line-height: 20px; | |||
| border: 0; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,59 @@ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import { ExperimentStatusValues, experimentStatusInfo } from '@/pages/Experiment/status'; | |||
| import { ExperimentInstance } from '@/types'; | |||
| import { elapsedTime, formatDate } from '@/utils/date'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { Button } from 'antd'; | |||
| import styles from './index.less'; | |||
| type ExperimentTableProps = { | |||
| tableData: ExperimentInstance[]; | |||
| style?: React.CSSProperties; | |||
| }; | |||
| function ExperimentTable({ tableData = [], style }: ExperimentTableProps) { | |||
| const navgite = useNavigate(); | |||
| const gotoExperiment = (record: ExperimentInstance) => { | |||
| navgite(`/pipeline/experimentPytorchtext/${record.workflow_id}/${record.id}`); | |||
| }; | |||
| return ( | |||
| <div className={styles['experiment-table']} style={style}> | |||
| <div className={styles['experiment-table__header']}> | |||
| <div className={styles['experiment-table__status']}>状态</div> | |||
| <div className={styles['experiment-table__duration']}>运行时长</div> | |||
| <div className={styles['experiment-table__date']}>开始时间</div> | |||
| <div className={styles['experiment-table__operation']}>操作</div> | |||
| </div> | |||
| {tableData?.map((item) => ( | |||
| <div className={styles['experiment-table__content']} key={item.id}> | |||
| <div className={styles['experiment-table__status']} style={{ paddingLeft: '6.5px' }}> | |||
| <img | |||
| src={experimentStatusInfo[item.status as ExperimentStatusValues]?.icon} | |||
| width={17} | |||
| height={17} | |||
| /> | |||
| </div> | |||
| <div className={styles['experiment-table__duration']}> | |||
| {elapsedTime( | |||
| new Date(item.create_time), | |||
| item.finish_time ? new Date(item.finish_time) : new Date(), | |||
| )} | |||
| </div> | |||
| <div className={styles['experiment-table__date']}>{formatDate(item.create_time)}</div> | |||
| <div className={styles['experiment-table__operation']}> | |||
| <Button | |||
| size="small" | |||
| type="link" | |||
| icon={<KFIcon type="icon-xiangqing2" font={14} />} | |||
| onClick={() => gotoExperiment(item)} | |||
| > | |||
| 详情 | |||
| </Button> | |||
| </div> | |||
| </div> | |||
| ))} | |||
| </div> | |||
| ); | |||
| } | |||
| export default ExperimentTable; | |||
| @@ -0,0 +1,60 @@ | |||
| import styles from './index.less'; | |||
| type WorkArrowProps = { | |||
| width?: number; | |||
| height?: number; | |||
| x: number; | |||
| y: number; | |||
| arrowLeft: number; | |||
| arrorwTop: number; | |||
| borderLeft?: number; | |||
| borderTop?: number; | |||
| borderRight?: number; | |||
| borderBottom?: number; | |||
| arrrowAngle?: number; | |||
| }; | |||
| function WorkArrow({ | |||
| width = 1, | |||
| height = 1, | |||
| x, | |||
| y, | |||
| arrowLeft, | |||
| arrorwTop, | |||
| borderLeft = 0, | |||
| borderTop = 0, | |||
| borderRight = 0, | |||
| borderBottom = 0, | |||
| arrrowAngle = 0, | |||
| }: WorkArrowProps) { | |||
| return ( | |||
| <div | |||
| className={styles['work-arrow']} | |||
| style={{ | |||
| left: `${x}px`, | |||
| top: `${y}px`, | |||
| width: `${width}px`, | |||
| height: `${height}px`, | |||
| borderLeftWidth: `${borderLeft}px`, | |||
| borderTopWidth: `${borderTop}px`, | |||
| borderRightWidth: `${borderRight}px`, | |||
| borderBottomWidth: `${borderBottom}px`, | |||
| }} | |||
| > | |||
| <img | |||
| className={styles['work-arrow__img']} | |||
| src={require('@/assets/img/blue-triangle.png')} | |||
| alt="" | |||
| width={10} | |||
| height={9} | |||
| style={{ | |||
| left: `${arrowLeft}px`, | |||
| top: `${arrorwTop}px`, | |||
| transform: `rotate(${arrrowAngle}deg)`, | |||
| }} | |||
| /> | |||
| </div> | |||
| ); | |||
| } | |||
| export default WorkArrow; | |||
| @@ -0,0 +1,31 @@ | |||
| import { Button } from 'antd'; | |||
| import styles from './index.less'; | |||
| type WorkFlowProps = { | |||
| content: string; | |||
| buttonText: string; | |||
| tips?: string; | |||
| onClick?: () => void; | |||
| buttonTop?: number; | |||
| x: number; | |||
| y: number; | |||
| }; | |||
| function WorkFlow({ content, buttonText, tips, buttonTop = 20, x, y, onClick }: WorkFlowProps) { | |||
| return ( | |||
| <div className={styles['work-flow']} style={{ left: `${x}px`, top: `${y}px` }}> | |||
| {tips && <div className={styles['work-flow__tips']}>{tips}</div>} | |||
| <div className={styles['work-flow__content']}>{content}</div> | |||
| <Button | |||
| className={styles['work-flow__button']} | |||
| type="primary" | |||
| style={{ marginTop: `${buttonTop}px` }} | |||
| onClick={onClick} | |||
| > | |||
| {buttonText} | |||
| </Button> | |||
| </div> | |||
| ); | |||
| } | |||
| export default WorkFlow; | |||
| @@ -0,0 +1,96 @@ | |||
| .quick-start { | |||
| width: calc(100% - 326px); | |||
| padding: 20px 30px; | |||
| background-color: white; | |||
| border-radius: 4px; | |||
| &__title { | |||
| margin-bottom: 20px; | |||
| color: @text-color; | |||
| font-weight: 500; | |||
| font-size: @font-size-title; | |||
| } | |||
| &__content { | |||
| position: relative; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| width: 100%; | |||
| height: 610px; | |||
| background-image: url(@/assets/img/workspace-quick-start.png); | |||
| background-repeat: no-repeat; | |||
| background-position: top left; | |||
| background-size: 100% 100%; | |||
| &__canvas { | |||
| position: relative; | |||
| width: 1223px; | |||
| height: 610px; | |||
| transform-origin: center left; | |||
| &__model { | |||
| position: absolute; | |||
| top: 358px; | |||
| left: 920px; | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| color: @primary-color; | |||
| font-size: @font-size; | |||
| } | |||
| &__task { | |||
| position: absolute; | |||
| top: 110px; | |||
| left: 603px; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| width: 131px; | |||
| height: 41px; | |||
| color: @primary-color; | |||
| font-size: @font-size; | |||
| background-color: rgba(22, 100, 255, 0.05); | |||
| border: 1px dashed @primary-color; | |||
| border-radius: 4px; | |||
| box-shadow: 0px 0px 6px rgba(22, 100, 255, 0.07); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .work-flow { | |||
| position: absolute; | |||
| width: 192px; | |||
| padding: 15px; | |||
| background-color: white; | |||
| border: 1px solid; | |||
| border-color: rgba(22, 100, 255, 0.08); | |||
| border-radius: 0px 8px 0px 0px; | |||
| box-shadow: 0px 0px 10px rgba(22, 100, 255, 0.06); | |||
| &__content { | |||
| color: @text-color-secondary; | |||
| font-size: @font-size; | |||
| } | |||
| &__tips { | |||
| position: absolute; | |||
| top: -16px; | |||
| left: 0; | |||
| padding: 4px 10px; | |||
| color: white; | |||
| font-size: @font-size; | |||
| background-color: #333333; | |||
| } | |||
| } | |||
| .work-arrow { | |||
| position: absolute; | |||
| border: 0 dashed @primary-color; | |||
| &__img { | |||
| position: absolute; | |||
| } | |||
| } | |||
| @@ -0,0 +1,149 @@ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { debounce } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import WorkArrow from './WorkArrow'; | |||
| import WorkFlow from './WorkFlow'; | |||
| import styles from './index.less'; | |||
| function QuickStart() { | |||
| const navgite = useNavigate(); | |||
| const [scale, setScale] = useState(1); | |||
| useEffect(() => { | |||
| const changeScale = () => { | |||
| const width = document.body.offsetWidth - 256 - 80 - 60 - 326 - 15 - 8; | |||
| const ratio = width >= 1223 ? 1 : width / 1223; | |||
| setScale(ratio); | |||
| }; | |||
| const debounceFunc = debounce(changeScale, 16); | |||
| window.addEventListener('resize', debounceFunc); | |||
| return () => { | |||
| window.removeEventListener('resize', debounceFunc); | |||
| }; | |||
| }, []); | |||
| return ( | |||
| <div className={styles['quick-start']}> | |||
| <div className={styles['quick-start__title']}>快速开始</div> | |||
| <div className={styles['quick-start__content']}> | |||
| <div | |||
| className={styles['quick-start__content__canvas']} | |||
| style={{ transform: `scale(${scale})` }} | |||
| > | |||
| <WorkFlow | |||
| content="为开发者提供数据智能标注与数据回流服务" | |||
| buttonText="数据准备" | |||
| buttonTop={40} | |||
| x={20} | |||
| y={309} | |||
| onClick={() => navgite('/datasetPreparation/datasetAnnotation')} | |||
| /> | |||
| <WorkFlow | |||
| content="为开发者提供定制化编辑器,开发者可根据自己需求选择配置,保存编译器中的调试环境为镜像供训练使用" | |||
| buttonText="开发环境" | |||
| buttonTop={20} | |||
| x={248} | |||
| y={301} | |||
| onClick={() => navgite('/developmentEnvironment')} | |||
| /> | |||
| <WorkFlow | |||
| content="为开发者提供定制化编辑器,开发者可根据自己需求选择配置,保存编译器中的调试环境为镜像供训练使用" | |||
| tips="可视化建模Designer" | |||
| buttonText="流水线" | |||
| buttonTop={20} | |||
| x={476} | |||
| y={276} | |||
| onClick={() => navgite('/pipeline/pipelineText')} | |||
| /> | |||
| <WorkFlow | |||
| content="开发者可以在这里运行流水线模板,产生实验实例,对比实验训练过程与产生的实验训练数据" | |||
| buttonText="实验" | |||
| buttonTop={40} | |||
| x={699} | |||
| y={295} | |||
| onClick={() => navgite('/pipeline/experimentText')} | |||
| /> | |||
| <WorkFlow | |||
| content="支持异构硬件(CPU/GPU)的模型加载,高吞吐,低延迟;支持大规模复杂模型的一键部署,实时弹性扩缩容;提供完整的运维监控体系。" | |||
| tips="模型在线服务" | |||
| buttonText="模型在线部署" | |||
| buttonTop={20} | |||
| x={1010} | |||
| y={263} | |||
| onClick={() => navgite('/modelDseployment')} | |||
| /> | |||
| <div className={styles['quick-start__content__canvas__model']}> | |||
| <KFIcon type="icon-moxingguanli" font={38} /> | |||
| <span>模型管理</span> | |||
| </div> | |||
| <div className={styles['quick-start__content__canvas__task']}> | |||
| <KFIcon type="icon-tiaoduguanli" font={13} style={{ marginRight: '5px' }} /> | |||
| <span>任务自动调度</span> | |||
| </div> | |||
| <WorkArrow | |||
| x={213} | |||
| y={378} | |||
| width={22} | |||
| height={1} | |||
| arrowLeft={22} | |||
| arrorwTop={-4} | |||
| borderBottom={1} | |||
| /> | |||
| <WorkArrow | |||
| x={441} | |||
| y={378} | |||
| width={22} | |||
| height={1} | |||
| arrowLeft={22} | |||
| arrorwTop={-4} | |||
| borderBottom={1} | |||
| /> | |||
| <WorkArrow | |||
| x={893} | |||
| y={378} | |||
| width={22} | |||
| height={1} | |||
| arrowLeft={22} | |||
| arrorwTop={-4} | |||
| borderBottom={1} | |||
| /> | |||
| <WorkArrow | |||
| x={974} | |||
| y={378} | |||
| width={22} | |||
| height={1} | |||
| arrowLeft={22} | |||
| arrorwTop={-4} | |||
| borderBottom={1} | |||
| /> | |||
| <WorkArrow | |||
| x={532} | |||
| y={139} | |||
| width={54} | |||
| height={125} | |||
| arrowLeft={54} | |||
| arrorwTop={-4} | |||
| borderLeft={1} | |||
| borderTop={1} | |||
| /> | |||
| <WorkArrow | |||
| x={740} | |||
| y={127} | |||
| width={49} | |||
| height={156} | |||
| arrowLeft={44} | |||
| arrorwTop={156} | |||
| arrrowAngle={90} | |||
| borderRight={1} | |||
| borderTop={1} | |||
| /> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default QuickStart; | |||
| @@ -0,0 +1,41 @@ | |||
| .total-statistics { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| width: 400px; | |||
| height: 140px; | |||
| background-color: @workspace-background; | |||
| border-radius: 4px; | |||
| &__icon { | |||
| width: 85px; | |||
| height: 80px; | |||
| margin-right: 40px; | |||
| } | |||
| &__title { | |||
| position: relative; | |||
| margin-bottom: 6px; | |||
| color: @text-color-secondary; | |||
| font-size: @font-size-content; | |||
| } | |||
| &__title-shadow { | |||
| position: absolute; | |||
| bottom: 6px; | |||
| left: 0; | |||
| width: 79px; | |||
| height: 6px; | |||
| background-color: linear-gradient( | |||
| 87.07deg, | |||
| rgba(22, 100, 255, 0.6) 0%, | |||
| rgba(22, 100, 255, 0) 100% | |||
| ); | |||
| } | |||
| &__count { | |||
| color: @text-color; | |||
| font-weight: 700; | |||
| font-size: 25px; | |||
| } | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| import styles from './index.less'; | |||
| type TotalStatisticsProps = { | |||
| icon: string; | |||
| title: string; | |||
| count?: string | number; | |||
| style?: React.CSSProperties; | |||
| }; | |||
| function TotalStatistics({ icon = '', title = '', count = 0, style }: TotalStatisticsProps) { | |||
| return ( | |||
| <div className={styles['total-statistics']} style={style}> | |||
| <img className={styles['total-statistics__icon']} src={icon} /> | |||
| <div> | |||
| <div className={styles['total-statistics__title']}> | |||
| <span>{title}</span> | |||
| <div className={styles['total-statistics__title-shadow']}></div> | |||
| </div> | |||
| <div className={styles['total-statistics__count']}>{count ?? '--'}</div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default TotalStatistics; | |||
| @@ -0,0 +1,65 @@ | |||
| .user-space { | |||
| margin-bottom: 16px; | |||
| padding-bottom: 20px; | |||
| background-color: white; | |||
| border-radius: 4px; | |||
| &__title { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| width: 100%; | |||
| height: 107px; | |||
| color: @text-color; | |||
| font-size: @font-size-title; | |||
| background-image: url(@/assets/img/workspace-user.png); | |||
| background-repeat: no-repeat; | |||
| background-position: top left; | |||
| background-size: 100% 100%; | |||
| } | |||
| &__avatar { | |||
| position: relative; | |||
| top: -28px; | |||
| width: 56px; | |||
| height: 56px; | |||
| } | |||
| &__name { | |||
| margin-top: -20px; | |||
| margin-bottom: 8px; | |||
| color: @text-color; | |||
| font-size: @font-size-content; | |||
| } | |||
| &__role { | |||
| display: inline-block; | |||
| padding: 1px 7px; | |||
| color: @primary-color-secondary; | |||
| font-size: 13px; | |||
| background-color: rgba(187, 210, 255, 0.29); | |||
| border-radius: 2px; | |||
| } | |||
| &__participant { | |||
| &__title { | |||
| color: @text-color-secondary; | |||
| font-size: @font-size-content; | |||
| } | |||
| &__count { | |||
| width: 24px; | |||
| height: 24px; | |||
| color: #8a8a8a; | |||
| font-size: 12px; | |||
| line-height: 24px; | |||
| text-align: center; | |||
| background-color: rgba(153, 153, 153, 0.13); | |||
| border-radius: 50%; | |||
| } | |||
| &__user { | |||
| width: 36px; | |||
| height: 36px; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| import { useModel } from '@umijs/max'; | |||
| import { Divider, Flex, Space } from 'antd'; | |||
| import styles from './index.less'; | |||
| type UserSpaceProps = { | |||
| users: any[]; | |||
| }; | |||
| function UserSpace({ users = [] }: UserSpaceProps) { | |||
| const { initialState } = useModel('@@initialState'); | |||
| const { currentUser } = initialState || {}; | |||
| return ( | |||
| <div className={styles['user-space']}> | |||
| <div className={styles['user-space__title']}>工作空间管理</div> | |||
| <div style={{ padding: '0 20px' }}> | |||
| <img className={styles['user-space__avatar']} src={currentUser?.avatar} alt="" /> | |||
| <div className={styles['user-space__name']}>{currentUser?.nickName}</div> | |||
| <div className={styles['user-space__role']}>{currentUser?.roleNames?.[0]?.roleName}</div> | |||
| <Divider | |||
| dashed | |||
| style={{ borderColor: 'rgba(22, 100, 255, 0.19)', margin: '20px 0' }} | |||
| ></Divider> | |||
| <div className={styles['user-space__participant']}> | |||
| <Space align="center" size={10} style={{ marginBottom: '20px' }}> | |||
| <div className={styles['user-space__participant__title']}>参与者</div> | |||
| <div className={styles['user-space__participant__count']}>8</div> | |||
| </Space> | |||
| <Flex align="center" gap={12} wrap="wrap"> | |||
| {users?.map((item, index) => { | |||
| return ( | |||
| <img | |||
| className={styles['user-space__participant__user']} | |||
| key={index} | |||
| src={require(`@/assets/img/user-avatar/${index + 1}.png`)} | |||
| alt="" | |||
| /> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default UserSpace; | |||
| @@ -0,0 +1,43 @@ | |||
| .workspace-intro { | |||
| display: flex; | |||
| align-items: center; | |||
| margin-bottom: 16px; | |||
| padding: 0 30px; | |||
| background-image: url(@/assets/img/workspace-intro.png); | |||
| background-repeat: no-repeat; | |||
| background-position: top right; | |||
| background-size: 100% 100%; | |||
| border-radius: 4px; | |||
| &__left { | |||
| padding: 30px 0 34px; | |||
| } | |||
| &__right { | |||
| display: flex; | |||
| flex: 1; | |||
| align-items: center; | |||
| justify-content: center; | |||
| } | |||
| &__title { | |||
| margin-bottom: 20px; | |||
| color: @text-color; | |||
| font-weight: 500; | |||
| font-size: 20px; | |||
| } | |||
| &__content { | |||
| max-width: 980px; | |||
| margin-bottom: 20px; | |||
| color: @text-color-secondary; | |||
| font-size: @font-size-title; | |||
| line-height: 1.8; | |||
| letter-spacing: 1px; | |||
| } | |||
| &__icon { | |||
| width: 363px; | |||
| height: 216px; | |||
| } | |||
| } | |||
| @@ -0,0 +1,43 @@ | |||
| import { Button } from 'antd'; | |||
| import styles from './index.less'; | |||
| function WorkspaceIntro() { | |||
| return ( | |||
| <div className={styles['workspace-intro']}> | |||
| <div className={styles['workspace-intro__left']}> | |||
| <div className={styles['workspace-intro__title']}>自主实验平台</div> | |||
| <div className={styles['workspace-intro__content']}> | |||
| 材料领域的自主实验系统是一种用于材料研究和开发的技术平台,它旨在提供实验数据收集、分析和可视化等功能, | |||
| 以支持材料工程师、科学家和研究人员在材料设计、性能评估和工艺优化方面的工作 | |||
| </div> | |||
| <div className={styles['workspace-intro__buttons']}> | |||
| <Button | |||
| type="primary" | |||
| style={{ marginRight: '20px' }} | |||
| icon={ | |||
| <img src={require('@/assets/img/functional-material.png')} width={19} height={19} /> | |||
| } | |||
| > | |||
| 功能材料自主实验系统 | |||
| </Button> | |||
| <Button | |||
| type="default" | |||
| icon={ | |||
| <img src={require('@/assets/img/molecular-material.png')} width={19} height={19} /> | |||
| } | |||
| > | |||
| 分子材料自主实验系统 | |||
| </Button> | |||
| </div> | |||
| </div> | |||
| <div className={styles['workspace-intro__right']}> | |||
| <img | |||
| className={styles['workspace-intro__icon']} | |||
| src={require('@/assets/img/workspace-intro-icon.png')} | |||
| /> | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default WorkspaceIntro; | |||
| @@ -0,0 +1,45 @@ | |||
| .workspace { | |||
| height: 100%; | |||
| padding: 20px 30px 10px; | |||
| overflow-y: auto; | |||
| background-color: linear-gradient(#ecf2fe, #f9fafb); | |||
| &__overview { | |||
| margin-bottom: 16px; | |||
| padding: 20px 30px; | |||
| background-color: white; | |||
| border-radius: 4px; | |||
| &__title { | |||
| margin-bottom: 20px; | |||
| color: @text-color; | |||
| font-weight: 500; | |||
| font-size: @font-size-title; | |||
| } | |||
| &__content { | |||
| display: flex; | |||
| gap: 15px; | |||
| align-items: center; | |||
| @media screen and (max-width: 1800px) { | |||
| flex-wrap: wrap; | |||
| } | |||
| } | |||
| } | |||
| &__quick-start { | |||
| display: flex; | |||
| gap: 15px; | |||
| align-items: flex-start; | |||
| width: 100%; | |||
| } | |||
| &__user { | |||
| display: flex; | |||
| flex-direction: column; | |||
| width: 326px; | |||
| min-width: 326px; | |||
| height: 700px; | |||
| } | |||
| } | |||
| @@ -0,0 +1,71 @@ | |||
| import { getWorkspaceOverviewReq } from '@/services/workspace'; | |||
| import { ExperimentInstance } from '@/types'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useEffect, useState } from 'react'; | |||
| import AssetsManagement from './components/AssetsManagement'; | |||
| import ExperimentChart, { type ExperimentStatistics } from './components/ExperimentChart'; | |||
| import ExperitableTable from './components/ExperimentTable'; | |||
| import QuickStart from './components/QuickStart'; | |||
| import TotalStatistics from './components/TotalStatistics'; | |||
| import UserSpace from './components/UserSpace'; | |||
| import WorkspaceIntro from './components/WorkspaceIntro'; | |||
| import styles from './index.less'; | |||
| type OverviewData = { | |||
| workflowCount: number; | |||
| runningExperimentInsCount: number; | |||
| experimentInsStatus: ExperimentStatistics; | |||
| latestExperimentInsList: ExperimentInstance[]; | |||
| }; | |||
| function Workspace() { | |||
| const [overviewData, setOverviewData] = useState<OverviewData>(); | |||
| const users: number[] = new Array(8).fill(0); | |||
| useEffect(() => { | |||
| getWorkspaceOverview(); | |||
| }, []); | |||
| // 获取工作空间概况 | |||
| const getWorkspaceOverview = async () => { | |||
| const [res] = await to(getWorkspaceOverviewReq()); | |||
| if (res && res.data) { | |||
| setOverviewData(res.data); | |||
| } | |||
| }; | |||
| return ( | |||
| <div className={styles.workspace}> | |||
| <WorkspaceIntro></WorkspaceIntro> | |||
| <div className={styles['workspace__overview']}> | |||
| <div className={styles['workspace__overview__title']}>运行概览</div> | |||
| <div className={styles['workspace__overview__content']}> | |||
| <TotalStatistics | |||
| icon={require('@/assets/img/workspace-pipeline.png')} | |||
| title="流水线总数" | |||
| count={overviewData?.workflowCount} | |||
| /> | |||
| <TotalStatistics | |||
| icon={require('@/assets/img/workspace-experiment.png')} | |||
| title="正在运行实例总数" | |||
| count={overviewData?.runningExperimentInsCount} | |||
| /> | |||
| <ExperitableTable | |||
| tableData={overviewData?.latestExperimentInsList || []} | |||
| ></ExperitableTable> | |||
| {overviewData?.experimentInsStatus && ( | |||
| <ExperimentChart chartData={overviewData?.experimentInsStatus}></ExperimentChart> | |||
| )} | |||
| </div> | |||
| </div> | |||
| <div className={styles['workspace__quick-start']}> | |||
| <QuickStart></QuickStart> | |||
| <div className={styles['workspace__user']}> | |||
| <UserSpace users={users}></UserSpace> | |||
| <AssetsManagement></AssetsManagement> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default Workspace; | |||
| @@ -46,7 +46,8 @@ export const requestConfig: RequestConfig = { | |||
| ], | |||
| responseInterceptors: [ | |||
| (response: any) => { | |||
| const { status, data } = response; | |||
| const { status, data } = response || {}; | |||
| console.log('response2', response); | |||
| if (status >= 200 && status < 300) { | |||
| if (data && (data instanceof Blob || data.code === 200)) { | |||
| return response; | |||
| @@ -14,6 +14,9 @@ declare namespace API { | |||
| }; | |||
| address?: string; | |||
| phone?: string; | |||
| roleNames?: { | |||
| roleName?: string; | |||
| }[]; | |||
| }; | |||
| type ErrorResponse = { | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 14:29:44 | |||
| * @Description: | |||
| * @Description: 镜像管理接口 | |||
| */ | |||
| import { request } from '@umijs/max'; | |||
| @@ -0,0 +1,21 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 14:29:44 | |||
| * @Description: 工作空间接口 | |||
| */ | |||
| import { request } from '@umijs/max'; | |||
| // 获取工作空间概况 | |||
| export function getWorkspaceOverviewReq() { | |||
| return request(`/api/mmp/workspace/overview`, { | |||
| method: 'GET', | |||
| }); | |||
| } | |||
| // 获取工作空间概况 | |||
| export function getWorkspaceAssetCountReq(params: any) { | |||
| return request(`/api/mmp/workspace/assetCount`, { | |||
| method: 'GET', | |||
| params, | |||
| }); | |||
| } | |||
| @@ -6,6 +6,7 @@ | |||
| // 颜色 | |||
| @primary-color: #1664ff; // 主色调 | |||
| @primary-color-secondary: #4e89ff; | |||
| @primary-color-hover: #69b1ff; | |||
| @background-color: #f9fafb; // 页面背景颜色 | |||
| @text-color: #1d1d20; | |||
| @@ -15,17 +16,35 @@ | |||
| @warning-color: #f98e1b; | |||
| @border-color: rgba(22, 100, 255, 0.3); | |||
| @border-color-second: rgba(22, 100, 255, 0.1); | |||
| @background-color-primay: rgba(22, 100, 255, 0.03); | |||
| @border-color-secondary: rgba(22, 100, 255, 0.1); | |||
| @background-color-primary: rgba(22, 100, 255, 0.03); | |||
| @background-color-gray: rgba(4, 3, 3, 0.06); | |||
| @heading-color: rgba(0, 0, 0, 0.85); | |||
| @input-icon-hover-color: rgba(0, 0, 0, 0.85); | |||
| @border-color-base: #d9d9d9; | |||
| @link-hover-color: #69b1ff; | |||
| @sider-background-color: #f2f5f7; | |||
| @workspace-background: linear-gradient( | |||
| 179.03deg, | |||
| rgba(138, 138, 138, 0.06) 0%, | |||
| rgba(22, 100, 255, 0.02) 100% | |||
| ); | |||
| // 字体大小 | |||
| @font-size: 15px; | |||
| @font-size-title: 18px; | |||
| @font-size-content: 16px; | |||
| // 函数 | |||
| .addAlpha(@color, @alpha) { | |||
| @red: red(@color); | |||
| @green: green(@color); | |||
| @blue: blue(@color); | |||
| @result: rgba(@red, @green, @blue, @alpha); | |||
| } | |||
| // 导出变量 | |||
| :export { | |||
| @@ -34,5 +53,7 @@ | |||
| errorColor: @error-color; | |||
| warningColor: @warning-color; | |||
| textColor: @text-color; | |||
| textColorSecondary: @text-color-secondary; | |||
| fontSize: @font-size; | |||
| siderBGColor: @sider-background-color; | |||
| } | |||
| @@ -12,3 +12,19 @@ export type PipelineGlobalParam = { | |||
| param_value: number | string | boolean; | |||
| is_sensitive: number; | |||
| }; | |||
| // 实验实例 | |||
| export type ExperimentInstance = { | |||
| id: number; | |||
| experiment_id: number; | |||
| workflow_id: number; | |||
| create_time: string; | |||
| finish_time: string; | |||
| update_time: string; | |||
| status: string; | |||
| argo_ins_name: string; | |||
| argo_ins_ns: string; | |||
| nodes_result: string; | |||
| nodes_status: string; | |||
| global_param: PipelineGlobalParam[]; | |||
| }; | |||
| @@ -36,3 +36,15 @@ export const isValidDate = (date: Date): boolean => { | |||
| } | |||
| return false; | |||
| }; | |||
| // 格式化日期 | |||
| export const formatDate = (text: Date | string, format = 'YYYY-MM-DD HH:mm:ss'): string => { | |||
| if (text === undefined || text === null || text === '') { | |||
| return '--'; | |||
| } | |||
| if (!dayjs(text).isValid()) { | |||
| return '--'; | |||
| } | |||
| return dayjs(text).format(format); | |||
| }; | |||
| @@ -51,7 +51,7 @@ export const gotoLoginPage = (toHome: boolean = true) => { | |||
| const { pathname, search } = window.location; | |||
| const urlParams = new URLSearchParams(); | |||
| urlParams.append('redirect', pathname + search); | |||
| const newSearch = toHome ? '' : urlParams.toString(); | |||
| const newSearch = toHome && pathname && pathname !== PageEnum.LOGIN ? '' : urlParams.toString(); | |||
| if (window.location.pathname !== PageEnum.LOGIN) { | |||
| history.replace({ | |||
| pathname: PageEnum.LOGIN, | |||