Browse Source

Merge remote-tracking branch 'origin/dev'

master-arm
chenzhihang 1 year ago
parent
commit
4a4ab6620f
20 changed files with 422 additions and 290 deletions
  1. +12
    -6
      react-ui/src/pages/AutoML/List/index.tsx
  2. +9
    -1
      react-ui/src/pages/AutoML/components/CreateForm/TrialConfig.tsx
  3. +1
    -5
      react-ui/src/pages/AutoML/components/ExperimentInstance/index.less
  4. +1
    -1
      react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx
  5. +1
    -1
      react-ui/src/pages/Dataset/components/AddModelModal/index.tsx
  6. +1
    -1
      react-ui/src/pages/Dataset/components/AddVersionModal/index.tsx
  7. +3
    -3
      react-ui/src/pages/DevelopmentEnvironment/components/CreateMirrorModal/index.tsx
  8. +1
    -5
      react-ui/src/pages/Experiment/components/ExperimentInstance/index.less
  9. +12
    -1
      react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx
  10. +0
    -9
      react-ui/src/pages/Experiment/components/ExperimentStatusCell/index.less
  11. +110
    -67
      react-ui/src/pages/Experiment/index.jsx
  12. +16
    -23
      react-ui/src/pages/Experiment/index.less
  13. +3
    -3
      react-ui/src/pages/Mirror/Create/index.tsx
  14. +25
    -3
      react-ui/src/pages/ModelDeployment/CreateVersion/index.less
  15. +97
    -80
      react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx
  16. +2
    -2
      react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx
  17. +2
    -0
      react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx
  18. +109
    -57
      react-ui/src/pages/Pipeline/index.jsx
  19. +16
    -21
      react-ui/src/pages/Pipeline/index.less
  20. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/WorkflowServiceImpl.java

+ 12
- 6
react-ui/src/pages/AutoML/List/index.tsx View File

@@ -26,6 +26,7 @@ import {
ConfigProvider,
Input,
Table,
Tooltip,
type TablePaginationConfig,
type TableProps,
} from 'antd';
@@ -276,13 +277,18 @@ function AutoMLList() {
{newText && newText.length > 0
? newText.map((item, index) => {
return (
<img
style={{ width: '17px', marginRight: '6px' }}
<Tooltip
key={index}
src={experimentStatusInfo[item as ExperimentStatus].icon}
draggable={false}
alt=""
/>
placement="top"
title={experimentStatusInfo[item as ExperimentStatus].label}
>
<img
style={{ width: '17px', marginRight: '6px' }}
src={experimentStatusInfo[item as ExperimentStatus].icon}
draggable={false}
alt=""
/>
</Tooltip>
);
})
: null}


+ 9
- 1
react-ui/src/pages/AutoML/components/CreateForm/TrialConfig.tsx View File

@@ -1,5 +1,6 @@
import SubAreaTitle from '@/components/SubAreaTitle';
import { AutoMLTaskType } from '@/enums';
import { modalConfirm } from '@/utils/ui';
import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
import { Button, Col, Flex, Form, Input, InputNumber, Radio, Row, Select } from 'antd';
import { classificationMetrics, regressionMetrics } from './ExecuteConfig';
@@ -72,8 +73,15 @@ function TrialConfig() {
shape="circle"
size="middle"
type="text"
onClick={() => remove(name)}
icon={<MinusCircleOutlined />}
onClick={() => {
modalConfirm({
title: '确定要删除该指标权重吗?',
onOk: () => {
remove(name);
},
});
}}
></Button>
{index === fields.length - 1 && (
<Button


+ 1
- 5
react-ui/src/pages/AutoML/components/ExperimentInstance/index.less View File

@@ -54,13 +54,9 @@
width: 200px;

.statusIcon {
visibility: hidden;
transition: all 0.2s;
visibility: visible;
}
}
.statusBox:hover .statusIcon {
visibility: visible;
}
}

.loadMoreBox {


+ 1
- 1
react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx View File

@@ -125,7 +125,7 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: '版本只支持字母、数字、点、下划线、中横线',
message: '版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
{
validator: (_rule, value) => {


+ 1
- 1
react-ui/src/pages/Dataset/components/AddModelModal/index.tsx View File

@@ -108,7 +108,7 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: '版本只支持字母、数字、点、下划线、中横线',
message: '版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
{
validator: (_rule, value) => {


+ 1
- 1
react-ui/src/pages/Dataset/components/AddVersionModal/index.tsx View File

@@ -131,7 +131,7 @@ function AddVersionModal({
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: '版本只支持字母、数字、点、下划线、中横线',
message: '版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
{
validator: (_rule, value) => {


+ 3
- 3
react-ui/src/pages/DevelopmentEnvironment/components/CreateMirrorModal/index.tsx View File

@@ -51,8 +51,8 @@ function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) {
message: '请输入镜像名称',
},
{
pattern: /^[a-zA-Z0-9_-]*$/,
message: '只支持字母、数字、下划线、中横线',
pattern: /^[a-z0-9/_-]*$/,
message: '只支持小写字母、数字、下划线(_)、中横线(-)、斜杠(/)',
},
]}
>
@@ -68,7 +68,7 @@ function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) {
},
{
pattern: /^[a-zA-Z0-9_-]*$/,
message: '只支持字母、数字、下划线、中横线',
message: '只支持字母、数字、下划线(_)、中横线(-)',
},
]}
>


+ 1
- 5
react-ui/src/pages/Experiment/components/ExperimentInstance/index.less View File

@@ -57,13 +57,9 @@
width: 200px;

.statusIcon {
visibility: hidden;
transition: all 0.2s;
visibility: visible;
}
}
.statusBox:hover .statusIcon {
visibility: visible;
}
}

.loadMoreBox {


+ 12
- 1
react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx View File

@@ -98,6 +98,17 @@ function ExperimentInstanceComponent({
}
};

// 终止实验实例
const handleTerminate = (instance: ExperimentInstance) => {
modalConfirm({
title: '确定要终止此次实验运行吗?',
isDelete: false,
onOk: () => {
terminateExperimentInstance(instance);
},
});
};

// 终止实验实例
const terminateExperimentInstance = async (instance: ExperimentInstance) => {
const [res] = await to(putQueryByExperimentInsId(instance.id));
@@ -202,7 +213,7 @@ function ExperimentInstanceComponent({
item.status === ExperimentStatus.Terminated
}
icon={<KFIcon type="icon-zhongzhi" />}
onClick={() => terminateExperimentInstance(item)}
onClick={() => handleTerminate(item)}
>
终止
</Button>


+ 0
- 9
react-ui/src/pages/Experiment/components/ExperimentStatusCell/index.less View File

@@ -1,12 +1,3 @@
.experiment-status-cell {
height: 100%;
&__label {
display: none;
}
}

.experiment-status-cell:hover {
.experiment-status-cell__label {
display: inline;
}
}

+ 110
- 67
react-ui/src/pages/Experiment/index.jsx View File

@@ -1,5 +1,7 @@
import KFIcon from '@/components/KFIcon';
import PageTitle from '@/components/PageTitle';
import { ExperimentStatus, TensorBoardStatus } from '@/enums';
import { useCacheState } from '@/hooks/pageCacheState';
import {
deleteExperimentById,
getExperiment,
@@ -16,14 +18,14 @@ import themes from '@/styles/theme.less';
import { to } from '@/utils/promise';
import tableCellRender, { TableCellValueType } from '@/utils/table';
import { modalConfirm } from '@/utils/ui';
import { App, Button, ConfigProvider, Dropdown, Space, Table } from 'antd';
import { App, Button, ConfigProvider, Dropdown, Input, Space, Table, Tooltip } from 'antd';
import classNames from 'classnames';
import { useEffect, useRef, useState } from 'react';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { ComparisonType } from './Comparison/config';
import AddExperimentModal from './components/AddExperimentModal';
import ExperimentInstance from './components/ExperimentInstance';
import Styles from './index.less';
import styles from './index.less';
import { experimentStatusInfo } from './status';

// 定时器
@@ -47,32 +49,34 @@ function Experiment() {
const [isModalOpen, setIsModalOpen] = useState(false);
const [addFormData, setAddFormData] = useState({});
const [experimentInsTotal, setExperimentInsTotal] = useState(0);
const [cacheState, setCacheState] = useCacheState();
const [searchText, setSearchText] = useState(cacheState?.searchText);
const [inputText, setInputText] = useState(cacheState?.searchText);
const [pagination, setPagination] = useState(
cacheState?.pagination ?? {
current: 1,
pageSize: 10,
},
);
const { message } = App.useApp();
const pageOption = useRef({ page: 1, size: 10 });
const paginationProps = {
showSizeChanger: true,
showQuickJumper: true,
showTotal: () => `共${total}条`,
total: total,
page: pageOption.current.page,
size: pageOption.current.size,
onChange: (current, size) => paginationChange(current, size),
};

useEffect(() => {
getExperimentList();
getWorkflowList();
return () => {
clearExperimentInTimers();
};
}, []);

useEffect(() => {
getExperimentList();
}, [pagination, searchText]);

// 获取实验列表
const getExperimentList = async () => {
const params = {
offset: 0,
page: pageOption.current.page - 1,
size: pageOption.current.size,
page: pagination.current - 1,
size: pagination.pageSize,
name: searchText || undefined,
};
const [res] = await to(getExperiment(params));
if (res && res.data && Array.isArray(res.data.content)) {
@@ -94,6 +98,15 @@ function Experiment() {
}
};

// 搜索
const onSearch = (value) => {
setSearchText(value);
setPagination((prev) => ({
...prev,
current: 1,
}));
};

// 获取实验实例列表
const getQueryByExperiment = async (experimentId, page) => {
const params = {
@@ -259,11 +272,11 @@ function Experiment() {
};

// 当前页面切换
const paginationChange = async (current, size) => {
pageOption.current = {
page: current,
size: size,
};
const paginationChange = async (current, pageSize) => {
setPagination({
current,
pageSize,
});
getExperimentList();
};
// 运行实验
@@ -278,14 +291,23 @@ function Experiment() {
}
};

// 跳转, 缓存当前状态
const navigateToUrl = (url) => {
setCacheState({
pagination,
searchText,
});
navigate(url);
};

// 跳转到流水线
const gotoPipeline = (record) => {
navigate({ pathname: `/pipeline/template/info/${record.workflow_id}` });
navigateToUrl(`/pipeline/template/info/${record.workflow_id}`);
};

// 跳转到实验实例详情
const gotoInstanceInfo = (item, record) => {
navigate({ pathname: `/pipeline/experiment/instance/${record.workflow_id}/${item.id}` });
navigateToUrl(`/pipeline/experiment/instance/${record.workflow_id}/${item.id}`);
};

// 处理 TensorBoard 操作
@@ -340,7 +362,7 @@ function Experiment() {
},
],
onClick: ({ key }) => {
navigate(`/pipeline/experiment/compare?type=${key}&id=${experimentId}`);
navigateToUrl(`/pipeline/experiment/compare?type=${key}&id=${experimentId}`);
},
};
};
@@ -392,13 +414,14 @@ function Experiment() {
{newText && newText.length > 0
? newText.map((item, index) => {
return (
<img
style={{ width: '17px', marginRight: '6px' }}
key={index}
src={experimentStatusInfo[item].icon}
draggable={false}
alt=""
/>
<Tooltip key={index} placement="top" title={experimentStatusInfo[item].label}>
<img
style={{ width: '17px', marginRight: '6px' }}
src={experimentStatusInfo[item].icon}
draggable={false}
alt=""
/>
</Tooltip>
);
})
: null}
@@ -479,41 +502,61 @@ function Experiment() {
},
];
return (
<div className={Styles.experimentBox}>
<div className={Styles.experimentTopBox}>
<Button type="default" onClick={createExperiment} icon={<KFIcon type="icon-xinjian2" />}>
新建实验
</Button>
</div>
<div className={classNames('vertical-scroll-table', Styles.experimentTable)}>
<Table
columns={columns}
dataSource={experimentList}
pagination={paginationProps}
rowKey="id"
scroll={{ y: 'calc(100% - 55px)' }}
expandable={{
expandedRowRender: (record) => (
<ExperimentInstance
experimentInList={experimentInList}
experimentInsTotal={experimentInsTotal}
onClickInstance={(item) => gotoInstanceInfo(item, record)}
onClickTensorBoard={handleTensorboard}
onRemove={() => {
refreshExperimentIns(record.id);
refreshExperimentList();
}}
onTerminate={handleInstanceTerminate}
onLoadMore={() => loadMoreExperimentIns()}
></ExperimentInstance>
),
onExpand: (e, a) => {
expandChange(e, a);
},
expandedRowKeys: [expandedRowKeys],
rowExpandable: (record) => true,
}}
/>
<div className={styles['experiment-list']}>
<PageTitle title="实验列表"></PageTitle>
<div className={styles['experiment-list__content']}>
<div className={styles['experiment-list__content__filter']}>
<Input.Search
placeholder="按实验名称筛选"
onSearch={onSearch}
onChange={(e) => setInputText(e.target.value)}
style={{ width: 300 }}
value={inputText}
allowClear
/>
<Button type="default" onClick={createExperiment} icon={<KFIcon type="icon-xinjian2" />}>
新建实验
</Button>
</div>
<div
className={classNames('vertical-scroll-table', styles['experiment-list__content__table'])}
>
<Table
columns={columns}
dataSource={experimentList}
pagination={{
...pagination,
total: total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: () => `共${total}条`,
onChange: paginationChange,
}}
rowKey="id"
scroll={{ y: 'calc(100% - 55px)' }}
expandable={{
expandedRowRender: (record) => (
<ExperimentInstance
experimentInList={experimentInList}
experimentInsTotal={experimentInsTotal}
onClickInstance={(item) => gotoInstanceInfo(item, record)}
onClickTensorBoard={handleTensorboard}
onRemove={() => {
refreshExperimentIns(record.id);
refreshExperimentList();
}}
onTerminate={handleInstanceTerminate}
onLoadMore={() => loadMoreExperimentIns()}
></ExperimentInstance>
),
onExpand: (e, a) => {
expandChange(e, a);
},
expandedRowKeys: [expandedRowKeys],
rowExpandable: (record) => true,
}}
/>
</div>
</div>

{isModalOpen && (


+ 16
- 23
react-ui/src/pages/Experiment/index.less View File

@@ -1,28 +1,21 @@
.experimentBox {
.experiment-list {
height: 100%;
.experimentTopBox {
display: flex;
align-items: center;
justify-content: flex-end;
width: 100%;
height: 49px;
margin-bottom: 10px;
padding-right: 30px;
background-image: url(@/assets/img/page-title-bg.png);
background-repeat: no-repeat;
background-position: top center;
background-size: 100% 100%;
}
.experimentTable {
&__content {
height: calc(100% - 60px);
:global {
.ant-table-wrapper .ant-table {
// overflow-y: auto;
height: calc(100% - 48px);
}
.ant-table-row-expand-icon-cell {
padding: 0 30px;
}
margin-top: 10px;
padding: 20px 30px 0;
background-color: white;
border-radius: 10px;

&__filter {
display: flex;
align-items: center;
justify-content: space-between;
}

&__table {
height: calc(100% - 32px - 28px);
margin-top: 28px;
}
}
}

+ 3
- 3
react-ui/src/pages/Mirror/Create/index.tsx View File

@@ -150,8 +150,8 @@ function MirrorCreate() {
message: '请输入镜像名称',
},
{
pattern: /^[a-zA-Z0-9_-]*$/,
message: '只支持字母、数字、下划线、中横线',
pattern: /^[a-z0-9/_-]*$/,
message: '只支持小写字母、数字、下划线(_)、中横线(-)、斜杠(/)',
},
]}
>
@@ -178,7 +178,7 @@ function MirrorCreate() {
},
{
pattern: /^[a-zA-Z0-9_-]*$/,
message: '只支持字母、数字、下划线、中横线',
message: '只支持字母、数字、下划线(_)、中横线(-)',
},
]}
>


+ 25
- 3
react-ui/src/pages/ModelDeployment/CreateVersion/index.less View File

@@ -11,9 +11,31 @@
background-color: white;
border-radius: 10px;

&__type {
color: @text-color;
font-size: @font-size-input-lg;
&__env {
margin-bottom: 0 !important;

// 增加样式权重
& &__add-btn {
border-color: .addAlpha(@primary-color, 0.5) [];
box-shadow: none !important;
&:hover {
border-style: solid;
}
}
}

:global {
.ant-btn.ant-btn-icon-only .anticon {
font-size: 20px;
}

.ant-btn-variant-text:disabled {
color: rgba(0, 0, 0, 0.25);
}

.ant-btn-variant-text {
color: #565658;
}
}
}
}

+ 97
- 80
react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx View File

@@ -4,11 +4,10 @@
* @Description: 创建服务版本
*/
import CodeSelect from '@/components/CodeSelect';
import KFIcon from '@/components/KFIcon';
import PageTitle from '@/components/PageTitle';
import ResourceSelect, {
requiredValidator,
ResourceSelectorType,
requiredValidator,
type ParameterInputObject,
} from '@/components/ResourceSelect';
import SubAreaTitle from '@/components/SubAreaTitle';
@@ -22,7 +21,7 @@ import { changePropertyName } from '@/utils';
import { to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
import { modalConfirm } from '@/utils/ui';
import { PlusOutlined } from '@ant-design/icons';
import { MinusCircleOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { useNavigate, useParams } from '@umijs/max';
import { App, Button, Col, Flex, Form, Input, InputNumber, Row, Select } from 'antd';
import { omit, pick } from 'lodash';
@@ -411,7 +410,7 @@ function CreateServiceVersion() {
{
pattern: /^\/[a-zA-Z0-9._/-]+$/,
message:
'请输入正确的挂载路径,以 / 开头,只支持字母、数字、点、下划线、中横线、斜杠',
'请输入正确的挂载路径,以斜杠(/)开头,只支持字母、数字、点(.)、下划线(_)、中横线(-)、斜杠(/)',
},
]}
>
@@ -426,82 +425,100 @@ function CreateServiceVersion() {
</Col>
</Row>

<Form.List name="env_variables">
{(fields, { add, remove }) => (
<>
<Row gutter={8}>
<Col span={10}>
<Form.Item label="环境变量">
{fields.length === 0 ? (
<Button
type="link"
style={{ padding: '0' }}
onClick={() => add()}
disabled={disabled}
>
添加环境变量
</Button>
) : null}
</Form.Item>
</Col>
</Row>
{fields.map(({ key, name, ...restField }) => (
<Flex key={key} align="center" gap="0 8px" style={{ width: '50%' }}>
<Form.Item
{...restField}
name={[name, 'key']}
style={{ flex: 1 }}
rules={[
{ required: true, message: '请输入变量名' },
{
pattern: /^[a-zA-Z_][a-zA-Z0-9_-]*$/,
message:
'变量名只支持字母、数字、下划线、中横线且开头必须是字母或下划线',
},
]}
>
<Input placeholder="请输入变量名" disabled={disabled} />
</Form.Item>
<span style={{ marginBottom: '24px' }}>=</span>
<Form.Item
{...restField}
name={[name, 'value']}
style={{ flex: 1 }}
rules={[{ required: true, message: '请输入变量值' }]}
>
<Input placeholder="请输入变量值" disabled={disabled} />
</Form.Item>
<Button
type="link"
style={{ marginBottom: '24px' }}
icon={<KFIcon type="icon-shanchu" font={16} />}
disabled={disabled}
onClick={() => {
modalConfirm({
title: '删除',
content: '是否确认要删除该环境变量?',
onOk: () => {
remove(name);
},
});
}}
></Button>
</Flex>
))}
{fields.length > 0 ? (
<Button
type="link"
style={{ padding: '0', margin: '-24px 0 24px' }}
onClick={() => add()}
icon={<PlusOutlined />}
disabled={disabled}
>
环境变量
</Button>
) : null}
</>
)}
</Form.List>
<Row gutter={8}>
<Col span={10}>
<Form.Item label="环境变量">
<Form.List name="env_variables">
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }, index) => (
<Flex key={key} align="center" gap="0 8px">
<Form.Item
{...restField}
name={[name, 'key']}
style={{ flex: 1 }}
rules={[
{ required: true, message: '请输入变量名' },
{
pattern: /^[a-zA-Z_][a-zA-Z0-9_-]*$/,
message:
'变量名只支持字母、数字、下划线、中横线且开头必须是字母或下划线',
},
]}
>
<Input placeholder="请输入变量名" disabled={disabled} />
</Form.Item>
<span style={{ marginBottom: '24px' }}>=</span>
<Form.Item
{...restField}
name={[name, 'value']}
style={{ flex: 1 }}
rules={[{ required: true, message: '请输入变量值' }]}
>
<Input placeholder="请输入变量值" disabled={disabled} />
</Form.Item>
<Flex
style={{
width: '76px',
marginLeft: '10px',
height: '46px',
marginBottom: '24px',
}}
align="center"
>
<Button
style={{
marginRight: '3px',
}}
shape="circle"
size="middle"
type="text"
icon={<MinusCircleOutlined />}
disabled={disabled}
onClick={() => {
modalConfirm({
title: '确定要删除该环境变量吗?',
onOk: () => {
remove(name);
},
});
}}
></Button>
{index === fields.length - 1 && (
<Button
shape="circle"
size="middle"
type="text"
disabled={disabled}
icon={<PlusCircleOutlined />}
onClick={() => add()}
></Button>
)}
</Flex>
</Flex>
))}
{fields.length === 0 && (
<Form.Item className={styles['create-service-version__content__env']}>
<Button
className={styles['create-service-version__content__env__add-btn']}
color="primary"
variant="dashed"
disabled={disabled}
icon={<PlusOutlined />}
block
onClick={() => add()}
>
环境变量
</Button>
</Form.Item>
)}
</>
)}
</Form.List>
</Form.Item>
</Col>
</Row>

<Form.Item wrapperCol={{ offset: 0, span: 16 }}>
<Button type="primary" htmlType="submit">
{buttonText}


+ 2
- 2
react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx View File

@@ -179,8 +179,8 @@ function ServiceInfo() {
// 处理停止
const handleServiceVersionStop = async (record: ServiceVersionData) => {
modalConfirm({
title: '停止',
content: '是否确认停止该服务?',
title: '是否确认停止该服务?',
isDelete: false,
onOk: () => {
stopServiceVersion(record);
},


+ 2
- 0
react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx View File

@@ -163,6 +163,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
showValue: res.code_repo_name,
fromSelect: true,
});
form.validateFields([formItemName]);
}
close();
},
@@ -278,6 +279,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
// 参数回填
const handleParameterClick = (name: NamePath, value: any) => {
form.setFieldValue(name, value);
form.validateFields([name]);
};

// form item label


+ 109
- 57
react-ui/src/pages/Pipeline/index.jsx View File

@@ -1,5 +1,7 @@
import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
import PageTitle from '@/components/PageTitle';
import { useCacheState } from '@/hooks/pageCacheState';
import {
addWorkflow,
cloneWorkflow,
@@ -13,9 +15,9 @@ import tableCellRender, { TableCellValueType } from '@/utils/table';
import { modalConfirm } from '@/utils/ui';
import { App, Button, ConfigProvider, Form, Input, Space, Table } from 'antd';
import classNames from 'classnames';
import { useEffect, useRef, useState } from 'react';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Styles from './index.less';
import styles from './index.less';

const { TextArea } = Input;
const Pipeline = () => {
@@ -26,7 +28,46 @@ const Pipeline = () => {
const [pipeList, setPipeList] = useState([]);
const [total, setTotal] = useState(0);
const [isModalOpen, setIsModalOpen] = useState(false);
const [cacheState, setCacheState] = useCacheState();
const [searchText, setSearchText] = useState(cacheState?.searchText);
const [inputText, setInputText] = useState(cacheState?.searchText);
const [pagination, setPagination] = useState(
cacheState?.pagination ?? {
current: 1,
pageSize: 10,
},
);
const { message } = App.useApp();

useEffect(() => {
getList();
}, [pagination, searchText]);

// 获取流水线模板列表
const getList = () => {
const params = {
page: pagination.current - 1,
size: pagination.pageSize,
name: searchText || undefined,
};
getWorkflow(params).then((res) => {
if (res && res.data && Array.isArray(res.data.content)) {
setPipeList(res.data.content);
setTotal(res.data.totalElements);
}
});
};

// 搜索
const onSearch = (value) => {
setSearchText(value);
setPagination((prev) => ({
...prev,
current: 1,
}));
};

// 编辑
const editTable = (e, record) => {
e.stopPropagation();
getWorkflowById(record.id).then((ret) => {
@@ -39,18 +80,35 @@ const Pipeline = () => {
}
});
};
const routeToEdit = (record) => {
navigate({ pathname: `/pipeline/template/info/${record.id}` });

// 跳转, 缓存当前状态
const navigateToUrl = (url) => {
setCacheState({
pagination,
searchText,
});
navigate(url);
};

// 查看详情
const gotoDetail = (record) => {
navigateToUrl(`/pipeline/template/info/${record.id}`);
};

// 显示 modal
const showModal = () => {
form.resetFields();
setFormId(null);
setDialogTitle('新建流水线');
setIsModalOpen(true);
};

// modal 取消
const handleCancel = () => {
setIsModalOpen(false);
};

// 表单提交
const onFinish = (values) => {
if (formId) {
editWorkflow({ ...values, id: formId }).then((ret) => {
@@ -63,47 +121,21 @@ const Pipeline = () => {
setIsModalOpen(false);
message.success('新建成功');
if (ret.code === 200) {
navigate({ pathname: `/pipeline/template/info/${ret.data.id}` });
navigateToUrl(`/pipeline/template/info/${ret.data.id}`);
}
});
}
};
const pageOption = useRef({ page: 1, size: 10 });
const paginationProps = {
showSizeChanger: true,
showQuickJumper: true,
showTotal: () => `共${total}条`,
total: total,
page: pageOption.current.page,
size: pageOption.current.size,
onChange: (current, size) => paginationChange(current, size),
};

// 当前页面切换
const paginationChange = async (current, size) => {
// console.log('page', current, size);
pageOption.current = {
page: current,
size: size,
};
const paginationChange = async (current, pageSize) => {
setPagination({
current,
pageSize,
});
getList();
};
const getList = () => {
let params = {
offset: 1,
page: pageOption.current.page - 1,
size: pageOption.current.size,
};
getWorkflow(params).then((ret) => {
if (ret.code === 200) {
setPipeList(ret.data.content);

setTotal(ret.data.totalElements);
}
});
};
useEffect(() => {
getList();
}, []);
const columns = [
{
title: '序号',
@@ -112,8 +144,8 @@ const Pipeline = () => {
width: 120,
align: 'center',
render: tableCellRender(false, TableCellValueType.Index, {
page: pageOption.current.page - 1,
pageSize: pageOption.current.size,
page: pagination.current - 1,
pageSize: pagination.pageSize,
}),
},
{
@@ -121,7 +153,7 @@ const Pipeline = () => {
dataIndex: 'name',
key: 'name',
render: tableCellRender(false, TableCellValueType.Link, {
onClick: routeToEdit,
onClick: gotoDetail,
}),
},
{
@@ -167,10 +199,10 @@ const Pipeline = () => {
icon={<KFIcon type="icon-fuzhi" />}
onClick={async () => {
modalConfirm({
title: '复制',
content: '确定复制该条流水线吗?',
title: '确定复制该条流水线吗?',
okText: '确认',
cancelText: '取消',
isDelete: false,
onOk: () => {
cloneWorkflow(record.id).then((ret) => {
if (ret.code === 200) {
@@ -235,27 +267,47 @@ const Pipeline = () => {
},
];
return (
<div className={Styles.PipelineBox}>
<div className={Styles.pipelineTopBox}>
<Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}>
新建流水线
</Button>
</div>
<div className={classNames('vertical-scroll-table', Styles.PipelineTable)}>
<Table
columns={columns}
dataSource={pipeList}
pagination={paginationProps}
rowKey="id"
scroll={{ y: 'calc(100% - 55px)' }}
/>
<div className={styles['pipeline-list']}>
<PageTitle title="流水线模板列表"></PageTitle>
<div className={styles['pipeline-list__content']}>
<div className={styles['pipeline-list__content__filter']}>
<Input.Search
placeholder="按流水线名称筛选"
onSearch={onSearch}
onChange={(e) => setInputText(e.target.value)}
style={{ width: 300 }}
value={inputText}
allowClear
/>
<Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}>
新建流水线
</Button>
</div>
<div
className={classNames('vertical-scroll-table', styles['pipeline-list__content__table'])}
>
<Table
columns={columns}
dataSource={pipeList}
pagination={{
...pagination,
total: total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: () => `共${total}条`,
onChange: paginationChange,
}}
rowKey="id"
scroll={{ y: 'calc(100% - 55px)' }}
/>
</div>
</div>
<KFModal
title={dialogTitle}
image={require('@/assets/img/create-experiment.png')}
width={825}
open={isModalOpen}
className={Styles.modal}
className={styles.modal}
okButtonProps={{
htmlType: 'submit',
form: 'form',


+ 16
- 21
react-ui/src/pages/Pipeline/index.less View File

@@ -1,26 +1,21 @@
.pipelineTopBox {
display: flex;
align-items: center;
justify-content: flex-end;
width: 100%;
height: 49px;
margin-bottom: 10px;
padding-right: 30px;
background-image: url(@/assets/img/page-title-bg.png);
background-repeat: no-repeat;
background-position: top left;
background-size: 100% 100%;
}

.PipelineBox {
.pipeline-list {
height: 100%;
.PipelineTable {
&__content {
height: calc(100% - 60px);
:global {
.ant-table-wrapper .ant-table {
// overflow-y: auto;
height: calc(100% - 48px);
}
margin-top: 10px;
padding: 20px 30px 0;
background-color: white;
border-radius: 10px;

&__filter {
display: flex;
align-items: center;
justify-content: space-between;
}

&__table {
height: calc(100% - 32px - 28px);
margin-top: 28px;
}
}
}

+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/WorkflowServiceImpl.java View File

@@ -351,7 +351,7 @@ public class WorkflowServiceImpl implements WorkflowService {
Map<String, Object> paramMap = JsonUtils.jsonToMap(inParameters);
Map<String, Object> model = (Map<String, Object>) paramMap.get("--model_name");
String value = (String) model.get("value");
if (!value.startsWith("{{")) {
if (StringUtils.isNotEmpty(value) && !value.startsWith("{{")) {
Map<String, Object> valueMap = JsonUtils.jsonToMap(value);
insertAssetWorkFlow(workflowId, workflowName, Long.valueOf((String) valueMap.get("id")), (String) valueMap.get("version"), Constant.Asset_Type_Model);
}


Loading…
Cancel
Save