| @@ -29,6 +29,8 @@ export async function getInitialState(): Promise<{ | |||||
| loading?: boolean; | loading?: boolean; | ||||
| fetchUserInfo?: () => Promise<API.CurrentUser | undefined>; | fetchUserInfo?: () => Promise<API.CurrentUser | undefined>; | ||||
| }> { | }> { | ||||
| console.log('getInitialState'); | |||||
| const fetchUserInfo = async () => { | const fetchUserInfo = async () => { | ||||
| try { | try { | ||||
| const response = await getUserInfo({ | const response = await getUserInfo({ | ||||
| @@ -133,22 +135,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => { | |||||
| // 增加一个 loading 的状态 | // 增加一个 loading 的状态 | ||||
| childrenRender: (children) => { | childrenRender: (children) => { | ||||
| // if (initialState?.loading) return <PageLoading />; | // if (initialState?.loading) return <PageLoading />; | ||||
| return ( | |||||
| <> | |||||
| {children} | |||||
| {/* <SettingDrawer | |||||
| disableUrlParams | |||||
| enableDarkTheme | |||||
| settings={initialState?.settings} | |||||
| onSettingChange={(settings) => { | |||||
| setInitialState((preInitialState) => ({ | |||||
| ...preInitialState, | |||||
| settings, | |||||
| })); | |||||
| }} | |||||
| /> */} | |||||
| </> | |||||
| ); | |||||
| return <>{children}</>; | |||||
| }, | }, | ||||
| ...initialState?.settings, | ...initialState?.settings, | ||||
| }; | }; | ||||
| @@ -17,13 +17,8 @@ const HeaderDropdown: React.FC<HeaderDropdownProps> = ({ overlayClassName: cls, | |||||
| }, | }, | ||||
| }; | }; | ||||
| }); | }); | ||||
| return ( | |||||
| <Dropdown | |||||
| overlayClassName={classNames(className, cls)} | |||||
| getPopupContainer={(target) => target.parentElement || document.body} | |||||
| {...restProps} | |||||
| /> | |||||
| ); | |||||
| return <Dropdown overlayClassName={classNames(className, cls)} {...restProps} />; | |||||
| }; | }; | ||||
| export default HeaderDropdown; | export default HeaderDropdown; | ||||
| @@ -1,13 +1,12 @@ | |||||
| import { clearSessionToken } from '@/access'; | import { clearSessionToken } from '@/access'; | ||||
| import { PageEnum } from '@/enums/pagesEnums'; | |||||
| import { setRemoteMenu } from '@/services/session'; | import { setRemoteMenu } from '@/services/session'; | ||||
| import { logout } from '@/services/system/auth'; | import { logout } from '@/services/system/auth'; | ||||
| import { gotoLoginPage } from '@/utils/ui'; | |||||
| import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons'; | import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons'; | ||||
| import { setAlpha } from '@ant-design/pro-components'; | import { setAlpha } from '@ant-design/pro-components'; | ||||
| import { useEmotionCss } from '@ant-design/use-emotion-css'; | import { useEmotionCss } from '@ant-design/use-emotion-css'; | ||||
| import { history, useModel } from '@umijs/max'; | import { history, useModel } from '@umijs/max'; | ||||
| import { Avatar, Spin } from 'antd'; | import { Avatar, Spin } from 'antd'; | ||||
| import { stringify } from 'querystring'; | |||||
| import type { MenuInfo } from 'rc-menu/lib/interface'; | import type { MenuInfo } from 'rc-menu/lib/interface'; | ||||
| import React, { useCallback } from 'react'; | import React, { useCallback } from 'react'; | ||||
| import { flushSync } from 'react-dom'; | import { flushSync } from 'react-dom'; | ||||
| @@ -23,7 +22,7 @@ const Name = () => { | |||||
| const nameClassName = useEmotionCss(({ token }) => { | const nameClassName = useEmotionCss(({ token }) => { | ||||
| return { | return { | ||||
| width: '70px', | |||||
| // width: '70px', | |||||
| height: '48px', | height: '48px', | ||||
| overflow: 'hidden', | overflow: 'hidden', | ||||
| lineHeight: '48px', | lineHeight: '48px', | ||||
| @@ -64,19 +63,7 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => { | |||||
| await logout(); | await logout(); | ||||
| clearSessionToken(); | clearSessionToken(); | ||||
| setRemoteMenu(null); | setRemoteMenu(null); | ||||
| const { search, pathname } = window.location; | |||||
| const urlParams = new URL(window.location.href).searchParams; | |||||
| /** 此方法会跳转到 redirect 参数所在的位置 */ | |||||
| const redirect = urlParams.get('redirect'); | |||||
| // Note: There may be security issues, please note | |||||
| if (window.location.pathname !== PageEnum.LOGIN && !redirect) { | |||||
| history.replace({ | |||||
| pathname: PageEnum.LOGIN, | |||||
| search: stringify({ | |||||
| redirect: pathname + search, | |||||
| }), | |||||
| }); | |||||
| } | |||||
| gotoLoginPage(); | |||||
| }; | }; | ||||
| const actionClassName = useEmotionCss(({ token }) => { | const actionClassName = useEmotionCss(({ token }) => { | ||||
| return { | return { | ||||
| @@ -1,6 +1,5 @@ | |||||
| import { QuestionCircleOutlined } from '@ant-design/icons'; | |||||
| import { useEmotionCss } from '@ant-design/use-emotion-css'; | import { useEmotionCss } from '@ant-design/use-emotion-css'; | ||||
| import { SelectLang, useModel } from '@umijs/max'; | |||||
| import { useModel } from '@umijs/max'; | |||||
| import React from 'react'; | import React from 'react'; | ||||
| import Avatar from './AvatarDropdown'; | import Avatar from './AvatarDropdown'; | ||||
| @@ -17,21 +16,21 @@ const GlobalHeaderRight: React.FC = () => { | |||||
| }; | }; | ||||
| }); | }); | ||||
| const actionClassName = useEmotionCss(({ token }) => { | |||||
| return { | |||||
| display: 'flex', | |||||
| float: 'right', | |||||
| height: '48px', | |||||
| marginLeft: 'auto', | |||||
| overflow: 'hidden', | |||||
| cursor: 'pointer', | |||||
| padding: '0 12px', | |||||
| borderRadius: token.borderRadius, | |||||
| '&:hover': { | |||||
| backgroundColor: token.colorBgTextHover, | |||||
| }, | |||||
| }; | |||||
| }); | |||||
| // const actionClassName = useEmotionCss(({ token }) => { | |||||
| // return { | |||||
| // display: 'flex', | |||||
| // float: 'right', | |||||
| // height: '48px', | |||||
| // marginLeft: 'auto', | |||||
| // overflow: 'hidden', | |||||
| // cursor: 'pointer', | |||||
| // padding: '0 12px', | |||||
| // borderRadius: token.borderRadius, | |||||
| // '&:hover': { | |||||
| // backgroundColor: token.colorBgTextHover, | |||||
| // }, | |||||
| // }; | |||||
| // }); | |||||
| const { initialState } = useModel('@@initialState'); | const { initialState } = useModel('@@initialState'); | ||||
| @@ -41,15 +40,15 @@ const GlobalHeaderRight: React.FC = () => { | |||||
| return ( | return ( | ||||
| <div className={className}> | <div className={className}> | ||||
| <span | |||||
| {/* <span | |||||
| className={actionClassName} | className={actionClassName} | ||||
| onClick={() => { | onClick={() => { | ||||
| window.open('https://pro.ant.design/docs/getting-started'); | window.open('https://pro.ant.design/docs/getting-started'); | ||||
| }} | }} | ||||
| > | > | ||||
| <QuestionCircleOutlined /> | <QuestionCircleOutlined /> | ||||
| </span> | |||||
| <Avatar menu={true} /> | |||||
| </span> */} | |||||
| <Avatar menu={false} /> | |||||
| {/* <SelectLang className={actionClassName} /> */} | {/* <SelectLang className={actionClassName} /> */} | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -1,3 +1,9 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-28 08:47:43 | |||||
| * @Description: 覆盖 antd 样式 | |||||
| */ | |||||
| // 设置 Table 可以滑动 | // 设置 Table 可以滑动 | ||||
| .vertical-scroll-table { | .vertical-scroll-table { | ||||
| .ant-table-wrapper { | .ant-table-wrapper { | ||||
| @@ -9,7 +15,7 @@ | |||||
| height: 100%; | height: 100%; | ||||
| .ant-table { | .ant-table { | ||||
| height: calc(100% - 74px); | |||||
| height: calc(100% - 74px); // 分页控件的高度 | |||||
| .ant-table-container { | .ant-table-container { | ||||
| height: 100%; | height: 100%; | ||||
| @@ -224,8 +224,8 @@ const Dataset = () => { | |||||
| <span style={{ color: '#1d1d20', fontSize: '20px' }}>{datasetDetailObj.name}</span> | <span style={{ color: '#1d1d20', fontSize: '20px' }}>{datasetDetailObj.name}</span> | ||||
| <div className={Styles.smallTagBox}> | <div className={Styles.smallTagBox}> | ||||
| <div className={Styles.tagItem}>数据集 id:{datasetDetailObj.id}</div> | <div className={Styles.tagItem}>数据集 id:{datasetDetailObj.id}</div> | ||||
| <div className={Styles.tagItem}>{datasetDetailObj.data_tag || '...'}</div> | |||||
| <div className={Styles.tagItem}>{datasetDetailObj.data_type}</div> | |||||
| <div className={Styles.tagItem}>{datasetDetailObj.dataset_type_name || '...'}</div> | |||||
| <div className={Styles.tagItem}>{datasetDetailObj.dataset_tag_name || '...'}</div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className={Styles.datasetIntroCneterBox}> | <div className={Styles.datasetIntroCneterBox}> | ||||
| @@ -229,6 +229,11 @@ | |||||
| border-color: #eaeaea; | border-color: #eaeaea; | ||||
| border-radius: 4px; | border-radius: 4px; | ||||
| cursor: pointer; | cursor: pointer; | ||||
| .dropdown{ | |||||
| position: absolute; | |||||
| right: 20px; | |||||
| top: 15px; | |||||
| } | |||||
| .itemText { | .itemText { | ||||
| position: absolute; | position: absolute; | ||||
| top: 20px; | top: 20px; | ||||
| @@ -1,14 +1,22 @@ | |||||
| import { getAccessToken } from '@/access'; | import { getAccessToken } from '@/access'; | ||||
| import clock from '@/assets/img/clock.png'; | import clock from '@/assets/img/clock.png'; | ||||
| import creatByImg from '@/assets/img/creatBy.png'; | import creatByImg from '@/assets/img/creatBy.png'; | ||||
| import deleteIcon from '@/assets/img/delete-icon.png'; | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { addDatesetAndVesion, getAssetIcon, getDatasetList } from '@/services/dataset/index.js'; | |||||
| import { | |||||
| addDatesetAndVesion, | |||||
| deleteDataset, | |||||
| getAssetIcon, | |||||
| getDatasetList, | |||||
| } from '@/services/dataset/index.js'; | |||||
| import { getDictSelectOption } from '@/services/system/dict'; | import { getDictSelectOption } from '@/services/system/dict'; | ||||
| import { modalConfirm } from '@/utils/ui'; | |||||
| import { UploadOutlined } from '@ant-design/icons'; | import { UploadOutlined } from '@ant-design/icons'; | ||||
| import { Button, Form, Input, Modal, Pagination, Radio, Select, Upload } from 'antd'; | import { Button, Form, Input, Modal, Pagination, Radio, Select, Upload } from 'antd'; | ||||
| import moment from 'moment'; | import moment from 'moment'; | ||||
| import React, { useEffect, useState } from 'react'; | import React, { useEffect, useState } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||
| import './index.less'; | import './index.less'; | ||||
| import Styles from './index.less'; | import Styles from './index.less'; | ||||
| const { Search } = Input; | const { Search } = Input; | ||||
| @@ -277,7 +285,30 @@ const PublicData = (React.FC = () => { | |||||
| return ( | return ( | ||||
| <div className={Styles.dataItem} onClick={(e) => routeToIntro(e, item)}> | <div className={Styles.dataItem} onClick={(e) => routeToIntro(e, item)}> | ||||
| <span className={Styles.itemText}>{item.name}</span> | <span className={Styles.itemText}>{item.name}</span> | ||||
| <img | |||||
| onClick={(e) => { | |||||
| e.stopPropagation(); | |||||
| modalConfirm({ | |||||
| title: '确定删除该条数据集实例吗?', | |||||
| onOk: () => { | |||||
| deleteDataset(item.id).then((ret) => { | |||||
| if (ret.code === 200) { | |||||
| message.success('删除成功'); | |||||
| getModelLists(queryFlow); | |||||
| } else { | |||||
| message.error(ret.msg); | |||||
| } | |||||
| }); | |||||
| }, | |||||
| }); | |||||
| }} | |||||
| className={Styles.dropdown} | |||||
| style={{ width: '17px', marginRight: '6px' }} | |||||
| src={deleteIcon} | |||||
| alt="" | |||||
| /> | |||||
| <div className={Styles.itemDescripition}>{item.description}</div> | <div className={Styles.itemDescripition}>{item.description}</div> | ||||
| <div className={Styles.itemTime}> | <div className={Styles.itemTime}> | ||||
| <img | <img | ||||
| style={{ width: '17px', marginRight: '6px' }} | style={{ width: '17px', marginRight: '6px' }} | ||||
| @@ -1,6 +1,8 @@ | |||||
| import clock from '@/assets/img/clock.png'; | import clock from '@/assets/img/clock.png'; | ||||
| import creatByImg from '@/assets/img/creatBy.png'; | import creatByImg from '@/assets/img/creatBy.png'; | ||||
| import { getAssetIcon, getDatasetList } from '@/services/dataset/index.js'; | |||||
| import deleteIcon from '@/assets/img/delete-icon.png'; | |||||
| import { deleteDataset, getAssetIcon, getDatasetList } from '@/services/dataset/index.js'; | |||||
| import { modalConfirm } from '@/utils/ui'; | |||||
| import { Form, Input, Pagination } from 'antd'; | import { Form, Input, Pagination } from 'antd'; | ||||
| import moment from 'moment'; | import moment from 'moment'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| @@ -225,6 +227,28 @@ const PublicData = () => { | |||||
| return ( | return ( | ||||
| <div className={Styles.dataItem} onClick={(e) => routeToIntro(e, item)}> | <div className={Styles.dataItem} onClick={(e) => routeToIntro(e, item)}> | ||||
| <span className={Styles.itemText}>{item.name}</span> | <span className={Styles.itemText}>{item.name}</span> | ||||
| <img | |||||
| onClick={(e) => { | |||||
| e.stopPropagation(); | |||||
| modalConfirm({ | |||||
| title: '确定删除该条数据集实例吗?', | |||||
| onOk: () => { | |||||
| deleteDataset(item.id).then((ret) => { | |||||
| if (ret.code === 200) { | |||||
| message.success('删除成功'); | |||||
| getModelLists(queryFlow); | |||||
| } else { | |||||
| message.error(ret.msg); | |||||
| } | |||||
| }); | |||||
| }, | |||||
| }); | |||||
| }} | |||||
| className={Styles.dropdown} | |||||
| style={{ width: '17px', marginRight: '6px' }} | |||||
| src={deleteIcon} | |||||
| alt="" | |||||
| /> | |||||
| <div className={Styles.itemDescripition}>{item.description}</div> | <div className={Styles.itemDescripition}>{item.description}</div> | ||||
| <div className={Styles.itemTime}> | <div className={Styles.itemTime}> | ||||
| <img | <img | ||||
| @@ -144,8 +144,15 @@ function AddExperimentModal({ | |||||
| label="实验描述" | label="实验描述" | ||||
| name="description" | name="description" | ||||
| rules={[{ required: true, message: '请输入实验描述' }]} | rules={[{ required: true, message: '请输入实验描述' }]} | ||||
| style={{ marginBottom: '48px' }} | |||||
| > | > | ||||
| <Input placeholder="请输入实验描述" maxLength={128} showCount allowClear /> | |||||
| <Input.TextArea | |||||
| placeholder="请输入实验描述" | |||||
| maxLength={128} | |||||
| autoSize={{ minRows: 2, maxRows: 5 }} | |||||
| showCount | |||||
| allowClear | |||||
| /> | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | <Form.Item | ||||
| label="选择流水线" | label="选择流水线" | ||||
| @@ -68,19 +68,9 @@ function ExperimentText() { | |||||
| }; | }; | ||||
| const formChange = (val) => {}; | const formChange = (val) => {}; | ||||
| const handlerClick = (e) => { | const handlerClick = (e) => { | ||||
| console.log(propsRef, graph, messageRef.current); | |||||
| // let cache = []; | |||||
| // let json_str = JSON.stringify(graph, function(key, value) { | |||||
| // if (typeof value === 'object' && value !== null) { | |||||
| // if (cache.indexOf(value) !== -1) { | |||||
| // return; | |||||
| // } | |||||
| // cache.push(value); | |||||
| // } | |||||
| // return value; | |||||
| // }); | |||||
| // console.log(json_str); | |||||
| propsRef.current.showDrawer(e, locationParams.id, messageRef.current); | |||||
| if (e.target.get('name') !== 'anchor-point' && e.item) { | |||||
| propsRef.current.showDrawer(e, locationParams.id, messageRef.current); | |||||
| } | |||||
| }; | }; | ||||
| const getGraphData = (data) => { | const getGraphData = (data) => { | ||||
| if (graph) { | if (graph) { | ||||
| @@ -387,8 +377,7 @@ function ExperimentText() { | |||||
| fitView: true, | fitView: true, | ||||
| fitViewPadding: [320, 320, 220, 320], | fitViewPadding: [320, 320, 220, 320], | ||||
| }); | }); | ||||
| graph.on('dblclick', handlerClick); | |||||
| graph.on('node:click', handlerClick); | |||||
| window.onresize = () => { | window.onresize = () => { | ||||
| if (!graph || graph.get('destroyed')) return; | if (!graph || graph.get('destroyed')) return; | ||||
| if (!graphRef.current || !graphRef.current.scrollWidth || !graphRef.current.scrollHeight) | if (!graphRef.current || !graphRef.current.scrollWidth || !graphRef.current.scrollHeight) | ||||
| @@ -29,12 +29,37 @@ | |||||
| background: #ffffff; | background: #ffffff; | ||||
| box-shadow: 0px 3px 6px rgba(146, 146, 146, 0.09); | box-shadow: 0px 3px 6px rgba(146, 146, 146, 0.09); | ||||
| } | } | ||||
| .drawBox{ | |||||
| :global{ | |||||
| .ant-drawer .ant-drawer-body{ | |||||
| overflow: hidden; | |||||
| } | |||||
| } | |||||
| } | |||||
| .experimentDrawer{ | |||||
| :global{ | |||||
| .ant-tabs >.ant-tabs-nav .ant-tabs-nav-list{ | |||||
| margin-left: 24px; | |||||
| } | |||||
| .ant-drawer .ant-drawer-body{ | |||||
| overflow-y: hidden; | |||||
| } | |||||
| .ant-tabs { | |||||
| height: calc(100% - 160px); | |||||
| overflow-y: auto; | |||||
| } | |||||
| } | |||||
| } | |||||
| .detailBox { | .detailBox { | ||||
| display: flex; | display: flex; | ||||
| align-items: center; | align-items: center; | ||||
| margin-bottom: 15px; | margin-bottom: 15px; | ||||
| color: #1d1d20; | color: #1d1d20; | ||||
| font-size: 15px; | font-size: 15px; | ||||
| padding-left: 24px; | |||||
| } | } | ||||
| .allMessageItem { | .allMessageItem { | ||||
| display: flex; | display: flex; | ||||
| @@ -12,7 +12,7 @@ import styles from './paramsModal.less'; | |||||
| type ParamsModalProps = { | type ParamsModalProps = { | ||||
| open: boolean; | open: boolean; | ||||
| onCancel: () => void; | onCancel: () => void; | ||||
| globalParam?: PipelineGlobalParam[]; | |||||
| globalParam?: PipelineGlobalParam[] | null; | |||||
| }; | }; | ||||
| function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) { | function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) { | ||||
| @@ -26,7 +26,7 @@ function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) { | |||||
| cancelButtonProps={{ style: { display: 'none' } }} | cancelButtonProps={{ style: { display: 'none' } }} | ||||
| > | > | ||||
| <div className={styles.params_container}> | <div className={styles.params_container}> | ||||
| {globalParam.map((item) => ( | |||||
| {globalParam?.map((item) => ( | |||||
| <div key={item.param_name} className={styles.params_container_line}> | <div key={item.param_name} className={styles.params_container_line}> | ||||
| <span className={styles.params_container_line_label}>{getParamType(item)}</span> | <span className={styles.params_container_line_label}>{getParamType(item)}</span> | ||||
| <span className={styles.params_container_line_value}>{item.param_value}</span> | <span className={styles.params_container_line_value}>{item.param_value}</span> | ||||
| @@ -386,7 +386,7 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| }, | }, | ||||
| })); | })); | ||||
| return ( | return ( | ||||
| <> | |||||
| <div className={Styles.drawBox}> | |||||
| <Drawer | <Drawer | ||||
| title="任务执行详情" | title="任务执行详情" | ||||
| placement="right" | placement="right" | ||||
| @@ -397,9 +397,12 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| afterOpenChange={afterOpenChange} | afterOpenChange={afterOpenChange} | ||||
| open={open} | open={open} | ||||
| width={420} | width={420} | ||||
| className={Styles.experimentDrawer} | |||||
| destroyOnClose={true} | destroyOnClose={true} | ||||
| > | > | ||||
| <div className={Styles.detailBox}>任务名称:{stagingItem.label}</div> | |||||
| <div className={Styles.detailBox} style={{ marginTop: '15px' }}> | |||||
| 任务名称:{stagingItem.label} | |||||
| </div> | |||||
| <div className={Styles.detailBox}> | <div className={Styles.detailBox}> | ||||
| 执行状态: | 执行状态: | ||||
| <div | <div | ||||
| @@ -429,7 +432,7 @@ const Props = forwardRef(({ onParentChange }, ref) => { | |||||
| </div> | </div> | ||||
| <Tabs defaultActiveKey="1" items={items} /> | <Tabs defaultActiveKey="1" items={items} /> | ||||
| </Drawer> | </Drawer> | ||||
| </> | |||||
| </div> | |||||
| ); | ); | ||||
| }); | }); | ||||
| @@ -69,6 +69,7 @@ function Experiment() { | |||||
| return { ...item, key: item.id }; | return { ...item, key: item.id }; | ||||
| }), | }), | ||||
| ); | ); | ||||
| setTotal(res.data.totalElements); | setTotal(res.data.totalElements); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -82,6 +83,7 @@ function Experiment() { | |||||
| // 获取实验实例 | // 获取实验实例 | ||||
| const getQueryByExperiment = (val) => { | const getQueryByExperiment = (val) => { | ||||
| getQueryByExperimentId(val).then((ret) => { | getQueryByExperimentId(val).then((ret) => { | ||||
| console.log(val); | |||||
| setExpandedRowKeys(val); | setExpandedRowKeys(val); | ||||
| if (ret && ret.data && ret.data.length > 0) { | if (ret && ret.data && ret.data.length > 0) { | ||||
| try { | try { | ||||
| @@ -159,6 +161,7 @@ function Experiment() { | |||||
| }; | }; | ||||
| const expandChange = (e, record) => { | const expandChange = (e, record) => { | ||||
| clearExperimentInTimers(); | clearExperimentInTimers(); | ||||
| console.log(e, record); | |||||
| if (record.id === expandedRowKeys) { | if (record.id === expandedRowKeys) { | ||||
| setExpandedRowKeys(null); | setExpandedRowKeys(null); | ||||
| } else { | } else { | ||||
| @@ -517,6 +520,7 @@ function Experiment() { | |||||
| : ''} | : ''} | ||||
| </div> | </div> | ||||
| ), | ), | ||||
| onExpand: (e, a) => { | onExpand: (e, a) => { | ||||
| expandChange(e, a); | expandChange(e, a); | ||||
| }, | }, | ||||
| @@ -219,6 +219,11 @@ | |||||
| border-color: #eaeaea; | border-color: #eaeaea; | ||||
| border-radius: 4px; | border-radius: 4px; | ||||
| cursor: pointer; | cursor: pointer; | ||||
| .dropdown{ | |||||
| position: absolute; | |||||
| right: 20px; | |||||
| top: 15px; | |||||
| } | |||||
| .itemText { | .itemText { | ||||
| position: absolute; | position: absolute; | ||||
| top: 20px; | top: 20px; | ||||
| @@ -222,8 +222,8 @@ const Dataset = () => { | |||||
| <span style={{ color: '#1d1d20', fontSize: '20px' }}>{datasetDetailObj.name}</span> | <span style={{ color: '#1d1d20', fontSize: '20px' }}>{datasetDetailObj.name}</span> | ||||
| <div className={Styles.smallTagBox}> | <div className={Styles.smallTagBox}> | ||||
| <div className={Styles.tagItem}>模型 id:{datasetDetailObj.id}</div> | <div className={Styles.tagItem}>模型 id:{datasetDetailObj.id}</div> | ||||
| <div className={Styles.tagItem}>{datasetDetailObj.data_tag || '...'}</div> | |||||
| <div className={Styles.tagItem}>{datasetDetailObj.data_type}</div> | |||||
| <div className={Styles.tagItem}>{datasetDetailObj.model_type_name || '...'}</div> | |||||
| <div className={Styles.tagItem}>{datasetDetailObj.model_tag_name || '...'}</div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className={Styles.datasetIntroCneterBox}> | <div className={Styles.datasetIntroCneterBox}> | ||||
| @@ -1,10 +1,12 @@ | |||||
| import { getAccessToken } from '@/access'; | import { getAccessToken } from '@/access'; | ||||
| import clock from '@/assets/img/clock.png'; | import clock from '@/assets/img/clock.png'; | ||||
| import creatByImg from '@/assets/img/creatBy.png'; | import creatByImg from '@/assets/img/creatBy.png'; | ||||
| import deleteIcon from '@/assets/img/delete-icon.png'; | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { addModel, getAssetIcon, getModelList } from '@/services/dataset/index.js'; | |||||
| import { addModel, deleteModel, getAssetIcon, getModelList } from '@/services/dataset/index.js'; | |||||
| import { modalConfirm } from '@/utils/ui'; | |||||
| import { UploadOutlined } from '@ant-design/icons'; | import { UploadOutlined } from '@ant-design/icons'; | ||||
| import { Button, Form, Input, Modal, Pagination, Select, Upload } from 'antd'; | |||||
| import { Button, Form, Input, Modal, Pagination, Select, Upload, message } from 'antd'; | |||||
| import moment from 'moment'; | import moment from 'moment'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||
| @@ -54,8 +56,8 @@ const PublicData = () => { | |||||
| }); | }); | ||||
| const [activeType, setActiveType] = useState(null); | const [activeType, setActiveType] = useState(null); | ||||
| const [activeTag, setActiveTag] = useState(null); | const [activeTag, setActiveTag] = useState(null); | ||||
| const [datasetTypeList, setDatasetTypeList] = useState([]); | |||||
| const [datasetDirectionList, setDatasetDirectionList] = useState([]); | |||||
| const [modelTypeList, setmodelTypeList] = useState([]); | |||||
| const [modelDirectionList, setmodelDirectionList] = useState([]); | |||||
| const [isModalOpen, setIsModalOpen] = useState(false); | const [isModalOpen, setIsModalOpen] = useState(false); | ||||
| const [datasetList, setDatasetList] = useState([]); | const [datasetList, setDatasetList] = useState([]); | ||||
| const [total, setTotal] = useState(0); | const [total, setTotal] = useState(0); | ||||
| @@ -82,11 +84,11 @@ const PublicData = () => { | |||||
| getAssetIcon(params).then((ret) => { | getAssetIcon(params).then((ret) => { | ||||
| console.log(ret); | console.log(ret); | ||||
| if (ret.code == 200 && ret.data.content && ret.data.content.length > 0) { | if (ret.code == 200 && ret.data.content && ret.data.content.length > 0) { | ||||
| setDatasetTypeList(ret.data.content.filter((item) => item.category_id == 3)); | |||||
| setDatasetDirectionList(ret.data.content.filter((item) => item.category_id == 4)); | |||||
| setmodelTypeList(ret.data.content.filter((item) => item.category_id == 3)); | |||||
| setmodelDirectionList(ret.data.content.filter((item) => item.category_id == 4)); | |||||
| } else { | } else { | ||||
| setDatasetTypeList([]); | |||||
| setDatasetDirectionList([]); | |||||
| setmodelTypeList([]); | |||||
| setmodelDirectionList([]); | |||||
| } | } | ||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -181,8 +183,8 @@ const PublicData = () => { | |||||
| /> | /> | ||||
| <div className={Styles.itemTitle}>模型框架</div> | <div className={Styles.itemTitle}>模型框架</div> | ||||
| <div className={Styles.itemBox}> | <div className={Styles.itemBox}> | ||||
| {datasetTypeList && datasetTypeList.length > 0 | |||||
| ? datasetTypeList.map((item) => { | |||||
| {modelTypeList && modelTypeList.length > 0 | |||||
| ? modelTypeList.map((item) => { | |||||
| return ( | return ( | ||||
| <div> | <div> | ||||
| <div | <div | ||||
| @@ -222,8 +224,8 @@ const PublicData = () => { | |||||
| </div> | </div> | ||||
| <div className={Styles.itemTitle}>模型能力</div> | <div className={Styles.itemTitle}>模型能力</div> | ||||
| <div className={Styles.itemBox}> | <div className={Styles.itemBox}> | ||||
| {datasetDirectionList && datasetDirectionList.length > 0 | |||||
| ? datasetDirectionList.map((item) => { | |||||
| {modelDirectionList && modelDirectionList.length > 0 | |||||
| ? modelDirectionList.map((item) => { | |||||
| return ( | return ( | ||||
| <div> | <div> | ||||
| <div | <div | ||||
| @@ -282,9 +284,80 @@ const PublicData = () => { | |||||
| {datasetList && datasetList.length > 0 | {datasetList && datasetList.length > 0 | ||||
| ? datasetList.map((item) => { | ? datasetList.map((item) => { | ||||
| return ( | return ( | ||||
| <div className={Styles.dataItem} onClick={(e) => routeToIntro(e, item)}> | |||||
| <div | |||||
| className={Styles.dataItem} | |||||
| onClick={(e) => { | |||||
| routeToIntro(e, item); | |||||
| }} | |||||
| > | |||||
| <span className={Styles.itemText}>{item.name}</span> | <span className={Styles.itemText}>{item.name}</span> | ||||
| <div className={Styles.itemDescripition}>{item.description}</div> | |||||
| <img | |||||
| onClick={(e) => { | |||||
| e.stopPropagation(); | |||||
| modalConfirm({ | |||||
| title: '确定删除该条模型实例吗?', | |||||
| onOk: () => { | |||||
| deleteModel(item.id).then((ret) => { | |||||
| if (ret.code === 200) { | |||||
| message.success('删除成功'); | |||||
| getModelLists(queryFlow); | |||||
| } else { | |||||
| message.error(ret.msg); | |||||
| } | |||||
| }); | |||||
| }, | |||||
| }); | |||||
| }} | |||||
| className={Styles.dropdown} | |||||
| style={{ width: '17px', marginRight: '6px' }} | |||||
| src={deleteIcon} | |||||
| alt="" | |||||
| /> | |||||
| {/* <Dropdown | |||||
| className={Styles.dropdown} | |||||
| key={item.name} | |||||
| menu={{ | |||||
| items: [ | |||||
| { | |||||
| label: '详情', | |||||
| key: 'detail', | |||||
| }, | |||||
| { | |||||
| label: '删除', | |||||
| key: 'delete', | |||||
| }, | |||||
| ], | |||||
| onClick: (e) => { | |||||
| console.log(e); | |||||
| if (e.key === 'detail') { | |||||
| routeToIntro(e, item); | |||||
| } else if (e.key === 'delete') { | |||||
| modalConfirm({ | |||||
| title: '确定删除该条模型实例吗?', | |||||
| onOk: () => { | |||||
| deleteModel(item.id).then((ret) => { | |||||
| if (ret.code === 200) { | |||||
| message.success('删除成功'); | |||||
| getModelLists(queryFlow); | |||||
| } else { | |||||
| message.error(ret.msg); | |||||
| } | |||||
| }); | |||||
| }, | |||||
| }); | |||||
| } | |||||
| }, | |||||
| }} | |||||
| > | |||||
| <div> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '6px' }} | |||||
| src={moreBack} | |||||
| alt="" | |||||
| /> | |||||
| </div> | |||||
| </Dropdown> */} | |||||
| ,<div className={Styles.itemDescripition}>{item.description}</div> | |||||
| <div className={Styles.itemTime}> | <div className={Styles.itemTime}> | ||||
| <img | <img | ||||
| style={{ width: '17px', marginRight: '6px' }} | style={{ width: '17px', marginRight: '6px' }} | ||||
| @@ -401,7 +474,13 @@ const PublicData = () => { | |||||
| ] | ] | ||||
| } | } | ||||
| > | > | ||||
| <Select allowClear placeholder="请选择模型类型" options={[]} /> | |||||
| <Select | |||||
| allowClear | |||||
| placeholder="请选择模型类型" | |||||
| options={modelTypeList.map((item) => { | |||||
| return { value: item.id, label: item.name }; | |||||
| })} | |||||
| /> | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | <Form.Item | ||||
| label="模型能力" | label="模型能力" | ||||
| @@ -415,7 +494,13 @@ const PublicData = () => { | |||||
| ] | ] | ||||
| } | } | ||||
| > | > | ||||
| <Select allowClear placeholder="请选择模型标签" options={[]} /> | |||||
| <Select | |||||
| allowClear | |||||
| placeholder="请选择模型标签" | |||||
| options={modelDirectionList.map((item) => { | |||||
| return { value: item.id, label: item.name }; | |||||
| })} | |||||
| /> | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item label="模型文件" name="models_version_vos"> | <Form.Item label="模型文件" name="models_version_vos"> | ||||
| <Upload {...props} data={{ uuid: uuid }} accept=".zip,.tgz"> | <Upload {...props} data={{ uuid: uuid }} accept=".zip,.tgz"> | ||||
| @@ -1,7 +1,9 @@ | |||||
| import clock from '@/assets/img/clock.png'; | import clock from '@/assets/img/clock.png'; | ||||
| import creatByImg from '@/assets/img/creatBy.png'; | import creatByImg from '@/assets/img/creatBy.png'; | ||||
| import { getAssetIcon, getModelList } from '@/services/dataset/index.js'; | |||||
| import { Form, Input, Modal, Pagination, Radio } from 'antd'; | |||||
| import deleteIcon from '@/assets/img/delete-icon.png'; | |||||
| import { deleteModel, getAssetIcon, getModelList } from '@/services/dataset/index.js'; | |||||
| import { modalConfirm } from '@/utils/ui'; | |||||
| import { Form, Input, Modal, Pagination, Radio, message } from 'antd'; | |||||
| import moment from 'moment'; | import moment from 'moment'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||
| @@ -224,8 +226,35 @@ const PublicData = () => { | |||||
| {datasetList && datasetList.length > 0 | {datasetList && datasetList.length > 0 | ||||
| ? datasetList.map((item) => { | ? datasetList.map((item) => { | ||||
| return ( | return ( | ||||
| <div className={Styles.dataItem} onClick={(e) => routeToIntro(e, item)}> | |||||
| <div | |||||
| className={Styles.dataItem} | |||||
| onClick={(e) => { | |||||
| routeToIntro(e, item); | |||||
| }} | |||||
| > | |||||
| <span className={Styles.itemText}>{item.name}</span> | <span className={Styles.itemText}>{item.name}</span> | ||||
| <img | |||||
| onClick={(e) => { | |||||
| e.stopPropagation(); | |||||
| modalConfirm({ | |||||
| title: '确定删除该条模型实例吗?', | |||||
| onOk: () => { | |||||
| deleteModel(item.id).then((ret) => { | |||||
| if (ret.code === 200) { | |||||
| message.success('删除成功'); | |||||
| getModelLists(queryFlow); | |||||
| } else { | |||||
| message.error(ret.msg); | |||||
| } | |||||
| }); | |||||
| }, | |||||
| }); | |||||
| }} | |||||
| className={Styles.dropdown} | |||||
| style={{ width: '17px', marginRight: '6px' }} | |||||
| src={deleteIcon} | |||||
| alt="" | |||||
| /> | |||||
| <div className={Styles.itemDescripition}>{item.description}</div> | <div className={Styles.itemDescripition}>{item.description}</div> | ||||
| <div className={Styles.itemTime}> | <div className={Styles.itemTime}> | ||||
| <img | <img | ||||
| @@ -14,7 +14,7 @@ import styles from './globalParamsDrawer.less'; | |||||
| type GlobalParamsDrawerProps = { | type GlobalParamsDrawerProps = { | ||||
| open: boolean; | open: boolean; | ||||
| onClose: () => void; | onClose: () => void; | ||||
| globalParam: PipelineGlobalParam[]; | |||||
| globalParam: PipelineGlobalParam[] | null; | |||||
| }; | }; | ||||
| const GlobalParamsDrawer = forwardRef( | const GlobalParamsDrawer = forwardRef( | ||||
| @@ -128,18 +128,7 @@ const EditPipeline = () => { | |||||
| }; | }; | ||||
| const handlerClick = (e) => { | const handlerClick = (e) => { | ||||
| e.stopPropagation(); | e.stopPropagation(); | ||||
| console.log(propsRef, graph); | |||||
| // let cache = []; | |||||
| // let json_str = JSON.stringify(graph, function(key, value) { | |||||
| // if (typeof value === 'object' && value !== null) { | |||||
| // if (cache.indexOf(value) !== -1) { | |||||
| // return; | |||||
| // } | |||||
| // cache.push(value); | |||||
| // } | |||||
| // return value; | |||||
| // }); | |||||
| // console.log(json_str); | |||||
| // console.log(propsRef, graph); | |||||
| propsRef.current.showDrawer(e); | propsRef.current.showDrawer(e); | ||||
| }; | }; | ||||
| const getGraphData = (data) => { | const getGraphData = (data) => { | ||||
| @@ -281,7 +270,7 @@ const EditPipeline = () => { | |||||
| const getFirstWorkflow = (val) => { | const getFirstWorkflow = (val) => { | ||||
| getWorkflowById(val).then((ret) => { | getWorkflowById(val).then((ret) => { | ||||
| if (ret && ret.data) { | if (ret && ret.data) { | ||||
| setGlobalParam(ret.data.global_param); | |||||
| setGlobalParam(ret.data.global_param || []); | |||||
| } | } | ||||
| if (graph && ret.data && ret.data.dag) { | if (graph && ret.data && ret.data.dag) { | ||||
| getGraphData(JSON.parse(ret.data.dag)); | getGraphData(JSON.parse(ret.data.dag)); | ||||
| @@ -353,7 +342,7 @@ const EditPipeline = () => { | |||||
| // this.graph.setItemState(e.item, 'showAnchors', false); | // this.graph.setItemState(e.item, 'showAnchors', false); | ||||
| graph.setItemState(e.item, 'nodeSelected', false); | graph.setItemState(e.item, 'nodeSelected', false); | ||||
| }); | }); | ||||
| graph.off('dblclick', handlerClick); | |||||
| // graph.off('dblclick', handlerClick); | |||||
| }; | }; | ||||
| }, []); | }, []); | ||||
| const initGraph = () => { | const initGraph = () => { | ||||
| @@ -390,30 +379,15 @@ const EditPipeline = () => { | |||||
| getAnchorPoints(cfg) { | getAnchorPoints(cfg) { | ||||
| return ( | return ( | ||||
| cfg.anchorPoints || [ | cfg.anchorPoints || [ | ||||
| // 上下各3,左右各1 | |||||
| // 四个 | |||||
| [0.5, 0], | [0.5, 0], | ||||
| [0.5, 1], | [0.5, 1], | ||||
| // [0, 0.5], | |||||
| // [1, 0.5], | |||||
| ] | ] | ||||
| ); | ); | ||||
| }, | }, | ||||
| // draw(cfg, group) { | |||||
| // let rect=group.addShape('text', { | |||||
| // attrs: { | |||||
| // text: fittingString(cfg.label, 110, 15), | |||||
| // x: 90 - getTextSize(cfg.label, 110, 15), | |||||
| // y: 0, | |||||
| // fontSize: 10, | |||||
| // textAlign: 'center', | |||||
| // textBaseline: 'middle', | |||||
| // fill:'#000' | |||||
| // }, | |||||
| // name: 'text-shape', | |||||
| // }); | |||||
| // return rect; | |||||
| // }, | |||||
| afterDraw(cfg, group) { | afterDraw(cfg, group) { | ||||
| // console.log(group, cfg, 12312); | |||||
| const image = group.addShape('image', { | const image = group.addShape('image', { | ||||
| attrs: { | attrs: { | ||||
| x: -45, | x: -45, | ||||
| @@ -442,7 +416,6 @@ const EditPipeline = () => { | |||||
| } | } | ||||
| const bbox = group.getBBox(); | const bbox = group.getBBox(); | ||||
| const anchorPoints = this.getAnchorPoints(cfg); | const anchorPoints = this.getAnchorPoints(cfg); | ||||
| // console.log(anchorPoints); | |||||
| anchorPoints.forEach((anchorPos, i) => { | anchorPoints.forEach((anchorPos, i) => { | ||||
| group.addShape('circle', { | group.addShape('circle', { | ||||
| attrs: { | attrs: { | ||||
| @@ -456,6 +429,7 @@ const EditPipeline = () => { | |||||
| anchorPointIdx: i, // flag the idx of the anchor-point circle | anchorPointIdx: i, // flag the idx of the anchor-point circle | ||||
| links: 0, // cache the number of edges connected to this shape | links: 0, // cache the number of edges connected to this shape | ||||
| visible: false, // invisible by default, shows up when links > 1 or the node is in showAnchors state | visible: false, // invisible by default, shows up when links > 1 or the node is in showAnchors state | ||||
| draggable: true, | |||||
| }); | }); | ||||
| }); | }); | ||||
| return image; | return image; | ||||
| @@ -503,7 +477,7 @@ const EditPipeline = () => { | |||||
| // config the shouldBegin and shouldEnd to make sure the create-edge is began and ended at anchor-point circles | // config the shouldBegin and shouldEnd to make sure the create-edge is began and ended at anchor-point circles | ||||
| { | { | ||||
| type: 'create-edge', | type: 'create-edge', | ||||
| // trigger: 'drag', | |||||
| trigger: 'drag', | |||||
| shouldBegin: (e) => { | shouldBegin: (e) => { | ||||
| // avoid beginning at other shapes on the node | // avoid beginning at other shapes on the node | ||||
| if (e.target && e.target.get('name') !== 'anchor-point') return false; | if (e.target && e.target.get('name') !== 'anchor-point') return false; | ||||
| @@ -575,7 +549,7 @@ const EditPipeline = () => { | |||||
| }, | }, | ||||
| defaultEdge: { | defaultEdge: { | ||||
| // type: 'quadratic', | // type: 'quadratic', | ||||
| type: 'cubic-vertical', | |||||
| // type: 'cubic-vertical', | |||||
| style: { | style: { | ||||
| endArrow: { | endArrow: { | ||||
| @@ -619,16 +593,20 @@ const EditPipeline = () => { | |||||
| fitView: true, | fitView: true, | ||||
| fitViewPadding: [320, 320, 220, 320], | fitViewPadding: [320, 320, 220, 320], | ||||
| }); | }); | ||||
| graph.on('dblclick', (e) => { | |||||
| console.log(e.item); | |||||
| if (e.item) { | |||||
| // graph.on('dblclick', (e) => { | |||||
| // console.log(e.item); | |||||
| // if (e.item) { | |||||
| // graph.setItemState(e.item, 'nodeClicked', true); | |||||
| // handlerClick(e); | |||||
| // } | |||||
| // }); | |||||
| graph.on('node:click', (e) => { | |||||
| console.log(e.target.get('name')); | |||||
| if (e.target.get('name') !== 'anchor-point' && e.item) { | |||||
| graph.setItemState(e.item, 'nodeClicked', true); | graph.setItemState(e.item, 'nodeClicked', true); | ||||
| handlerClick(e); | handlerClick(e); | ||||
| } | } | ||||
| }); | }); | ||||
| graph.on('click', (e) => { | |||||
| console.log(e.item); | |||||
| }); | |||||
| graph.on('aftercreateedge', (e) => { | graph.on('aftercreateedge', (e) => { | ||||
| // update the sourceAnchor and targetAnchor for the newly added edge | // update the sourceAnchor and targetAnchor for the newly added edge | ||||
| graph.updateItem(e.edge, { | graph.updateItem(e.edge, { | ||||
| @@ -666,6 +644,39 @@ const EditPipeline = () => { | |||||
| }, | }, | ||||
| }); | }); | ||||
| }); | }); | ||||
| graph.on('node:dragenter', (e) => { | |||||
| console.log(e.target.get('name')); | |||||
| console.log('node:dragenter'); | |||||
| graph.setItemState(e.item, 'nodeSelected', true); | |||||
| graph.updateItem(e.item, { | |||||
| // 节点的样式 | |||||
| style: { | |||||
| stroke: '#1664ff', | |||||
| }, | |||||
| }); | |||||
| }); | |||||
| graph.on('node:dragleave', (e) => { | |||||
| console.log(e.target.get('name')); | |||||
| console.log('node:dragleave'); | |||||
| graph.setItemState(e.item, 'nodeSelected', false); | |||||
| graph.updateItem(e.item, { | |||||
| // 节点的样式 | |||||
| style: { | |||||
| stroke: 'transparent', | |||||
| }, | |||||
| }); | |||||
| }); | |||||
| graph.on('node:dragstart', (e) => { | |||||
| console.log('node:dragstart'); | |||||
| graph.setItemState(e.item, 'nodeSelected', true); | |||||
| graph.updateItem(e.item, { | |||||
| // 节点的样式 | |||||
| style: { | |||||
| stroke: '#1664ff', | |||||
| }, | |||||
| }); | |||||
| }); | |||||
| graph.on('afterremoveitem', (e) => { | graph.on('afterremoveitem', (e) => { | ||||
| if (e.item && e.item.source && e.item.target) { | if (e.item && e.item.source && e.item.target) { | ||||
| const sourceNode = graph.findById(e.item.source); | const sourceNode = graph.findById(e.item.source); | ||||
| @@ -1,13 +1,13 @@ | |||||
| /* | /* | ||||
| * @Author: 赵伟 | * @Author: 赵伟 | ||||
| * @Date: 2024-03-25 13:52:54 | * @Date: 2024-03-25 13:52:54 | ||||
| * @Description: | |||||
| * @Description: 网络请求配置,详情请参考 https://umijs.org/docs/max/request | |||||
| */ | */ | ||||
| import type { RequestConfig } from '@umijs/max'; | import type { RequestConfig } from '@umijs/max'; | ||||
| import { message } from 'antd'; | import { message } from 'antd'; | ||||
| import { clearSessionToken, getAccessToken, getRefreshToken, getTokenExpireTime } from './access'; | |||||
| const checkRegion = 5 * 60 * 1000; | |||||
| import { clearSessionToken, getAccessToken } from './access'; | |||||
| import { setRemoteMenu } from './services/session'; | |||||
| import { gotoLoginPage } from './utils/ui'; | |||||
| /** | /** | ||||
| * Umi Max 网络请求配置 | * Umi Max 网络请求配置 | ||||
| @@ -21,23 +21,25 @@ export const requestConfig: RequestConfig = { | |||||
| const authHeader = headers['Authorization']; | const authHeader = headers['Authorization']; | ||||
| const isToken = headers['isToken']; | const isToken = headers['isToken']; | ||||
| if (!authHeader && isToken !== false) { | if (!authHeader && isToken !== false) { | ||||
| const expireTime = getTokenExpireTime(); | |||||
| if (expireTime) { | |||||
| const left = Number(expireTime) - new Date().getTime(); | |||||
| const refreshToken = getRefreshToken(); | |||||
| if (left < checkRegion && refreshToken) { | |||||
| if (left < 0) { | |||||
| clearSessionToken(); | |||||
| } | |||||
| } else { | |||||
| const accessToken = getAccessToken(); | |||||
| if (accessToken) { | |||||
| headers['Authorization'] = `Bearer ${accessToken}`; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| clearSessionToken(); | |||||
| const accessToken = getAccessToken(); | |||||
| if (accessToken) { | |||||
| headers['Authorization'] = `Bearer ${accessToken}`; | |||||
| } | } | ||||
| // const expireTime = getTokenExpireTime(); | |||||
| // if (expireTime) { | |||||
| // const left = Number(expireTime) - new Date().getTime(); | |||||
| // const refreshToken = getRefreshToken(); | |||||
| // if (left < 0 && refreshToken) { | |||||
| // clearSessionToken(); | |||||
| // } else { | |||||
| // const accessToken = getAccessToken(); | |||||
| // if (accessToken) { | |||||
| // headers['Authorization'] = `Bearer ${accessToken}`; | |||||
| // } | |||||
| // } | |||||
| // } else { | |||||
| // clearSessionToken(); | |||||
| // } | |||||
| } | } | ||||
| return { url, options }; | return { url, options }; | ||||
| }, | }, | ||||
| @@ -45,15 +47,19 @@ export const requestConfig: RequestConfig = { | |||||
| responseInterceptors: [ | responseInterceptors: [ | ||||
| (response: any) => { | (response: any) => { | ||||
| const { status, data } = response; | const { status, data } = response; | ||||
| // console.log('response', response); | |||||
| if (status >= 200 && status < 300 && data && (data instanceof Blob || data.code === 200)) { | |||||
| return response; | |||||
| } else { | |||||
| if (data && data.msg) { | |||||
| message.error(data.msg); | |||||
| if (status >= 200 && status < 300) { | |||||
| if (data && (data instanceof Blob || data.code === 200)) { | |||||
| return response; | |||||
| } else if (data && data.code === 401) { | |||||
| clearSessionToken(); | |||||
| setRemoteMenu(null); | |||||
| gotoLoginPage(false); | |||||
| } else { | } else { | ||||
| message.error('请求失败'); | |||||
| message.error(data?.msg ?? '请求失败'); | |||||
| return Promise.reject(response); | |||||
| } | } | ||||
| } else { | |||||
| message.error('请求失败'); | |||||
| return Promise.reject(response); | return Promise.reject(response); | ||||
| } | } | ||||
| }, | }, | ||||
| @@ -118,3 +118,15 @@ export function exportDataset(id) { | |||||
| method: 'GET', | method: 'GET', | ||||
| }); | }); | ||||
| } | } | ||||
| // 删除模型集 | |||||
| export function deleteModel(id) { | |||||
| return request(`/api/mmp/models/${id}`, { | |||||
| method: 'DELETE', | |||||
| }); | |||||
| } | |||||
| // 删除数据集 | |||||
| export function deleteDataset(id) { | |||||
| return request(`/api/mmp/dataset/${id}`, { | |||||
| method: 'DELETE', | |||||
| }); | |||||
| } | |||||
| @@ -1,8 +1,7 @@ | |||||
| import { createIcon } from '@/utils/IconUtil'; | |||||
| import { MenuDataItem } from '@ant-design/pro-components'; | import { MenuDataItem } from '@ant-design/pro-components'; | ||||
| import { request } from '@umijs/max'; | import { request } from '@umijs/max'; | ||||
| import React, { lazy } from 'react'; | import React, { lazy } from 'react'; | ||||
| import { createFromIconfontCN } from '@ant-design/icons'; | |||||
| let remoteMenu: any = null; | let remoteMenu: any = null; | ||||
| export function getRemoteMenu() { | export function getRemoteMenu() { | ||||
| @@ -101,7 +100,7 @@ export function convertCompatRouters(childrens: API.RoutersMenuItem[]): any[] { | |||||
| return { | return { | ||||
| path: item.path, | path: item.path, | ||||
| // icon:'icon-a-057_fenlei', | // icon:'icon-a-057_fenlei', | ||||
| icon: 'icon-'+item.meta.icon, | |||||
| icon: 'icon-' + item.meta.icon, | |||||
| // icon: item.meta.icon, | // icon: item.meta.icon, | ||||
| name: item.meta.title, | name: item.meta.title, | ||||
| routes: item.children ? convertCompatRouters(item.children) : undefined, | routes: item.children ? convertCompatRouters(item.children) : undefined, | ||||
| @@ -28,8 +28,8 @@ export async function login(body: API.LoginParams, options?: Record<string, any> | |||||
| /** 退出登录接口 POST /api/login/outLogin */ | /** 退出登录接口 POST /api/login/outLogin */ | ||||
| export async function logout() { | export async function logout() { | ||||
| return request<Record<string, any>>('/api/logout', { | |||||
| method: 'delete', | |||||
| return request<Record<string, any>>('/api/auth/logout', { | |||||
| method: 'DELETE', | |||||
| }); | }); | ||||
| } | } | ||||
| @@ -1,4 +1,10 @@ | |||||
| // 全局颜色变量 | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-28 08:47:43 | |||||
| * @Description: 全局变量,可以直接在 less 文件里使用,无需引入;也可以导入到 js 里使用;为 antd 主题修改提供常量值 | |||||
| */ | |||||
| // 颜色 | |||||
| @primary-color: #1664ff; // 主色调 | @primary-color: #1664ff; // 主色调 | ||||
| @primary-color-hover: #69b1ff; | @primary-color-hover: #69b1ff; | ||||
| @background-color: #f9fafb; // 页面背景颜色 | @background-color: #f9fafb; // 页面背景颜色 | ||||
| @@ -1,3 +1,9 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-08 09:54:39 | |||||
| * @Description: 定义全局类型,比如无关联的页面都需要要的类型 | |||||
| */ | |||||
| // 流水线全局参数 | // 流水线全局参数 | ||||
| export type PipelineGlobalParam = { | export type PipelineGlobalParam = { | ||||
| param_name: string; | param_name: string; | ||||
| @@ -3,7 +3,9 @@ | |||||
| * @Date: 2024-04-19 14:42:51 | * @Date: 2024-04-19 14:42:51 | ||||
| * @Description: UI 公共方法 | * @Description: UI 公共方法 | ||||
| */ | */ | ||||
| import { PageEnum } from '@/enums/pagesEnums'; | |||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { history } from '@umijs/max'; | |||||
| import { Modal, type ModalFuncProps, type UploadFile } from 'antd'; | import { Modal, type ModalFuncProps, type UploadFile } from 'antd'; | ||||
| // 自定义 Confirm 弹框 | // 自定义 Confirm 弹框 | ||||
| @@ -43,3 +45,17 @@ export const getFileListFromEvent = (e: any) => { | |||||
| return item; | return item; | ||||
| }); | }); | ||||
| }; | }; | ||||
| // 去登录页面 | |||||
| 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(); | |||||
| if (window.location.pathname !== PageEnum.LOGIN) { | |||||
| history.replace({ | |||||
| pathname: PageEnum.LOGIN, | |||||
| search: newSearch, | |||||
| }); | |||||
| } | |||||
| }; | |||||
| @@ -158,7 +158,7 @@ public class DatasetController { | |||||
| */ | */ | ||||
| @DeleteMapping({"{id}"}) | @DeleteMapping({"{id}"}) | ||||
| @ApiOperation("根据id删除数据集") | @ApiOperation("根据id删除数据集") | ||||
| public AjaxResult deleteById(@PathVariable("id") Integer id) { | |||||
| public AjaxResult deleteById(@PathVariable("id") Integer id) throws Exception { | |||||
| return AjaxResult.success(this.datasetService.removeById(id)); | return AjaxResult.success(this.datasetService.removeById(id)); | ||||
| } | } | ||||
| @@ -123,7 +123,7 @@ public class ModelsController extends BaseController { | |||||
| */ | */ | ||||
| @PostMapping | @PostMapping | ||||
| @ApiOperation("添加模型") | @ApiOperation("添加模型") | ||||
| public GenericsAjaxResult<Models> add(@RequestBody Models models) { | |||||
| public GenericsAjaxResult<Models> add(@RequestBody Models models) throws Exception { | |||||
| return genericsSuccess(this.modelsService.insert(models)); | return genericsSuccess(this.modelsService.insert(models)); | ||||
| } | } | ||||
| @@ -161,7 +161,7 @@ public class ModelsController extends BaseController { | |||||
| */ | */ | ||||
| @DeleteMapping("{id}") | @DeleteMapping("{id}") | ||||
| @ApiOperation("删除模型") | @ApiOperation("删除模型") | ||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) { | |||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) throws Exception { | |||||
| return genericsSuccess(this.modelsService.removeById(id)); | return genericsSuccess(this.modelsService.removeById(id)); | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ package com.ruoyi.platform.domain; | |||||
| import com.fasterxml.jackson.databind.PropertyNamingStrategy; | import com.fasterxml.jackson.databind.PropertyNamingStrategy; | ||||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||||
| import com.ruoyi.platform.annotations.CheckDuplicate; | |||||
| import io.swagger.annotations.ApiModel; | import io.swagger.annotations.ApiModel; | ||||
| import io.swagger.annotations.ApiModelProperty; | import io.swagger.annotations.ApiModelProperty; | ||||
| @@ -22,6 +23,7 @@ public class Models implements Serializable { | |||||
| private Integer id; | private Integer id; | ||||
| @ApiModelProperty(value = "模型名称") | @ApiModelProperty(value = "模型名称") | ||||
| @CheckDuplicate | |||||
| private String name; | private String name; | ||||
| @ApiModelProperty(value = "模型描述") | @ApiModelProperty(value = "模型描述") | ||||
| @@ -52,6 +54,16 @@ public class Models implements Serializable { | |||||
| private Integer state; | private Integer state; | ||||
| @ApiModelProperty(value = "模型类型名") | |||||
| private String modelTypeName; | |||||
| @ApiModelProperty(value = "模型tag名") | |||||
| private String modelTagName; | |||||
| public Integer getId() { | public Integer getId() { | ||||
| return id; | return id; | ||||
| } | } | ||||
| @@ -158,5 +170,21 @@ public class Models implements Serializable { | |||||
| this.state = state; | this.state = state; | ||||
| } | } | ||||
| public String getModelTagName() { | |||||
| return modelTagName; | |||||
| } | |||||
| public void setModelTagName(String modelTagName) { | |||||
| this.modelTagName = modelTagName; | |||||
| } | |||||
| public String getModelTypeName() { | |||||
| return modelTypeName; | |||||
| } | |||||
| public void setModelTypeName(String modelTypeName) { | |||||
| this.modelTypeName = modelTypeName; | |||||
| } | |||||
| } | } | ||||
| @@ -84,7 +84,8 @@ public interface DatasetVersionDao { | |||||
| List<DatasetVersion> queryByDatasetId(Integer datasetId); | List<DatasetVersion> queryByDatasetId(Integer datasetId); | ||||
| DatasetVersion queryByDatasetVersion(@Param("datasetVersion") DatasetVersion datasetVersion); | |||||
| DatasetVersion | |||||
| queryByDatasetVersion(@Param("datasetVersion") DatasetVersion datasetVersion); | |||||
| List<DatasetVersion> queryAllByDatasetVersion(@Param("datasetId") Integer datasetId, @Param("version") String version); | List<DatasetVersion> queryAllByDatasetVersion(@Param("datasetId") Integer datasetId, @Param("version") String version); | ||||
| @@ -1,10 +1,12 @@ | |||||
| package com.ruoyi.platform.mapper; | package com.ruoyi.platform.mapper; | ||||
| import com.ruoyi.platform.domain.Dataset; | |||||
| import com.ruoyi.platform.domain.Models; | import com.ruoyi.platform.domain.Models; | ||||
| import org.apache.ibatis.annotations.Param; | import org.apache.ibatis.annotations.Param; | ||||
| import org.springframework.data.domain.Pageable; | import org.springframework.data.domain.Pageable; | ||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.Map; | |||||
| /** | /** | ||||
| * (Models)表数据库访问层 | * (Models)表数据库访问层 | ||||
| @@ -22,6 +24,8 @@ public interface ModelsDao { | |||||
| */ | */ | ||||
| Models queryById(Integer id); | Models queryById(Integer id); | ||||
| Models findByName(@Param("name") String name); | |||||
| /** | /** | ||||
| * 查询指定行数据 | * 查询指定行数据 | ||||
| * | * | ||||
| @@ -68,7 +68,7 @@ DatasetService { | |||||
| */ | */ | ||||
| boolean deleteById(Integer id); | boolean deleteById(Integer id); | ||||
| String removeById(Integer id); | |||||
| String removeById(Integer id) throws Exception; | |||||
| ResponseEntity<InputStreamResource> downloadDataset(Integer id) throws Exception; | ResponseEntity<InputStreamResource> downloadDataset(Integer id) throws Exception; | ||||
| @@ -2,6 +2,7 @@ package com.ruoyi.platform.service; | |||||
| import com.ruoyi.platform.domain.Dataset; | |||||
| import com.ruoyi.platform.domain.Models; | import com.ruoyi.platform.domain.Models; | ||||
| import com.ruoyi.platform.domain.ModelsVersion; | import com.ruoyi.platform.domain.ModelsVersion; | ||||
| import com.ruoyi.platform.vo.ModelsVo; | import com.ruoyi.platform.vo.ModelsVo; | ||||
| @@ -45,7 +46,7 @@ public interface ModelsService { | |||||
| * @param models 实例对象 | * @param models 实例对象 | ||||
| * @return 实例对象 | * @return 实例对象 | ||||
| */ | */ | ||||
| Models insert(Models models); | |||||
| Models insert(Models models) throws Exception; | |||||
| /** | /** | ||||
| * 修改数据 | * 修改数据 | ||||
| @@ -64,7 +65,7 @@ public interface ModelsService { | |||||
| */ | */ | ||||
| boolean deleteById(Integer id); | boolean deleteById(Integer id); | ||||
| String removeById(Integer id); | |||||
| String removeById(Integer id) throws Exception; | |||||
| ResponseEntity<InputStreamResource> downloadModels(Integer id) throws Exception; | ResponseEntity<InputStreamResource> downloadModels(Integer id) throws Exception; | ||||
| @@ -81,5 +82,7 @@ public interface ModelsService { | |||||
| String readFileContent(Integer modelsId, String version) throws Exception; | String readFileContent(Integer modelsId, String version) throws Exception; | ||||
| public void checkDeclaredName(Models insert) throws Exception; | |||||
| List<Map<String, String>> exportModels(String path, String uuid) throws Exception; | List<Map<String, String>> exportModels(String path, String uuid) throws Exception; | ||||
| } | } | ||||
| @@ -84,17 +84,27 @@ public class DatasetServiceImpl implements DatasetService { | |||||
| */ | */ | ||||
| @Override | @Override | ||||
| public Dataset queryById(Integer id) { | public Dataset queryById(Integer id) { | ||||
| Dataset dataset = this.datasetDao.queryById(id); | Dataset dataset = this.datasetDao.queryById(id); | ||||
| String dataType = dataset.getDataType(); | |||||
| String dataTag = dataset.getDataTag(); | |||||
| //去资产管理表中查询对应的图标名 | |||||
| AssetIcon dataTypeAssetIcon = assetIconService.queryByPath(dataType); | |||||
| AssetIcon dataTagAssetIcon = assetIconService.queryByPath(dataTag); | |||||
| dataset.setDatasetTypeName(dataTypeAssetIcon.getName()); | |||||
| dataset.setDatasetTagName(dataTagAssetIcon.getName()); | |||||
| return dataset; | |||||
| if (dataset != null) { | |||||
| String dataType = dataset.getDataType(); | |||||
| String dataTag = dataset.getDataTag(); | |||||
| // 判空逻辑,只有当dataType和dataTag不为空时,才进行查询 | |||||
| if (dataType != null && !dataType.isEmpty()) { | |||||
| AssetIcon dataTypeAssetIcon = assetIconService.queryById(Integer.valueOf(dataType)); | |||||
| if (dataTypeAssetIcon != null) { | |||||
| dataset.setDatasetTypeName(dataTypeAssetIcon.getName()); | |||||
| } | |||||
| } | |||||
| if (dataTag != null && !dataTag.isEmpty()) { | |||||
| AssetIcon dataTagAssetIcon = assetIconService.queryById(Integer.valueOf(dataTag)); | |||||
| if (dataTagAssetIcon != null) { | |||||
| dataset.setDatasetTagName(dataTagAssetIcon.getName()); | |||||
| } | |||||
| } | |||||
| } | |||||
| return dataset; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -163,10 +173,10 @@ public class DatasetServiceImpl implements DatasetService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String removeById(Integer id) { | |||||
| public String removeById(Integer id) throws Exception { | |||||
| Dataset dataset = this.datasetDao.queryById(id); | Dataset dataset = this.datasetDao.queryById(id); | ||||
| if (dataset == null){ | if (dataset == null){ | ||||
| return "数据集不存在"; | |||||
| throw new Exception("数据集不存在"); | |||||
| } | } | ||||
| //判断权限,只有admin和创建者本身可以删除该数据集 | //判断权限,只有admin和创建者本身可以删除该数据集 | ||||
| @@ -174,10 +184,10 @@ public class DatasetServiceImpl implements DatasetService { | |||||
| String username = loginUser.getUsername(); | String username = loginUser.getUsername(); | ||||
| String createdBy = dataset.getCreateBy(); | String createdBy = dataset.getCreateBy(); | ||||
| if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | ||||
| return "无权限删除该数据集版本"; | |||||
| throw new Exception("无权限删除该数据集"); | |||||
| } | } | ||||
| if (datasetVersionService.queryByDatasetId(id).size()>0){ | if (datasetVersionService.queryByDatasetId(id).size()>0){ | ||||
| return "请先删除该数据集的版本文件"; | |||||
| throw new Exception("请先删除该数据集下的版本文件"); | |||||
| } | } | ||||
| dataset.setState(0); | dataset.setState(0); | ||||
| @@ -232,6 +242,7 @@ public class DatasetServiceImpl implements DatasetService { | |||||
| @Override | @Override | ||||
| public List<Map<String, String>> uploadDataset(MultipartFile[] files, String uuid) throws Exception { | public List<Map<String, String>> uploadDataset(MultipartFile[] files, String uuid) throws Exception { | ||||
| List<Map<String, String>> results = new ArrayList<>(); | List<Map<String, String>> results = new ArrayList<>(); | ||||
| for (MultipartFile file:files){ | for (MultipartFile file:files){ | ||||
| // 构建objectName | // 构建objectName | ||||
| String username = SecurityUtils.getLoginUser().getUsername(); | String username = SecurityUtils.getLoginUser().getUsername(); | ||||
| @@ -251,7 +262,6 @@ public class DatasetServiceImpl implements DatasetService { | |||||
| if (dataset == null) { | if (dataset == null) { | ||||
| throw new Exception("数据集不存在,请检查数据集id"); | throw new Exception("数据集不存在,请检查数据集id"); | ||||
| } | } | ||||
| DatasetVersion version = datasetVersionService.queryByDatasetVersion(datasetVersion); | DatasetVersion version = datasetVersionService.queryByDatasetVersion(datasetVersion); | ||||
| String url = ""; | String url = ""; | ||||
| if (version == null) { | if (version == null) { | ||||
| @@ -221,7 +221,7 @@ public class ImageServiceImpl implements ImageService { | |||||
| if (imageVersionInsert == null) { | if (imageVersionInsert == null) { | ||||
| throw new Exception("新增镜像版本失败"); | throw new Exception("新增镜像版本失败"); | ||||
| } | } | ||||
| // 使用CompletableFuture异步执行不同的镜像构建逻辑 | |||||
| // 使用CompletableFuture异步执行不同的镜像构建逻辑,在构建镜像的同时,更新数据库中的镜像版本状态 | |||||
| CompletableFuture.supplyAsync(() -> { | CompletableFuture.supplyAsync(() -> { | ||||
| Map<String, String> resultMap = new HashMap<>(); | Map<String, String> resultMap = new HashMap<>(); | ||||
| try { | try { | ||||
| @@ -281,6 +281,7 @@ public class ImageServiceImpl implements ImageService { | |||||
| String pushCmd = "docker push " + imageUrl; | String pushCmd = "docker push " + imageUrl; | ||||
| String sizeCmd = "docker inspect --format='{{.Size}}' " + imageUrl; | String sizeCmd = "docker inspect --format='{{.Size}}' " + imageUrl; | ||||
| String s = k8sClientUtil.executeCommand(pod, tagCmd); | String s = k8sClientUtil.executeCommand(pod, tagCmd); | ||||
| if (StringUtils.isNotEmpty(k8sClientUtil.executeCommand(pod, pushCmd))){ | if (StringUtils.isNotEmpty(k8sClientUtil.executeCommand(pod, pushCmd))){ | ||||
| resultMap.put("url", imageUrl); | resultMap.put("url", imageUrl); | ||||
| //得到镜像文件大小 | //得到镜像文件大小 | ||||
| @@ -1,10 +1,14 @@ | |||||
| package com.ruoyi.platform.service.impl; | package com.ruoyi.platform.service.impl; | ||||
| import com.ruoyi.common.security.utils.SecurityUtils; | import com.ruoyi.common.security.utils.SecurityUtils; | ||||
| import com.ruoyi.platform.annotations.CheckDuplicate; | |||||
| import com.ruoyi.platform.domain.AssetIcon; | |||||
| import com.ruoyi.platform.domain.Dataset; | |||||
| import com.ruoyi.platform.domain.Models; | import com.ruoyi.platform.domain.Models; | ||||
| import com.ruoyi.platform.domain.ModelsVersion; | import com.ruoyi.platform.domain.ModelsVersion; | ||||
| import com.ruoyi.platform.mapper.ModelsDao; | import com.ruoyi.platform.mapper.ModelsDao; | ||||
| import com.ruoyi.platform.mapper.ModelsVersionDao; | import com.ruoyi.platform.mapper.ModelsVersionDao; | ||||
| import com.ruoyi.platform.service.AssetIconService; | |||||
| import com.ruoyi.platform.service.MinioService; | import com.ruoyi.platform.service.MinioService; | ||||
| import com.ruoyi.platform.service.ModelsService; | import com.ruoyi.platform.service.ModelsService; | ||||
| import com.ruoyi.platform.service.ModelsVersionService; | import com.ruoyi.platform.service.ModelsVersionService; | ||||
| @@ -33,6 +37,7 @@ import javax.annotation.Resource; | |||||
| import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||
| import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.lang.reflect.Field; | |||||
| import java.net.URLEncoder; | import java.net.URLEncoder; | ||||
| import java.util.*; | import java.util.*; | ||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
| @@ -58,6 +63,9 @@ public class ModelsServiceImpl implements ModelsService { | |||||
| @Resource | @Resource | ||||
| private MinioService minioService; | private MinioService minioService; | ||||
| @Resource | |||||
| private AssetIconService assetIconService; | |||||
| // 固定存储桶名 | // 固定存储桶名 | ||||
| @Value("${minio.dataReleaseBucketName}") | @Value("${minio.dataReleaseBucketName}") | ||||
| @@ -74,7 +82,23 @@ public class ModelsServiceImpl implements ModelsService { | |||||
| */ | */ | ||||
| @Override | @Override | ||||
| public Models queryById(Integer id) { | public Models queryById(Integer id) { | ||||
| return this.modelsDao.queryById(id); | |||||
| Models models = this.modelsDao.queryById(id); | |||||
| String modelType = models.getModelType(); | |||||
| String modelTag = models.getModelTag(); | |||||
| //去资产管理表中查询对应的图标名,注意判空逻辑,只有当dataType和dataTag不为空时,才进行查询 | |||||
| if(modelType != null && !modelType.isEmpty()){ | |||||
| AssetIcon modelTypeAssetIcon = assetIconService.queryById(Integer.valueOf(modelType)); | |||||
| if (modelTypeAssetIcon != null){ | |||||
| models.setModelTypeName(modelTypeAssetIcon.getName()); | |||||
| } | |||||
| } | |||||
| if(modelTag != null && !modelTag.isEmpty()){ | |||||
| AssetIcon modelTagAssetIcon = assetIconService.queryById(Integer.valueOf(modelTag)); | |||||
| if (modelTagAssetIcon != null){ | |||||
| models.setModelTagName(modelTagAssetIcon.getName()); | |||||
| } | |||||
| } | |||||
| return models; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -97,8 +121,9 @@ public class ModelsServiceImpl implements ModelsService { | |||||
| * @return 实例对象 | * @return 实例对象 | ||||
| */ | */ | ||||
| @Override | @Override | ||||
| public Models insert(Models models) { | |||||
| public Models insert(Models models) throws Exception { | |||||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | LoginUser loginUser = SecurityUtils.getLoginUser(); | ||||
| checkDeclaredName(models); | |||||
| models.setCreateBy(loginUser.getUsername()); | models.setCreateBy(loginUser.getUsername()); | ||||
| models.setUpdateBy(loginUser.getUsername()); | models.setUpdateBy(loginUser.getUsername()); | ||||
| models.setUpdateTime(new Date()); | models.setUpdateTime(new Date()); | ||||
| @@ -139,23 +164,24 @@ public class ModelsServiceImpl implements ModelsService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String removeById(Integer id) { | |||||
| public String removeById(Integer id) throws Exception { | |||||
| Models models = this.modelsDao.queryById(id); | Models models = this.modelsDao.queryById(id); | ||||
| if (models == null){ | if (models == null){ | ||||
| return "模型不存在"; | |||||
| throw new Exception("模型不存在"); | |||||
| } | } | ||||
| //判断权限,只有admin和创建者本身可以删除该数据集 | |||||
| //判断权限,只有admin和创建者本身可以删除该模型 | |||||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | LoginUser loginUser = SecurityUtils.getLoginUser(); | ||||
| String username = loginUser.getUsername(); | String username = loginUser.getUsername(); | ||||
| String createdBy = models.getCreateBy(); | String createdBy = models.getCreateBy(); | ||||
| if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | ||||
| return "无权限删除该模型"; | |||||
| throw new Exception("无权限删除该模型"); | |||||
| } | } | ||||
| if (modelsVersionService.queryByModelsId(id).size()>0){ | |||||
| return "请先删除该模型的版本文件"; | |||||
| //判断是否有版本文件 | |||||
| if (!modelsVersionService.queryByModelsId(id).isEmpty()){ | |||||
| throw new Exception("请先删除该模型下的版本文件"); | |||||
| } | } | ||||
| models.setState(0); | models.setState(0); | ||||
| return this.modelsDao.update(models)>0?"删除成功":"删除失败"; | return this.modelsDao.update(models)>0?"删除成功":"删除失败"; | ||||
| @@ -209,7 +235,6 @@ public class ModelsServiceImpl implements ModelsService { | |||||
| */ | */ | ||||
| @Override | @Override | ||||
| public List<Map<String, String>> uploadModels(MultipartFile[] files, String uuid) throws Exception { | public List<Map<String, String>> uploadModels(MultipartFile[] files, String uuid) throws Exception { | ||||
| List<Map<String, String>> results = new ArrayList<>(); | List<Map<String, String>> results = new ArrayList<>(); | ||||
| for (MultipartFile file:files) { | for (MultipartFile file:files) { | ||||
| // 构建objectName | // 构建objectName | ||||
| @@ -234,7 +259,6 @@ public class ModelsServiceImpl implements ModelsService { | |||||
| ModelsVersion version = modelsVersionService.queryByModelsVersion(modelsVersion); | ModelsVersion version = modelsVersionService.queryByModelsVersion(modelsVersion); | ||||
| String url = ""; | String url = ""; | ||||
| if (version == null) { | if (version == null) { | ||||
| //插表,因为这里是一次直接插表所以这里定掉date,然后用DAO插入 | //插表,因为这里是一次直接插表所以这里定掉date,然后用DAO插入 | ||||
| @@ -410,6 +434,31 @@ public class ModelsServiceImpl implements ModelsService { | |||||
| } | } | ||||
| @Override | |||||
| public void checkDeclaredName(Models insert) throws Exception { | |||||
| Models existingModel = modelsDao.findByName(insert.getName()); | |||||
| if (existingModel != null) { | |||||
| // Check if the found models is not the same as the one being inserted | |||||
| // This is important if you are using this method for both insert and update operations | |||||
| // You may need an identifier check here, e.g., if 'insert' has an ID and it's the same as 'existingDataset' | |||||
| if (insert.getId() != null && insert.getId().equals(existingModel.getId())) { | |||||
| // This is the same dataset, no duplicate name issue for update operation | |||||
| return; | |||||
| } | |||||
| // Now we know there's another dataset with the same name | |||||
| Field[] fields = Models.class.getDeclaredFields(); | |||||
| for (Field field : fields) { | |||||
| field.setAccessible(true); // Make private fields accessible | |||||
| if ("name".equals(field.getName()) && field.isAnnotationPresent(CheckDuplicate.class)) { | |||||
| // If the field is 'name' and is marked with CheckDuplicate annotation | |||||
| CheckDuplicate annotation = field.getAnnotation(CheckDuplicate.class); | |||||
| throw new Exception("重复的模型名称: " + insert.getName() + ". " + annotation.message()); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @Override | @Override | ||||
| public List<Map<String, String>> exportModels(String path, String uuid) throws Exception { | public List<Map<String, String>> exportModels(String path, String uuid) throws Exception { | ||||
| List<Map<String, String>> results = new ArrayList<>(); | List<Map<String, String>> results = new ArrayList<>(); | ||||
| @@ -31,6 +31,7 @@ | |||||
| from dataset | from dataset | ||||
| where name = #{name} and state = 1 limit 1 | where name = #{name} and state = 1 limit 1 | ||||
| </select> | </select> | ||||
| <!--查询指定行数据--> | <!--查询指定行数据--> | ||||
| <select id="queryAllByLimit" resultMap="DatasetMap"> | <select id="queryAllByLimit" resultMap="DatasetMap"> | ||||
| select | select | ||||
| @@ -24,6 +24,14 @@ | |||||
| where id = #{id} and state = 1 | where id = #{id} and state = 1 | ||||
| </select> | </select> | ||||
| <!--查询单个--> | |||||
| <select id="findByName" resultMap="ModelsMap"> | |||||
| select | |||||
| id, name, description,available_range, model_type,model_tag, create_by, create_time, update_by, update_time, state | |||||
| from models | |||||
| where name = #{name} and state = 1 limit 1 | |||||
| </select> | |||||
| <!--查询指定行数据--> | <!--查询指定行数据--> | ||||
| <select id="queryAllByLimit" resultMap="ModelsMap"> | <select id="queryAllByLimit" resultMap="ModelsMap"> | ||||
| select | select | ||||