| @@ -78,7 +78,7 @@ export default defineConfig({ | |||||
| */ | */ | ||||
| title: '智能软件开发平台', | title: '智能软件开发平台', | ||||
| layout: { | layout: { | ||||
| locale: true, | |||||
| locale: false, | |||||
| ...defaultSettings, | ...defaultSettings, | ||||
| }, | }, | ||||
| // keepalive: [/./], | // keepalive: [/./], | ||||
| @@ -97,10 +97,8 @@ export default defineConfig({ | |||||
| * @doc https://umijs.org/docs/max/i18n | * @doc https://umijs.org/docs/max/i18n | ||||
| */ | */ | ||||
| locale: { | locale: { | ||||
| // default zh-CN | |||||
| default: 'zh-CN', | default: 'zh-CN', | ||||
| antd: true, | antd: true, | ||||
| // default true, when it is true, will use `navigator.language` overwrite default | |||||
| baseNavigator: true, | baseNavigator: true, | ||||
| }, | }, | ||||
| /** | /** | ||||
| @@ -13,7 +13,7 @@ | |||||
| export default [ | export default [ | ||||
| { | { | ||||
| path: '/', | path: '/', | ||||
| redirect: '/account/center', | |||||
| redirect: '/workspace', | |||||
| }, | }, | ||||
| { | { | ||||
| path: '*', | path: '*', | ||||
| @@ -88,13 +88,6 @@ export default [ | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | |||||
| name: 'experiment', | |||||
| path: '/experiment', | |||||
| routes: [ | |||||
| ], | |||||
| }, | |||||
| { | { | ||||
| name: 'developmentEnvironment', | name: 'developmentEnvironment', | ||||
| path: '/developmentEnvironment', | path: '/developmentEnvironment', | ||||
| @@ -169,7 +162,7 @@ export default [ | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'workspace', | name: 'workspace', | ||||
| path: '/workspace', | path: '/workspace', | ||||
| @@ -177,7 +170,8 @@ export default [ | |||||
| { | { | ||||
| name: '工作空间', | name: '工作空间', | ||||
| path: '', | path: '', | ||||
| component: './missingPage.jsx', | |||||
| key: 'workspace', | |||||
| component: './Workspace/index', | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| @@ -188,11 +182,11 @@ export default [ | |||||
| { | { | ||||
| name: '模型部署', | name: '模型部署', | ||||
| path: '', | path: '', | ||||
| key: 'modelDseployment', | |||||
| component: './missingPage.jsx', | component: './missingPage.jsx', | ||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'appsDeployment', | name: 'appsDeployment', | ||||
| path: '/appsDeployment', | path: '/appsDeployment', | ||||
| @@ -200,6 +194,7 @@ export default [ | |||||
| { | { | ||||
| name: '应用开发', | name: '应用开发', | ||||
| path: '', | path: '', | ||||
| key: 'appsDeployment', | |||||
| component: './missingPage.jsx', | component: './missingPage.jsx', | ||||
| }, | }, | ||||
| ], | ], | ||||
| @@ -211,6 +206,7 @@ export default [ | |||||
| { | { | ||||
| name: '监控运维', | name: '监控运维', | ||||
| path: '', | path: '', | ||||
| key: 'see', | |||||
| component: './missingPage.jsx', | 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", | "@umijs/route-utils": "^4.0.1", | ||||
| "antd": "^5.4.4", | "antd": "^5.4.4", | ||||
| "classnames": "^2.3.2", | "classnames": "^2.3.2", | ||||
| "echarts": "^5.5.0", | |||||
| "fabric": "^5.3.0", | "fabric": "^5.3.0", | ||||
| "highlight.js": "^11.7.0", | "highlight.js": "^11.7.0", | ||||
| "lodash": "^4.17.21", | "lodash": "^4.17.21", | ||||
| @@ -70,6 +71,7 @@ | |||||
| "rc-menu": "^9.8.4", | "rc-menu": "^9.8.4", | ||||
| "rc-util": "^5.30.0", | "rc-util": "^5.30.0", | ||||
| "react": "^18.2.0", | "react": "^18.2.0", | ||||
| "react-activation": "^0.12.4", | |||||
| "react-cropper": "^2.3.3", | "react-cropper": "^2.3.3", | ||||
| "react-dev-inspector": "^1.8.1", | "react-dev-inspector": "^1.8.1", | ||||
| "react-dom": "^18.2.0", | "react-dom": "^18.2.0", | ||||
| @@ -35,13 +35,12 @@ export async function getInitialState(): Promise<{ | |||||
| const response = await getUserInfo({ | const response = await getUserInfo({ | ||||
| skipErrorHandler: true, | skipErrorHandler: true, | ||||
| }); | }); | ||||
| response.user.avatar = | |||||
| response.user.avatar || | |||||
| 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png'; | |||||
| return { | return { | ||||
| ...response.user, | ...response.user, | ||||
| avatar: response.user.avatar || require('@/assets/img/avatar-default.png'), | |||||
| permissions: response.permissions, | permissions: response.permissions, | ||||
| roles: response.roles, | roles: response.roles, | ||||
| roleNames: response.user.roles, | |||||
| } as API.CurrentUser; | } as API.CurrentUser; | ||||
| } catch (error) { | } catch (error) { | ||||
| console.log(error); | console.log(error); | ||||
| @@ -128,7 +127,7 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => { | |||||
| // </Link>, | // </Link>, | ||||
| // ] | // ] | ||||
| // : [], | // : [], | ||||
| menuHeaderRender: undefined, | |||||
| menuHeaderRender: false, | |||||
| // 自定义 403 页面 | // 自定义 403 页面 | ||||
| // unAccessible: <div>unAccessible</div>, | // unAccessible: <div>unAccessible</div>, | ||||
| // 增加一个 loading 的状态 | // 增加一个 loading 的状态 | ||||
| @@ -138,10 +137,40 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => { | |||||
| }, | }, | ||||
| menuProps: { | menuProps: { | ||||
| onClick: () => { | onClick: () => { | ||||
| // 点击菜单项,删除所有的页面 state 缓存 | |||||
| removeAllPageCacheState(); | removeAllPageCacheState(); | ||||
| }, | }, | ||||
| // onSelect: (e) => { | |||||
| // console.log(e); | |||||
| // }, | |||||
| }, | }, | ||||
| ...initialState?.settings, | ...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) { | function CommonTableCell(text?: string | null) { | ||||
| return <span>{text ?? '--'}</span>; | return <span>{text ?? '--'}</span>; | ||||
| } | } | ||||
| @@ -1,3 +1,9 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-28 14:18:11 | |||||
| * @Description: 自定义 Table 日期类单元格 | |||||
| */ | |||||
| import dayjs from 'dayjs'; | import dayjs from 'dayjs'; | ||||
| function DateTableCell(text?: string | null) { | function DateTableCell(text?: string | null) { | ||||
| @@ -5,7 +11,7 @@ function DateTableCell(text?: string | null) { | |||||
| return <span>--</span>; | return <span>--</span>; | ||||
| } | } | ||||
| if (!dayjs(text).isValid()) { | if (!dayjs(text).isValid()) { | ||||
| return <span>日期无效</span>; | |||||
| return <span>无效的日期</span>; | |||||
| } | } | ||||
| return <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>; | return <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>; | ||||
| } | } | ||||
| @@ -1,7 +1,7 @@ | |||||
| /* | /* | ||||
| * @Author: 赵伟 | * @Author: 赵伟 | ||||
| * @Date: 2024-04-17 16:59:42 | * @Date: 2024-04-17 16:59:42 | ||||
| * @Description: 自定义Radio | |||||
| * @Description: 自定义 Radio | |||||
| */ | */ | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| @@ -1,3 +1,9 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-28 14:18:11 | |||||
| * @Description: 自定义 Modal Title | |||||
| */ | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import React from 'react'; | import React from 'react'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| @@ -1,6 +1,7 @@ | |||||
| html, | html, | ||||
| body, | body, | ||||
| #root { | #root { | ||||
| min-width: 1440px; | |||||
| height: 100%; | height: 100%; | ||||
| margin: 0; | margin: 0; | ||||
| padding: 0; | padding: 0; | ||||
| @@ -20,9 +21,6 @@ body, | |||||
| .ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed { | .ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed { | ||||
| left: unset; | left: unset; | ||||
| } | } | ||||
| .ant-layout-sider-children { | |||||
| margin-top: 60px !important; | |||||
| } | |||||
| canvas { | canvas { | ||||
| display: block; | display: block; | ||||
| } | } | ||||
| @@ -33,58 +31,29 @@ body { | |||||
| -moz-osx-font-smoothing: grayscale; | -moz-osx-font-smoothing: grayscale; | ||||
| } | } | ||||
| .ant-pro-layout .ant-pro-layout-content { | .ant-pro-layout .ant-pro-layout-content { | ||||
| padding: 10px; | |||||
| padding: 0 10px 10px; | |||||
| background-color: transparent; | |||||
| } | } | ||||
| .ant-pro-layout .ant-pro-layout-bg-list { | .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 { | .ant-pro-base-menu-inline-item-title .ant-pro-base-menu-inline-item-text { | ||||
| // color: #1d1d20; | |||||
| font-size: 16px; | font-size: 16px; | ||||
| } | } | ||||
| // .ant-menu-light .ant-menu-item-selected{ | |||||
| // color:#1664ff; | |||||
| // } | |||||
| .ant-pro-layout .ant-pro-sider-menu { | .ant-pro-layout .ant-pro-sider-menu { | ||||
| padding-top: 40px; | 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 { | .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 { | .ant-pro-layout .ant-pro-sider .ant-layout-sider-children { | ||||
| border-right: unset; | border-right: unset; | ||||
| border-bottom-right-radius: 20px; | |||||
| } | } | ||||
| .ant-pro-base-menu-inline { | .ant-pro-base-menu-inline { | ||||
| // height: 87vh; | |||||
| background: #f2f5f7; | |||||
| border-radius: 0px 20px 20px 0px; | border-radius: 0px 20px 20px 0px; | ||||
| } | } | ||||
| .ant-pro-layout .ant-pro-layout-content { | |||||
| background-color: transparent; | |||||
| } | |||||
| .ant-drawer .ant-drawer-body { | .ant-drawer .ant-drawer-body { | ||||
| padding: 0; | padding: 0; | ||||
| } | } | ||||
| @@ -102,15 +71,12 @@ body { | |||||
| padding: 20px 16px; | padding: 20px 16px; | ||||
| background-color: #fff; | background-color: #fff; | ||||
| } | } | ||||
| // .ant-table-wrapper .ant-table { | |||||
| // height: 81vh; | |||||
| // // overflow-y: auto; | |||||
| // } | |||||
| .ant-pro-global-header-logo img { | .ant-pro-global-header-logo img { | ||||
| height: 21px; | height: 21px; | ||||
| } | } | ||||
| .ant-pro-layout .ant-layout-sider.ant-pro-sider { | .ant-pro-layout .ant-layout-sider.ant-pro-sider { | ||||
| height: 94vh; | |||||
| height: 100vh; | |||||
| padding-top: 56px; | |||||
| } | } | ||||
| .ant-pro-layout .ant-pro-layout-container { | .ant-pro-layout .ant-pro-layout-container { | ||||
| height: 100vh; | height: 100vh; | ||||
| @@ -131,7 +97,7 @@ body { | |||||
| width: 110px; | width: 110px; | ||||
| height: 40px; | height: 40px; | ||||
| margin-right: 10px; | margin-right: 10px; | ||||
| // color: #1d1d20; | |||||
| // color: @text-color; | |||||
| font-size: 18px; | font-size: 18px; | ||||
| background: rgba(22, 100, 255, 0.06); | background: rgba(22, 100, 255, 0.06); | ||||
| border-color: transparent; | border-color: transparent; | ||||
| @@ -161,9 +127,6 @@ body { | |||||
| .ant-modal .ant-select-single .ant-select-selector .ant-select-selection-placeholder { | .ant-modal .ant-select-single .ant-select-selector .ant-select-selection-placeholder { | ||||
| line-height: 46px; | line-height: 46px; | ||||
| } | } | ||||
| .ant-menu-light.ant-menu-inline .ant-menu-item { | |||||
| color: #575757; | |||||
| } | |||||
| .ant-modal .ant-modal-close-x { | .ant-modal .ant-modal-close-x { | ||||
| width: 26px; | width: 26px; | ||||
| height: 26px; | height: 26px; | ||||
| @@ -41,7 +41,7 @@ export function useVisible(initialValue: boolean) { | |||||
| setVisible(false); | setVisible(false); | ||||
| }, []); | }, []); | ||||
| return [visible, open, close]; | |||||
| return [visible, open, close] as const; | |||||
| } | } | ||||
| type Callback<T> = (state: T) => void; | type Callback<T> = (state: T) => void; | ||||
| @@ -92,7 +92,7 @@ export function useDomSize<T extends HTMLElement>( | |||||
| setWidth(domRef.current.offsetWidth); | setWidth(domRef.current.offsetWidth); | ||||
| } | } | ||||
| }; | }; | ||||
| const debounceFunc = debounce(setDomHeight, 200); | |||||
| const debounceFunc = debounce(setDomHeight, 100); | |||||
| setDomHeight(); | setDomHeight(); | ||||
| window.addEventListener('resize', debounceFunc); | window.addEventListener('resize', debounceFunc); | ||||
| @@ -52,3 +52,7 @@ | |||||
| .ant-table-wrapper .ant-table-thead > tr > td { | .ant-table-wrapper .ant-table-thead > tr > td { | ||||
| background-color: #fff; | background-color: #fff; | ||||
| } | } | ||||
| .ant-pro-page-container { | |||||
| overflow-y: auto; | |||||
| } | |||||
| @@ -117,14 +117,14 @@ const PublicData = (React.FC = () => { | |||||
| }; | }; | ||||
| const chooseDatasetType = (val, item) => { | const chooseDatasetType = (val, item) => { | ||||
| console.log(val, item); | console.log(val, item); | ||||
| if (item.path == queryFlow.data_type) { | |||||
| if (item.id == queryFlow.data_type) { | |||||
| setActiveType(''); | setActiveType(''); | ||||
| setQueryFlow({ ...queryFlow, data_type: null }); | setQueryFlow({ ...queryFlow, data_type: null }); | ||||
| getDatasetlist({ ...queryFlow, data_type: null }); | getDatasetlist({ ...queryFlow, data_type: null }); | ||||
| } else { | } 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},()=>{ | // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | ||||
| // getDatasetlist() | // getDatasetlist() | ||||
| @@ -132,14 +132,14 @@ const PublicData = (React.FC = () => { | |||||
| }; | }; | ||||
| const chooseDatasetTag = (val, item) => { | const chooseDatasetTag = (val, item) => { | ||||
| console.log(val, item); | console.log(val, item); | ||||
| if (item.path == queryFlow.data_tag) { | |||||
| if (item.id == queryFlow.data_tag) { | |||||
| setActiveTag(''); | setActiveTag(''); | ||||
| setQueryFlow({ ...queryFlow, data_tag: null }); | setQueryFlow({ ...queryFlow, data_tag: null }); | ||||
| getDatasetlist({ ...queryFlow, data_tag: null }); | getDatasetlist({ ...queryFlow, data_tag: null }); | ||||
| } else { | } 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},()=>{ | // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | ||||
| // getDatasetlist() | // getDatasetlist() | ||||
| @@ -196,7 +196,7 @@ const PublicData = (React.FC = () => { | |||||
| <div | <div | ||||
| className={[ | className={[ | ||||
| Styles.messageBox, | Styles.messageBox, | ||||
| item.path === activeType ? Styles.active : null, | |||||
| item.id === activeType ? Styles.active : null, | |||||
| ].join(' ')} | ].join(' ')} | ||||
| onClick={(e) => { | onClick={(e) => { | ||||
| chooseDatasetType(e, item); | chooseDatasetType(e, item); | ||||
| @@ -230,7 +230,7 @@ const PublicData = (React.FC = () => { | |||||
| <div | <div | ||||
| className={[ | className={[ | ||||
| Styles.messageBox, | Styles.messageBox, | ||||
| item.path === activeTag ? Styles.active : null, | |||||
| item.id === activeTag ? Styles.active : null, | |||||
| ].join(' ')} | ].join(' ')} | ||||
| onClick={(e) => { | onClick={(e) => { | ||||
| chooseDatasetTag(e, item); | chooseDatasetTag(e, item); | ||||
| @@ -76,14 +76,14 @@ const PublicData = () => { | |||||
| }; | }; | ||||
| const chooseDatasetType = (val, item) => { | const chooseDatasetType = (val, item) => { | ||||
| console.log(val, item); | console.log(val, item); | ||||
| if (item.path == queryFlow.data_type) { | |||||
| if (item.id == queryFlow.data_type) { | |||||
| setActiveType(''); | setActiveType(''); | ||||
| setQueryFlow({ ...queryFlow, data_type: null }); | setQueryFlow({ ...queryFlow, data_type: null }); | ||||
| getDatasetlist({ ...queryFlow, data_type: null }); | getDatasetlist({ ...queryFlow, data_type: null }); | ||||
| } else { | } 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},()=>{ | // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | ||||
| // getDatasetlist() | // getDatasetlist() | ||||
| @@ -91,14 +91,14 @@ const PublicData = () => { | |||||
| }; | }; | ||||
| const chooseDatasetTag = (val, item) => { | const chooseDatasetTag = (val, item) => { | ||||
| console.log(val, item); | console.log(val, item); | ||||
| if (item.path == queryFlow.data_tag) { | |||||
| if (item.id == queryFlow.data_tag) { | |||||
| setActiveTag(''); | setActiveTag(''); | ||||
| setQueryFlow({ ...queryFlow, data_tag: null }); | setQueryFlow({ ...queryFlow, data_tag: null }); | ||||
| getDatasetlist({ ...queryFlow, data_tag: null }); | getDatasetlist({ ...queryFlow, data_tag: null }); | ||||
| } else { | } 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},()=>{ | // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | ||||
| // getDatasetlist() | // getDatasetlist() | ||||
| @@ -146,7 +146,7 @@ const PublicData = () => { | |||||
| <div | <div | ||||
| className={[ | className={[ | ||||
| Styles.messageBox, | Styles.messageBox, | ||||
| item.path === activeType ? Styles.active : null, | |||||
| item.id === activeType ? Styles.active : null, | |||||
| ].join(' ')} | ].join(' ')} | ||||
| onClick={(e) => { | onClick={(e) => { | ||||
| chooseDatasetType(e, item); | chooseDatasetType(e, item); | ||||
| @@ -180,7 +180,7 @@ const PublicData = () => { | |||||
| <div | <div | ||||
| className={[ | className={[ | ||||
| Styles.messageBox, | Styles.messageBox, | ||||
| item.path === activeTag ? Styles.active : null, | |||||
| item.id === activeTag ? Styles.active : null, | |||||
| ].join(' ')} | ].join(' ')} | ||||
| onClick={(e) => { | onClick={(e) => { | ||||
| chooseDatasetTag(e, item); | 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; | align-items: center; | ||||
| width: 100%; | width: 100%; | ||||
| padding: 0 0 0 33px; | padding: 0 0 0 33px; | ||||
| color: #1d1d20; | |||||
| color: @text-color; | |||||
| font-size: 15px; | font-size: 15px; | ||||
| & > div { | & > div { | ||||
| @@ -76,16 +76,18 @@ | |||||
| .statusBox:hover .statusIcon { | .statusBox:hover .statusIcon { | ||||
| visibility: visible; | visibility: visible; | ||||
| } | } | ||||
| .experimentBox{ | |||||
| .experimentBox { | |||||
| height: calc(100% - 20px); | height: calc(100% - 20px); | ||||
| .experimentTable{ | |||||
| .experimentTable { | |||||
| height: calc(100% - 60px); | height: calc(100% - 60px); | ||||
| :global{ | |||||
| .ant-table-wrapper .ant-table{ | |||||
| :global { | |||||
| .ant-table-wrapper .ant-table { | |||||
| // overflow-y: auto; | // overflow-y: auto; | ||||
| height: calc(100% - 48px); | 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 ExperimentStatusKeys = keyof typeof ExperimentStatus; | ||||
| type ExperimentStatusValues = (typeof ExperimentStatus)[ExperimentStatusKeys]; | |||||
| export type ExperimentStatusValues = (typeof ExperimentStatus)[ExperimentStatusKeys]; | |||||
| export const experimentStatusInfo: Record<ExperimentStatusValues, StatusInfo | undefined> = { | export const experimentStatusInfo: Record<ExperimentStatusValues, StatusInfo | undefined> = { | ||||
| Running: { | Running: { | ||||
| @@ -121,14 +121,14 @@ const PublicData = () => { | |||||
| const chooseModelType = (val, item) => { | const chooseModelType = (val, item) => { | ||||
| console.log(val, item); | console.log(val, item); | ||||
| if (item.path == queryFlow.model_type) { | |||||
| if (item.id == queryFlow.model_type) { | |||||
| setActiveType(''); | setActiveType(''); | ||||
| setQueryFlow({ ...queryFlow, model_type: null }); | setQueryFlow({ ...queryFlow, model_type: null }); | ||||
| getModelLists({ ...queryFlow, model_type: null }); | getModelLists({ ...queryFlow, model_type: null }); | ||||
| } else { | } 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},()=>{ | // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | ||||
| @@ -136,14 +136,14 @@ const PublicData = () => { | |||||
| // }) | // }) | ||||
| }; | }; | ||||
| const chooseModelTag = (val, item) => { | const chooseModelTag = (val, item) => { | ||||
| if (item.path == queryFlow.model_tag) { | |||||
| if (item.id == queryFlow.model_tag) { | |||||
| setActiveTag(''); | setActiveTag(''); | ||||
| setQueryFlow({ ...queryFlow, model_tag: null }); | setQueryFlow({ ...queryFlow, model_tag: null }); | ||||
| getModelLists({ ...queryFlow, model_tag: null }); | getModelLists({ ...queryFlow, model_tag: null }); | ||||
| } else { | } 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},()=>{ | // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | ||||
| // getDatasetlist() | // getDatasetlist() | ||||
| @@ -190,7 +190,7 @@ const PublicData = () => { | |||||
| <div | <div | ||||
| className={[ | className={[ | ||||
| Styles.messageBox, | Styles.messageBox, | ||||
| item.path === activeType ? Styles.active : null, | |||||
| item.id === activeType ? Styles.active : null, | |||||
| ].join(' ')} | ].join(' ')} | ||||
| onClick={(e) => { | onClick={(e) => { | ||||
| chooseModelType(e, item); | chooseModelType(e, item); | ||||
| @@ -231,7 +231,7 @@ const PublicData = () => { | |||||
| <div | <div | ||||
| className={[ | className={[ | ||||
| Styles.messageBox, | Styles.messageBox, | ||||
| item.path === activeTag ? Styles.active : null, | |||||
| item.id === activeTag ? Styles.active : null, | |||||
| ].join(' ')} | ].join(' ')} | ||||
| onClick={(e) => { | onClick={(e) => { | ||||
| chooseModelTag(e, item); | chooseModelTag(e, item); | ||||
| @@ -77,14 +77,14 @@ const PublicData = () => { | |||||
| }; | }; | ||||
| const chooseModelType = (val, item) => { | const chooseModelType = (val, item) => { | ||||
| console.log(val, item); | console.log(val, item); | ||||
| if (item.path == queryFlow.model_type) { | |||||
| if (item.id == queryFlow.model_type) { | |||||
| setActiveType(''); | setActiveType(''); | ||||
| setQueryFlow({ ...queryFlow, model_type: null }); | setQueryFlow({ ...queryFlow, model_type: null }); | ||||
| getModelLists({ ...queryFlow, model_type: null }); | getModelLists({ ...queryFlow, model_type: null }); | ||||
| } else { | } 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},()=>{ | // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | ||||
| @@ -92,14 +92,14 @@ const PublicData = () => { | |||||
| // }) | // }) | ||||
| }; | }; | ||||
| const chooseModelTag = (val, item) => { | const chooseModelTag = (val, item) => { | ||||
| if (item.path == queryFlow.model_tag) { | |||||
| if (item.id == queryFlow.model_tag) { | |||||
| setActiveTag(''); | setActiveTag(''); | ||||
| setQueryFlow({ ...queryFlow, model_tag: null }); | setQueryFlow({ ...queryFlow, model_tag: null }); | ||||
| getModelLists({ ...queryFlow, model_tag: null }); | getModelLists({ ...queryFlow, model_tag: null }); | ||||
| } else { | } 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},()=>{ | // setQueryFlow({...queryFlow,data_type:item.path},()=>{ | ||||
| // getDatasetlist() | // getDatasetlist() | ||||
| @@ -147,7 +147,7 @@ const PublicData = () => { | |||||
| <div | <div | ||||
| className={[ | className={[ | ||||
| Styles.messageBox, | Styles.messageBox, | ||||
| item.path === activeType ? Styles.active : null, | |||||
| item.id === activeType ? Styles.active : null, | |||||
| ].join(' ')} | ].join(' ')} | ||||
| onClick={(e) => { | onClick={(e) => { | ||||
| chooseModelType(e, item); | chooseModelType(e, item); | ||||
| @@ -181,7 +181,7 @@ const PublicData = () => { | |||||
| <div | <div | ||||
| className={[ | className={[ | ||||
| Styles.messageBox, | Styles.messageBox, | ||||
| item.path === activeTag ? Styles.active : null, | |||||
| item.id === activeTag ? Styles.active : null, | |||||
| ].join(' ')} | ].join(' ')} | ||||
| onClick={(e) => { | onClick={(e) => { | ||||
| chooseModelTag(e, item); | chooseModelTag(e, item); | ||||
| @@ -22,7 +22,7 @@ | |||||
| height: 398px; | height: 398px; | ||||
| margin-right: 15px; | margin-right: 15px; | ||||
| padding: 15px; | padding: 15px; | ||||
| background-color: @background-color-primay; | |||||
| background-color: @background-color-primary; | |||||
| border: 1px solid @border-color; | border: 1px solid @border-color; | ||||
| border-radius: 8px; | border-radius: 8px; | ||||
| @@ -31,7 +31,7 @@ | |||||
| padding-left: 0; | padding-left: 0; | ||||
| background-color: transparent; | background-color: transparent; | ||||
| border-width: 0; | border-width: 0; | ||||
| border-bottom: 1px solid @border-color-second; | |||||
| border-bottom: 1px solid @border-color-secondary; | |||||
| border-radius: 0; | border-radius: 0; | ||||
| } | } | ||||
| } | } | ||||
| @@ -40,7 +40,7 @@ | |||||
| width: calc(100% - 488px - 15px); | width: calc(100% - 488px - 15px); | ||||
| height: 398px; | height: 398px; | ||||
| padding: 15px; | padding: 15px; | ||||
| background-color: @background-color-primay; | |||||
| background-color: @background-color-primary; | |||||
| border: 1px solid @border-color; | border: 1px solid @border-color; | ||||
| border-radius: 8px; | border-radius: 8px; | ||||
| @@ -49,7 +49,7 @@ | |||||
| padding: 3px 0 6px; | padding: 3px 0 6px; | ||||
| color: @text-color; | color: @text-color; | ||||
| font-size: @font-size; | font-size: @font-size; | ||||
| border-bottom: 1px solid @border-color-second; | |||||
| border-bottom: 1px solid @border-color-secondary; | |||||
| } | } | ||||
| &__files { | &__files { | ||||
| height: calc(100% - 75px); | 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: [ | responseInterceptors: [ | ||||
| (response: any) => { | (response: any) => { | ||||
| const { status, data } = response; | |||||
| const { status, data } = response || {}; | |||||
| console.log('response2', response); | |||||
| if (status >= 200 && status < 300) { | if (status >= 200 && status < 300) { | ||||
| if (data && (data instanceof Blob || data.code === 200)) { | if (data && (data instanceof Blob || data.code === 200)) { | ||||
| return response; | return response; | ||||
| @@ -14,6 +14,9 @@ declare namespace API { | |||||
| }; | }; | ||||
| address?: string; | address?: string; | ||||
| phone?: string; | phone?: string; | ||||
| roleNames?: { | |||||
| roleName?: string; | |||||
| }[]; | |||||
| }; | }; | ||||
| type ErrorResponse = { | type ErrorResponse = { | ||||
| @@ -1,7 +1,7 @@ | |||||
| /* | /* | ||||
| * @Author: 赵伟 | * @Author: 赵伟 | ||||
| * @Date: 2024-04-16 14:29:44 | * @Date: 2024-04-16 14:29:44 | ||||
| * @Description: | |||||
| * @Description: 镜像管理接口 | |||||
| */ | */ | ||||
| import { request } from '@umijs/max'; | 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: #1664ff; // 主色调 | ||||
| @primary-color-secondary: #4e89ff; | |||||
| @primary-color-hover: #69b1ff; | @primary-color-hover: #69b1ff; | ||||
| @background-color: #f9fafb; // 页面背景颜色 | @background-color: #f9fafb; // 页面背景颜色 | ||||
| @text-color: #1d1d20; | @text-color: #1d1d20; | ||||
| @@ -15,17 +16,35 @@ | |||||
| @warning-color: #f98e1b; | @warning-color: #f98e1b; | ||||
| @border-color: rgba(22, 100, 255, 0.3); | @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); | @background-color-gray: rgba(4, 3, 3, 0.06); | ||||
| @heading-color: rgba(0, 0, 0, 0.85); | @heading-color: rgba(0, 0, 0, 0.85); | ||||
| @input-icon-hover-color: rgba(0, 0, 0, 0.85); | @input-icon-hover-color: rgba(0, 0, 0, 0.85); | ||||
| @border-color-base: #d9d9d9; | @border-color-base: #d9d9d9; | ||||
| @link-hover-color: #69b1ff; | @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: 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 { | :export { | ||||
| @@ -34,5 +53,7 @@ | |||||
| errorColor: @error-color; | errorColor: @error-color; | ||||
| warningColor: @warning-color; | warningColor: @warning-color; | ||||
| textColor: @text-color; | textColor: @text-color; | ||||
| textColorSecondary: @text-color-secondary; | |||||
| fontSize: @font-size; | fontSize: @font-size; | ||||
| siderBGColor: @sider-background-color; | |||||
| } | } | ||||
| @@ -12,3 +12,19 @@ export type PipelineGlobalParam = { | |||||
| param_value: number | string | boolean; | param_value: number | string | boolean; | ||||
| is_sensitive: number; | 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; | 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 { pathname, search } = window.location; | ||||
| const urlParams = new URLSearchParams(); | const urlParams = new URLSearchParams(); | ||||
| urlParams.append('redirect', pathname + search); | 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) { | if (window.location.pathname !== PageEnum.LOGIN) { | ||||
| history.replace({ | history.replace({ | ||||
| pathname: PageEnum.LOGIN, | pathname: PageEnum.LOGIN, | ||||