Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
fanshuai 2024-04-28 17:25:27 +08:00
commit c6a7d74c13
49 changed files with 555 additions and 213 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -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 ( return <>{children}</>;
<>
{children}
{/* <SettingDrawer
disableUrlParams
enableDarkTheme
settings={initialState?.settings}
onSettingChange={(settings) => {
setInitialState((preInitialState) => ({
...preInitialState,
settings,
}));
}}
/> */}
</>
);
}, },
...initialState?.settings, ...initialState?.settings,
}; };

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -17,13 +17,8 @@ const HeaderDropdown: React.FC<HeaderDropdownProps> = ({ overlayClassName: cls,
}, },
}; };
}); });
return (
<Dropdown return <Dropdown overlayClassName={classNames(className, cls)} {...restProps} />;
overlayClassName={classNames(className, cls)}
getPopupContainer={(target) => target.parentElement || document.body}
{...restProps}
/>
);
}; };
export default HeaderDropdown; export default HeaderDropdown;

View File

@ -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; gotoLoginPage();
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,
}),
});
}
}; };
const actionClassName = useEmotionCss(({ token }) => { const actionClassName = useEmotionCss(({ token }) => {
return { return {

View File

@ -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 }) => { // const actionClassName = useEmotionCss(({ token }) => {
return { // return {
display: 'flex', // display: 'flex',
float: 'right', // float: 'right',
height: '48px', // height: '48px',
marginLeft: 'auto', // marginLeft: 'auto',
overflow: 'hidden', // overflow: 'hidden',
cursor: 'pointer', // cursor: 'pointer',
padding: '0 12px', // padding: '0 12px',
borderRadius: token.borderRadius, // borderRadius: token.borderRadius,
'&:hover': { // '&:hover': {
backgroundColor: token.colorBgTextHover, // 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> </span> */}
<Avatar menu={true} /> <Avatar menu={false} />
{/* <SelectLang className={actionClassName} /> */} {/* <SelectLang className={actionClassName} /> */}
</div> </div>
); );

View File

@ -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%;

View File

@ -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.dataset_type_name || '...'}</div>
<div className={Styles.tagItem}>{datasetDetailObj.data_type}</div> <div className={Styles.tagItem}>{datasetDetailObj.dataset_tag_name || '...'}</div>
</div> </div>
</div> </div>
<div className={Styles.datasetIntroCneterBox}> <div className={Styles.datasetIntroCneterBox}>

View File

@ -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;

View File

@ -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' }}

View File

@ -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

View File

@ -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="选择流水线"

View File

@ -68,19 +68,9 @@ function ExperimentText() {
}; };
const formChange = (val) => {}; const formChange = (val) => {};
const handlerClick = (e) => { const handlerClick = (e) => {
console.log(propsRef, graph, messageRef.current); if (e.target.get('name') !== 'anchor-point' && e.item) {
// let cache = []; propsRef.current.showDrawer(e, locationParams.id, messageRef.current);
// 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);
}; };
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('node:click', handlerClick);
graph.on('dblclick', 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)

View File

@ -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;

View File

@ -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>

View File

@ -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>
); );
}); });

View File

@ -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);
}, },

View File

@ -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;

View File

@ -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.model_type_name || '...'}</div>
<div className={Styles.tagItem}>{datasetDetailObj.data_type}</div> <div className={Styles.tagItem}>{datasetDetailObj.model_tag_name || '...'}</div>
</div> </div>
</div> </div>
<div className={Styles.datasetIntroCneterBox}> <div className={Styles.datasetIntroCneterBox}>

View File

@ -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 [modelTypeList, setmodelTypeList] = useState([]);
const [datasetDirectionList, setDatasetDirectionList] = 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)); setmodelTypeList(ret.data.content.filter((item) => item.category_id == 3));
setDatasetDirectionList(ret.data.content.filter((item) => item.category_id == 4)); setmodelDirectionList(ret.data.content.filter((item) => item.category_id == 4));
} else { } else {
setDatasetTypeList([]); setmodelTypeList([]);
setDatasetDirectionList([]); 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 {modelTypeList && modelTypeList.length > 0
? datasetTypeList.map((item) => { ? 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 {modelDirectionList && modelDirectionList.length > 0
? datasetDirectionList.map((item) => { ? 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">

View File

@ -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 deleteIcon from '@/assets/img/delete-icon.png';
import { Form, Input, Modal, Pagination, Radio } from 'antd'; 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

View File

@ -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(

View File

@ -128,18 +128,7 @@ const EditPipeline = () => {
}; };
const handlerClick = (e) => { const handlerClick = (e) => {
e.stopPropagation(); e.stopPropagation();
console.log(propsRef, graph); // 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);
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 || [
// 31 //
[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) => { // graph.on('dblclick', (e) => {
console.log(e.item); // console.log(e.item);
if (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);

View File

@ -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'; import { clearSessionToken, getAccessToken } from './access';
import { setRemoteMenu } from './services/session';
const checkRegion = 5 * 60 * 1000; 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(); const accessToken = getAccessToken();
if (expireTime) { if (accessToken) {
const left = Number(expireTime) - new Date().getTime(); headers['Authorization'] = `Bearer ${accessToken}`;
const refreshToken = getRefreshToken();
if (left < checkRegion && refreshToken) {
if (left < 0) {
clearSessionToken();
}
} else {
const accessToken = getAccessToken();
if (accessToken) {
headers['Authorization'] = `Bearer ${accessToken}`;
}
}
} else {
clearSessionToken();
} }
// 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) {
if (status >= 200 && status < 300 && data && (data instanceof Blob || data.code === 200)) { if (data && (data instanceof Blob || data.code === 200)) {
return response; return response;
} else { } else if (data && data.code === 401) {
if (data && data.msg) { clearSessionToken();
message.error(data.msg); 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);
} }
}, },

View File

@ -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',
});
}

View File

@ -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,

View File

@ -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', { return request<Record<string, any>>('/api/auth/logout', {
method: 'delete', method: 'DELETE',
}); });
} }

View File

@ -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; // 页面背景颜色

View File

@ -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;

View File

@ -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,
});
}
};

View File

@ -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));
} }

View File

@ -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));
} }

View File

@ -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;
}
} }

View File

@ -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);

View File

@ -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);
/** /**
* 查询指定行数据 * 查询指定行数据
* *

View File

@ -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;

View File

@ -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;
} }

View File

@ -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(); if (dataset != null) {
String dataTag = dataset.getDataTag(); 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;
// 判空逻辑只有当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) {

View File

@ -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);
//得到镜像文件大小 //得到镜像文件大小

View File

@ -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<>();

View File

@ -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

View File

@ -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