-
- 启动时间:{momnet(message.create_time).format('YYYY-MM-DD HH:mm:ss')}
-
-
- 执行时长:
- {message.finish_time
- ? elapsedTime(new Date(message.create_time), new Date(message.finish_time))
- : elapsedTime(new Date(message.create_time), new Date())}
-
-
- 状态:
-
-
- {experimentStatusInfo[message.status]?.label}
-
-
-
+
+
+
+ 启动时间:{formatDate(message.create_time)}
+
+
+ 执行时长:
+ {elapsedTime(message.create_time, message.finish_time)}
+
+
+ 状态:
+
+
+ {experimentStatusInfo[message.status]?.label}
+
-
+
-
+
+
.ant-tabs-nav .ant-tabs-nav-list{
- margin-left: 24px;
- }
- .ant-drawer .ant-drawer-body{
- overflow-y: hidden;
+ background-color: #fff;
+
+ &__top {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ height: 56px;
+ padding: 0 30px;
+ background: #ffffff;
+ box-shadow: 0px 3px 6px rgba(146, 146, 146, 0.09);
+
+ &__info {
+ display: flex;
+ align-items: center;
+ margin-right: 30px;
+ color: rgba(29, 29, 32, 0.8);
+ font-size: 15px;
}
- .ant-tabs {
- height: calc(100% - 160px);
- overflow-y: auto;
+ &__param-button {
+ margin-right: 0;
+ margin-left: auto;
}
}
-
-}
-.detailBox {
- display: flex;
- align-items: center;
- margin-bottom: 15px;
- color: #1d1d20;
- font-size: 15px;
- padding-left: 24px;
-
-}
-.allMessageItem {
- display: flex;
- align-items: center;
- margin-right: 30px;
- color: rgba(29, 29, 32, 0.8);
- font-size: 15px;
-}
-.param_button {
- margin-right: 0;
- margin-left: auto;
-}
-.resultTop {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 10px 0;
- border-bottom: 1px solid #eee;
-}
-.resultContent {
- display: flex;
-
- align-items: center;
- justify-content: space-between;
- width: 100%;
- margin-bottom: 10px;
- padding: 0 20px 0 0;
+ &__graph {
+ width: 100%;
+ height: calc(100% - 56px);
+ background-color: @background-color;
+ }
}
diff --git a/react-ui/src/pages/Experiment/experimentText/logGroup.less b/react-ui/src/pages/Experiment/experimentText/logGroup.less
deleted file mode 100644
index 4eee30d3..00000000
--- a/react-ui/src/pages/Experiment/experimentText/logGroup.less
+++ /dev/null
@@ -1,34 +0,0 @@
-.log_group {
- padding-bottom: 10px;
-}
-
-.log_group_pod {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 15px;
- background: rgba(234, 234, 234, 0.5);
- cursor: pointer;
-
- &_name {
- margin-right: 10px;
- color: #1d1d20;
- font-size: 14px;
- }
-}
-
-.log_group_detail {
- padding: 15px;
- color: white;
- font-size: 14px;
- white-space: pre-line;
- word-break: break-all;
- background: #19253b;
-}
-
-.log_group_more_button {
- display: flex;
- justify-content: center;
- color: white;
- background: #19253b;
-}
diff --git a/react-ui/src/pages/Experiment/experimentText/props.jsx b/react-ui/src/pages/Experiment/experimentText/props.jsx
deleted file mode 100644
index d5ac7999..00000000
--- a/react-ui/src/pages/Experiment/experimentText/props.jsx
+++ /dev/null
@@ -1,439 +0,0 @@
-import { getNodeResult, getQueryByExperimentLog } from '@/services/experiment/index.js';
-import { elapsedTime } from '@/utils/date';
-import { downLoadZip } from '@/utils/downloadfile';
-import { DatabaseOutlined, ProfileOutlined } from '@ant-design/icons';
-import { Drawer, Form, Input, Tabs, message } from 'antd';
-import moment from 'moment';
-import { forwardRef, useImperativeHandle, useState } from 'react';
-import LogList from './LogList';
-import Styles from './index.less';
-const { TextArea } = Input;
-const Props = forwardRef(({ onParentChange }, ref) => {
- const [form] = Form.useForm();
- const [stagingItem, setStagingItem] = useState({});
- const [resultObj, setResultObj] = useState([]);
- const [logList, setLogList] = useState([]);
- const statusObj = {
- Running: '运行中',
- Succeeded: '成功',
- Pending: '等待中',
- Failed: '失败',
- Error: '错误',
- Terminated: '终止',
- Skipped: '未执行',
- Omitted: '未执行',
- };
- const statusColorObj = {
- Running: '#165bff',
- Succeeded: '#63a728',
- Pending: '#f981eb',
- Failed: '#c73131',
- Error: '#c73131',
- Terminated: '#8a8a8a',
- Skipped: '#8a8a8a',
- Omitted: '#8a8a8ae',
- };
- const exportResult = (e, val) => {
- const hide = message.loading('正在下载');
- hide();
- downLoadZip(`/api/mmp/minioStorage/download`, { path: val });
- };
- const timers = (time) => {
- let timer = new Date(time);
- let hours = timer.getHours(); //转换成时
- let minutes = timer.getMinutes(); //转换成分
- let secend = timer.getSeconds(); //转换成秒
-
- let str = `${minutes}分${secend}秒`;
- return str;
- };
- const items = [
- {
- key: '1',
- label: '日志详情',
- children: ,
- icon: ,
- },
- {
- key: '2',
- label: '配置参数',
- icon: ,
- children: (
-
-
-
-
-
-
-
-

- 任务信息
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {stagingItem.control_strategy &&
- Object.keys(stagingItem.control_strategy) &&
- Object.keys(stagingItem.control_strategy).length > 0
- ? Object.keys(stagingItem.control_strategy).map((item) => (
-
-
-
- ))
- : ''}
-
-

- 输入参数
-
- {stagingItem.in_parameters &&
- Object.keys(stagingItem.in_parameters) &&
- Object.keys(stagingItem.in_parameters).length > 0
- ? Object.keys(stagingItem.in_parameters).map((item) => (
-
-
-
- ))
- : ''}
-
-

- 输出参数
-
- {stagingItem.out_parameters &&
- Object.keys(stagingItem.out_parameters) &&
- Object.keys(stagingItem.out_parameters).length > 0
- ? Object.keys(stagingItem.out_parameters).map((item) => (
-
-
-
- ))
- : ''}
-
- ),
- },
- {
- key: '3',
- label: '输出结果',
- children: (
-
- {resultObj && resultObj.length > 0
- ? resultObj.map((item) => (
-
-
-
- 文件名称
- 文件大小
-
- {item.value && item.value.length > 0
- ? item.value.map((ele) => (
-
- {ele.name}
- {ele.size}
-
- ))
- : null}
-
- ))
- : null}
-
- ),
- icon: ,
- },
- ];
- const [open, setOpen] = useState(false);
- const afterOpenChange = () => {
- if (!open) {
- console.log(111, open);
-
- console.log(stagingItem, form.getFieldsValue());
- for (let i in form.getFieldsValue()) {
- for (let j in stagingItem.in_parameters) {
- if (i == j) {
- console.log(j, i);
- stagingItem.in_parameters[j].value = form.getFieldsValue()[i];
- }
- }
- for (let p in stagingItem.out_parameters) {
- if (i == p) {
- stagingItem.out_parameters[p].value = form.getFieldsValue()[i];
- }
- }
- for (let k in stagingItem.control_strategy) {
- if (i == k) {
- stagingItem.control_strategy[k].value = form.getFieldsValue()[i];
- }
- }
- }
- // setStagingItem({...stagingItem,})
- console.log(stagingItem.control_strategy);
- onParentChange({
- ...stagingItem,
- control_strategy: JSON.stringify(stagingItem.control_strategy),
- in_parameters: JSON.stringify(stagingItem.in_parameters),
- out_parameters: JSON.stringify(stagingItem.out_parameters),
- ...form.getFieldsValue(),
- });
- // onParentChange({...stagingItem,...form.getFieldsValue()})
- }
- };
- const onClose = () => {
- setOpen(false);
- };
- const onFinish = (values) => {
- console.log('Success:', values);
- };
- const onFinishFailed = (errorInfo) => {
- console.log('Failed:', errorInfo);
- };
- useImperativeHandle(ref, () => ({
- showDrawer(e, id, message) {
- setLogList([]);
- if (e.item && e.item.getModel().component_id) {
- const model = e.item.getModel() || {};
- const start_time = moment(model.experimentStartTime).valueOf() * 1.0e6;
- const params = {
- task_id: model.id,
- component_id: model.component_id,
- name: message.argo_ins_name,
- namespace: message.argo_ins_ns,
- start_time: start_time,
- };
- getQueryByExperimentLog(params).then((ret) => {
- const { log_type, pods, log_detail } = ret.data;
- if (log_type === 'normal') {
- const list = [
- {
- ...log_detail,
- log_type,
- },
- ];
- setLogList(list);
- } else if (log_type === 'resource') {
- const list = pods.map((v) => ({
- log_type,
- pod_name: v,
- log_content: '',
- start_time,
- }));
- setLogList(list);
- }
-
- getNodeResult({ id, node_id: e.item.getModel().id }).then((res) => {
- setResultObj(res.data);
- form.resetFields();
- form.setFieldsValue({
- ...e.item.getModel(),
- in_parameters: JSON.parse(e.item.getModel().in_parameters),
- out_parameters: JSON.parse(e.item.getModel().out_parameters),
- control_strategy: JSON.parse(e.item.getModel().control_strategy),
- });
- setStagingItem({
- ...e.item.getModel(),
- in_parameters: JSON.parse(e.item.getModel().in_parameters),
- out_parameters: JSON.parse(e.item.getModel().out_parameters),
- control_strategy: JSON.parse(e.item.getModel().control_strategy),
- });
- setOpen(true);
- });
- });
- } else {
- form.resetFields();
- form.setFieldsValue({
- ...e.item.getModel(),
- in_parameters: JSON.parse(e.item.getModel().in_parameters),
- out_parameters: JSON.parse(e.item.getModel().out_parameters),
- control_strategy: JSON.parse(e.item.getModel().control_strategy),
- });
- setStagingItem({
- ...e.item.getModel(),
- in_parameters: JSON.parse(e.item.getModel().in_parameters),
- out_parameters: JSON.parse(e.item.getModel().out_parameters),
- control_strategy: JSON.parse(e.item.getModel().control_strategy),
- });
- setOpen(true);
- }
- // console.log(e.item.getModel().in_parameters);
- },
- }));
- return (
-
-
-
- 任务名称:{stagingItem.label}
-
-
- 执行状态:
-
-
- {statusObj[stagingItem.experimentStatus]}
-
-
-
- 启动时间:{moment(stagingItem.experimentStartTime).format('YYYY-MM-DD HH:mm:ss')}
-
-
- 耗时:
- {stagingItem.experimentEndTime
- ? elapsedTime(
- new Date(stagingItem.experimentStartTime),
- new Date(stagingItem.experimentEndTime),
- )
- : elapsedTime(new Date(stagingItem.experimentStartTime), new Date())}
-
-
-
-
- );
-});
-
-export default Props;
diff --git a/react-ui/src/pages/Experiment/experimentText/props.less b/react-ui/src/pages/Experiment/experimentText/props.less
new file mode 100644
index 00000000..b3294d55
--- /dev/null
+++ b/react-ui/src/pages/Experiment/experimentText/props.less
@@ -0,0 +1,37 @@
+.experiment-drawer {
+ :global {
+ .ant-drawer-body {
+ overflow-y: hidden;
+ }
+ }
+
+ &__tabs {
+ height: calc(100% - 170px);
+ :global {
+ .ant-tabs-nav {
+ padding-left: 24px;
+ background-color: #f8fbff;
+ border: 1px solid #e0eaff;
+ }
+ .ant-tabs-content-holder {
+ overflow-y: auto;
+ }
+ }
+ }
+
+ &__info {
+ display: flex;
+ align-items: center;
+ margin-bottom: 15px;
+ padding-left: 24px;
+ color: @text-color;
+ font-size: 15px;
+ }
+
+ &__status-dot {
+ width: 8px;
+ height: 8px;
+ margin-right: 6px;
+ border-radius: 50%;
+ }
+}
diff --git a/react-ui/src/pages/Experiment/experimentText/props.tsx b/react-ui/src/pages/Experiment/experimentText/props.tsx
new file mode 100644
index 00000000..ccd51997
--- /dev/null
+++ b/react-ui/src/pages/Experiment/experimentText/props.tsx
@@ -0,0 +1,171 @@
+import { getNodeResult, getQueryByExperimentLog } from '@/services/experiment/index.js';
+import { PipelineNodeModelSerialize } from '@/types';
+import { elapsedTime, formatDate } from '@/utils/date';
+import { to } from '@/utils/promise';
+import { DatabaseOutlined, ProfileOutlined } from '@ant-design/icons';
+import { Drawer, Form, Tabs } from 'antd';
+import dayjs from 'dayjs';
+import { forwardRef, useImperativeHandle, useState } from 'react';
+import ExperimentParameter from '../components/ExperimentParameter';
+import ExperimentResult from '../components/ExperimentResult';
+import LogList from '../components/LogList';
+import { experimentStatusInfo } from '../status';
+import styles from './props.less';
+
+export type ExperimentLog = {
+ log_type: 'normal' | 'resource'; // 日志类型
+ pod_name?: string; // 分布式名称
+ log_content?: string; // 日志内容
+ start_time?: string; // 日志开始时间
+};
+
+const Props = forwardRef((_, ref) => {
+ const [form] = Form.useForm();
+ const [experimentNodeData, setExperimentNodeData] = useState(
+ {} as PipelineNodeModelSerialize,
+ );
+ const [experimentResults, setExperimentResults] = useState([]);
+ const [experimentLogList, setExperimentLogList] = useState([]);
+
+ const items = [
+ {
+ key: '1',
+ label: '日志详情',
+ children: (
+
+ ),
+ icon: ,
+ },
+ {
+ key: '2',
+ label: '配置参数',
+ icon: ,
+ children: ,
+ },
+ {
+ key: '3',
+ label: '输出结果',
+ children: ,
+ icon: ,
+ },
+ ];
+ const [open, setOpen] = useState(false);
+ const onClose = () => {
+ setOpen(false);
+ };
+
+ // 获取实验日志
+ const getExperimentLog = async (params: any, start_time: number) => {
+ const [res] = await to(getQueryByExperimentLog(params));
+ if (res && res.data) {
+ const { log_type, pods, log_detail } = res.data;
+ if (log_type === 'normal') {
+ const list = [
+ {
+ ...log_detail,
+ log_type,
+ },
+ ];
+ setExperimentLogList(list);
+ } else if (log_type === 'resource') {
+ const list = pods.map((v: string) => ({
+ log_type,
+ pod_name: v,
+ log_content: '',
+ start_time,
+ }));
+ setExperimentLogList(list);
+ }
+ }
+ };
+
+ // 获取实验结果
+ const getExperimentResult = async (params: any) => {
+ const [res] = await to(getNodeResult(params));
+ if (res && res.data) {
+ setExperimentResults(res.data);
+ }
+ };
+
+ useImperativeHandle(ref, () => ({
+ showDrawer(e: any, id: string, message: any) {
+ setOpen(true);
+
+ // 获取实验参数
+ const model = e.item.getModel();
+ try {
+ const nodeData = {
+ ...model,
+ in_parameters: JSON.parse(model.in_parameters),
+ out_parameters: JSON.parse(model.out_parameters),
+ control_strategy: JSON.parse(model.control_strategy),
+ };
+ setExperimentNodeData(nodeData);
+ form.setFieldsValue(nodeData);
+ } catch (error) {
+ console.log(error);
+ }
+
+ // 获取实验日志和实验结果
+ setExperimentLogList([]);
+ setExperimentResults([]);
+ if (e.item && e.item.getModel()) {
+ const model = e.item.getModel();
+ const start_time = dayjs(model.experimentStartTime).valueOf() * 1.0e6;
+ const params = {
+ task_id: model.id,
+ component_id: model.component_id,
+ name: message.argo_ins_name,
+ namespace: message.argo_ins_ns,
+ start_time: start_time,
+ };
+ getExperimentLog(params, start_time);
+ getExperimentResult({ id, node_id: model.id });
+ }
+ },
+ }));
+ return (
+
+
+
+ 任务名称:{experimentNodeData.label}
+
+
+ 执行状态:
+
+
+ {experimentStatusInfo[experimentNodeData.experimentStatus]?.label}
+
+
+
+ 启动时间:{formatDate(experimentNodeData.experimentStartTime)}
+
+
+ 耗时:
+ {elapsedTime(
+ experimentNodeData.experimentStartTime,
+ experimentNodeData.experimentEndTime,
+ )}
+
+
+
+
+ );
+});
+
+export default Props;
diff --git a/react-ui/src/pages/Experiment/index.jsx b/react-ui/src/pages/Experiment/index.jsx
index 611c18c6..56548d6e 100644
--- a/react-ui/src/pages/Experiment/index.jsx
+++ b/react-ui/src/pages/Experiment/index.jsx
@@ -14,16 +14,15 @@ import {
} from '@/services/experiment/index.js';
import { getWorkflow } from '@/services/pipeline/index.js';
import themes from '@/styles/theme.less';
-import { elapsedTime } from '@/utils/date';
+import { elapsedTime, formatDate } from '@/utils/date';
import { to } from '@/utils/promise';
import { modalConfirm } from '@/utils/ui';
import { Button, ConfigProvider, Space, Table, message } from 'antd';
import classNames from 'classnames';
-import momnet from 'moment';
import { useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
+import AddExperimentModal from './components/AddExperimentModal';
import TensorBoardStatus, { TensorBoardStatusEnum } from './components/TensorBoardStatus';
-import AddExperimentModal from './experimentText/addExperimentModal';
import Styles from './index.less';
import { experimentStatusInfo } from './status';
@@ -438,13 +437,9 @@ function Experiment() {
- {item.finish_time
- ? elapsedTime(new Date(item.create_time), new Date(item.finish_time))
- : elapsedTime(new Date(item.create_time), new Date())}
-
-
- {momnet(item.create_time).format('YYYY-MM-DD HH:mm:ss')}
+ {elapsedTime(item.create_time, item.finish_time)}
+
{formatDate(item.create_time)}
![]()
{
const fileList = form.getFieldValue('fileList');
if (Array.isArray(fileList) && fileList.length >= 1) {
@@ -134,12 +121,13 @@ function MirrorCreate() {
-
- >
- );
-};
-export default PublicData;
diff --git a/react-ui/src/pages/Model/publicData.jsx b/react-ui/src/pages/Model/publicData.jsx
deleted file mode 100644
index 5cb7082e..00000000
--- a/react-ui/src/pages/Model/publicData.jsx
+++ /dev/null
@@ -1,384 +0,0 @@
-import clock from '@/assets/img/clock.png';
-import creatByImg from '@/assets/img/creatBy.png';
-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 { useEffect, useState } from 'react';
-import { useNavigate } from 'react-router-dom';
-import Styles from './index.less';
-const { Search } = Input;
-const leftdataList = [1, 2, 3];
-
-const PublicData = () => {
- const [queryFlow, setQueryFlow] = useState({
- page: 0,
- size: 20,
- name: null,
- available_range: 1,
- });
- const [iconParams, setIconParams] = useState({
- name: null,
- page: 0,
- size: 10000,
- });
- const [activeType, setActiveType] = useState(null);
- const [activeTag, setActiveTag] = useState(null);
- const [datasetTypeList, setDatasetTypeList] = useState([]);
- const [datasetDirectionList, setDatasetDirectionList] = useState([]);
- const navgite = useNavigate();
- const [isModalOpen, setIsModalOpen] = useState(false);
- const [datasetList, setDatasetList] = useState([]);
- const [total, setTotal] = useState(0);
- const [form] = Form.useForm();
- const [dialogTitle, setDialogTitle] = useState('新建数据');
- const getModelLists = (queryFlow) => {
- getModelList(queryFlow).then((ret) => {
- console.log(ret);
- if (ret.code == 200) {
- setDatasetList(ret.data.content);
- setTotal(ret.data.totalElements);
- }
- });
- };
-
- const showModal = () => {
- form.resetFields();
- setDialogTitle('新建数据集');
- setIsModalOpen(true);
- };
- const nameSearch = (values) => {
- console.log(values);
- getModelLists({ ...queryFlow, name: values });
- };
- const getAssetIconList = (params) => {
- getAssetIcon(params).then((ret) => {
- console.log(ret);
- 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));
- } else {
- setDatasetTypeList([]);
- setDatasetDirectionList([]);
- }
- });
- };
- const onSearch = (values) => {
- console.log(values);
- getAssetIconList({ ...iconParams, name: values });
- };
- const handleOk = () => {
- console.log(1111);
- setIsModalOpen(false);
- };
- const handleCancel = () => {
- setIsModalOpen(false);
- };
- const chooseModelType = (val, item) => {
- console.log(val, item);
- if (item.id == queryFlow.model_type) {
- setActiveType('');
- setQueryFlow({ ...queryFlow, model_type: null });
- getModelLists({ ...queryFlow, model_type: null });
- } else {
- setActiveType(item.id);
- setQueryFlow({ ...queryFlow, model_type: item.id });
- getModelLists({ ...queryFlow, model_type: item.id });
- }
-
- // setQueryFlow({...queryFlow,data_type:item.path},()=>{
- // getDatasetlist()
- // })
- };
- const chooseModelTag = (val, item) => {
- if (item.id == queryFlow.model_tag) {
- setActiveTag('');
- setQueryFlow({ ...queryFlow, model_tag: null });
- getModelLists({ ...queryFlow, model_tag: null });
- } else {
- setActiveTag(item.id);
- setQueryFlow({ ...queryFlow, model_tag: item.id });
- getModelLists({ ...queryFlow, model_tag: item.id });
- }
- // setQueryFlow({...queryFlow,data_type:item.path},()=>{
- // getDatasetlist()
- // })
- };
- const onFinish = (values) => {};
- const routeToIntro = (e, record) => {
- e.stopPropagation();
- console.log(record);
- navgite({ pathname: `/dataset/model/${record.id}` });
- };
- const onFinishFailed = (errorInfo) => {
- console.log('Failed:', errorInfo);
- };
- const onPageChange = (pageNum, pageSize) => {
- console.log(pageNum, pageSize);
- setQueryFlow({ ...queryFlow, page: pageNum - 1, size: pageSize });
- getModelLists({ ...queryFlow, page: pageNum - 1, size: pageSize });
- };
- useEffect(() => {
- getAssetIconList(iconParams);
- getModelLists(queryFlow);
- return () => {};
- }, []);
- return (
- <>
-
-
-
-
-
模型框架
-
- {datasetTypeList && datasetTypeList.length > 0
- ? datasetTypeList.map((item) => {
- return (
-
-
{
- chooseModelType(e, item);
- }}
- >
-

-

-
{item.name}
-
-
- );
- })
- : ''}
-
-
模型能力
-
- {datasetDirectionList && datasetDirectionList.length > 0
- ? datasetDirectionList.map((item) => {
- return (
-
-
{
- chooseModelTag(e, item);
- }}
- >
-

-

-
{item.name}
-
-
- );
- })
- : ''}
-
-
-
-
-
-
- {datasetList && datasetList.length > 0
- ? datasetList.map((item) => {
- return (
-
{
- routeToIntro(e, item);
- }}
- >
-
{item.name}
-
![]()
{
- 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=""
- />
-
{item.description}
-
-

-
{item.create_by}
-
-
-

-
最近更新: {moment(item.update_time).format('YYYY-MM-DD')}
-
-
- );
- })
- : ''}
- {/*
Demo */}
-
-
-
-
-
-
- {dialogTitle}
-
- }
- open={isModalOpen}
- className={Styles.modal}
- okButtonProps={{
- htmlType: 'submit',
- form: 'form',
- }}
- onCancel={handleCancel}
- >
-
-
-
-
-
-
-
-
-
-
-
- 仅自己可见
- 工作空间可见
-
-
-
-
-
-
-
- >
- );
-};
-export default PublicData;
diff --git a/react-ui/src/pages/ModelDeployment/components/MirrorStatusCell/index.less b/react-ui/src/pages/ModelDeployment/components/MirrorStatusCell/index.less
new file mode 100644
index 00000000..043bf411
--- /dev/null
+++ b/react-ui/src/pages/ModelDeployment/components/MirrorStatusCell/index.less
@@ -0,0 +1,11 @@
+.mirror-status-cell {
+ color: @text-color;
+
+ &--success {
+ color: @success-color;
+ }
+
+ &--error {
+ color: @error-color;
+ }
+}
diff --git a/react-ui/src/pages/ModelDeployment/components/MirrorStatusCell/index.tsx b/react-ui/src/pages/ModelDeployment/components/MirrorStatusCell/index.tsx
new file mode 100644
index 00000000..3702825f
--- /dev/null
+++ b/react-ui/src/pages/ModelDeployment/components/MirrorStatusCell/index.tsx
@@ -0,0 +1,39 @@
+/*
+ * @Author: 赵伟
+ * @Date: 2024-04-18 18:35:41
+ * @Description:
+ */
+import { MirrorVersionStatus } from '@/enums';
+import styles from './index.less';
+
+type MirrorVersionStatusKeys = keyof typeof MirrorVersionStatus;
+type MirrorVersionStatusValues = (typeof MirrorVersionStatus)[MirrorVersionStatusKeys];
+
+export type MirrorVersionStatusInfo = {
+ text: string;
+ classname: string;
+};
+
+const statusInfo: Record
= {
+ [MirrorVersionStatus.Building]: {
+ text: '构建中',
+ classname: styles['mirror-status-cell'],
+ },
+ [MirrorVersionStatus.Available]: {
+ classname: styles['mirror-status-cell--success'],
+ text: '可用',
+ },
+ [MirrorVersionStatus.Failed]: {
+ classname: styles['mirror-status-cell--error'],
+ text: '构建失败',
+ },
+};
+
+function MirrorStatusCell(status: MirrorVersionStatus) {
+ if (status === null || status === undefined || !statusInfo[status]) {
+ return --;
+ }
+ return {statusInfo[status].text};
+}
+
+export default MirrorStatusCell;
diff --git a/react-ui/src/pages/ModelDeployment/create.less b/react-ui/src/pages/ModelDeployment/create.less
new file mode 100644
index 00000000..63c00764
--- /dev/null
+++ b/react-ui/src/pages/ModelDeployment/create.less
@@ -0,0 +1,17 @@
+.model-deployment-create {
+ height: 100%;
+
+ &__content {
+ height: calc(100% - 60px);
+ margin-top: 10px;
+ padding: 30px 30px 10px;
+ overflow: auto;
+ background-color: white;
+ border-radius: 10px;
+
+ &__type {
+ color: @text-color;
+ font-size: @font-size-input-lg;
+ }
+ }
+}
diff --git a/react-ui/src/pages/ModelDeployment/create.tsx b/react-ui/src/pages/ModelDeployment/create.tsx
new file mode 100644
index 00000000..cc2c43ff
--- /dev/null
+++ b/react-ui/src/pages/ModelDeployment/create.tsx
@@ -0,0 +1,297 @@
+/*
+ * @Author: 赵伟
+ * @Date: 2024-04-16 13:58:08
+ * @Description: 创建模型部署
+ */
+import PageTitle from '@/components/PageTitle';
+import SubAreaTitle from '@/components/SubAreaTitle';
+import { CommonTabKeys } from '@/enums';
+import { createMirrorReq } from '@/services/mirror';
+import { getComputingResourceReq } from '@/services/pipeline';
+import { to } from '@/utils/promise';
+import { getSessionItemThenRemove, mirrorNameKey } from '@/utils/sessionStorage';
+import { validateUploadFiles } from '@/utils/ui';
+import { useNavigate } from '@umijs/max';
+import { Button, Col, Form, Input, Row, Select, UploadFile, message, type SelectProps } from 'antd';
+import { omit } from 'lodash';
+import { useEffect, useState } from 'react';
+import styles from './create.less';
+
+type FormData = {
+ name: string;
+ tag: string;
+ description: string;
+ path?: string;
+ upload_type: string;
+ fileList?: UploadFile[];
+};
+
+function ModelDeploymentCreate() {
+ const navgite = useNavigate();
+ const [form] = Form.useForm();
+ const [nameDisabled, setNameDisabled] = useState(false);
+ const [resourceStandardList, setResourceStandardList] = useState([]);
+
+ useEffect(() => {
+ const name = getSessionItemThenRemove(mirrorNameKey);
+ if (name) {
+ form.setFieldValue('name', name);
+ setNameDisabled(true);
+ }
+ getComputingResource();
+ }, []);
+
+ const getComputingResource = async () => {
+ const params = {
+ page: 0,
+ size: 1000,
+ resource_type: '',
+ };
+ const [res] = await to(getComputingResourceReq(params));
+ if (res && res.data && res.data.content) {
+ setResourceStandardList(res.data.content);
+ }
+ };
+
+ const filterResourceStandard: SelectProps['filterOption'] = (
+ input: string,
+ { computing_resource = '' },
+ ) => {
+ return computing_resource.toLocaleLowerCase().includes(input.toLocaleLowerCase());
+ };
+
+ // 创建公网、本地镜像
+ const createPublicMirror = async (formData: FormData) => {
+ const upload_type = formData['upload_type'];
+ let params;
+ if (upload_type === CommonTabKeys.Public) {
+ params = {
+ ...omit(formData, ['upload_type']),
+ upload_type: 0,
+ image_type: 0,
+ };
+ } else {
+ const fileList = formData['fileList'] ?? [];
+ if (validateUploadFiles(fileList)) {
+ const file = fileList[0];
+ params = {
+ ...omit(formData, ['fileList', 'upload_type']),
+ path: file.response.data.url,
+ file_size: file.response.data.fileSize,
+ upload_type: 1,
+ image_type: 0,
+ };
+ }
+ }
+
+ const [res] = await to(createMirrorReq(params));
+ if (res) {
+ message.success('创建成功');
+ navgite(-1);
+ }
+ };
+
+ // 提交
+ const handleSubmit = (values: FormData) => {
+ createPublicMirror(values);
+ };
+
+ // 取消
+ const cancel = () => {
+ navgite(-1);
+ };
+
+ return (
+
+ );
+}
+
+export default ModelDeploymentCreate;
diff --git a/react-ui/src/pages/ModelDeployment/info.less b/react-ui/src/pages/ModelDeployment/info.less
new file mode 100644
index 00000000..c77a7070
--- /dev/null
+++ b/react-ui/src/pages/ModelDeployment/info.less
@@ -0,0 +1,53 @@
+.model-deployment-info {
+ height: 100%;
+
+ &__basic {
+ &__item {
+ display: flex;
+ align-items: flex-start;
+ font-size: 16px;
+ line-height: 1.6;
+
+ .label {
+ width: 80px;
+ color: @text-color-secondary;
+ }
+
+ .value {
+ flex: 1;
+ color: @text-color;
+ }
+ }
+ }
+
+ &__content {
+ height: calc(100% - 60px);
+ margin-top: 10px;
+ padding: 30px 30px 0;
+ background-color: white;
+ border-radius: 10px;
+
+ &__title {
+ display: flex;
+ align-items: center;
+ }
+
+ &__table {
+ :global {
+ .ant-table-wrapper {
+ height: 100%;
+ .ant-spin-nested-loading {
+ height: 100%;
+ }
+ .ant-spin-container {
+ height: 100%;
+ }
+ .ant-table {
+ height: calc(100% - 74px);
+ overflow: auto;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/react-ui/src/pages/ModelDeployment/info.tsx b/react-ui/src/pages/ModelDeployment/info.tsx
new file mode 100644
index 00000000..3e4e8a81
--- /dev/null
+++ b/react-ui/src/pages/ModelDeployment/info.tsx
@@ -0,0 +1,148 @@
+/*
+ * @Author: 赵伟
+ * @Date: 2024-04-16 13:58:08
+ * @Description: 镜像详情
+ */
+import KFIcon from '@/components/KFIcon';
+import PageTitle from '@/components/PageTitle';
+import SubAreaTitle from '@/components/SubAreaTitle';
+import { getMirrorInfoReq } from '@/services/mirror';
+import { formatDate } from '@/utils/date';
+import { to } from '@/utils/promise';
+import { useNavigate, useParams } from '@umijs/max';
+import { Col, Row, Tabs, type TabsProps } from 'antd';
+import { useEffect, useState } from 'react';
+import styles from './info.less';
+
+type MirrorInfoData = {
+ name?: string;
+ description?: string;
+ version_count?: string;
+ create_time?: string;
+};
+
+type MirrorVersionData = {
+ id: number;
+ version: string;
+ url: string;
+ status: string;
+ file_size: string;
+ create_time: string;
+};
+
+const tabItems = [
+ {
+ key: '1',
+ label: '预测',
+ icon: ,
+ },
+ {
+ key: '2',
+ label: '调用指南',
+ icon: ,
+ },
+ {
+ key: '3',
+ label: '服务日志',
+ icon: ,
+ },
+];
+
+function ModelDeploymentInfo() {
+ const navigate = useNavigate();
+ const urlParams = useParams();
+
+ const [mirrorInfo, setMirrorInfo] = useState({});
+
+ const [activeTab, setActiveTab] = useState('1');
+ useEffect(() => {
+ getMirrorInfo();
+ }, []);
+
+ // 获取镜像详情
+ const getMirrorInfo = async () => {
+ const id = Number(urlParams.id);
+ const [res] = await to(getMirrorInfoReq(id));
+ if (res && res.data) {
+ const { name = '', description = '', version_count = '', create_time: time } = res.data;
+ const create_time = formatDate(time);
+ setMirrorInfo({
+ name,
+ description,
+ version_count,
+ create_time,
+ });
+ }
+ };
+
+ // 切换 Tab,重置数据
+ const hanleTabChange: TabsProps['onChange'] = (value) => {
+ setActiveTab(value);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
服务名称:
+
{mirrorInfo.name}
+
+
+
+
+
镜像:
+
{mirrorInfo.version_count ?? '--'}
+
+
+
+
+
+
+
状态:
+
{mirrorInfo.name}
+
+
+
+
+
模型:
+
{mirrorInfo.version_count ?? '--'}
+
+
+
+
+
+
+
环境变量:
+
{mirrorInfo.name}
+
+
+
+
+
+
+
描述:
+
{mirrorInfo.description}
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default ModelDeploymentInfo;
diff --git a/react-ui/src/pages/ModelDeployment/list.less b/react-ui/src/pages/ModelDeployment/list.less
new file mode 100644
index 00000000..9e521f70
--- /dev/null
+++ b/react-ui/src/pages/ModelDeployment/list.less
@@ -0,0 +1,21 @@
+.model-deployment {
+ height: 100%;
+ &__content {
+ height: calc(100% - 60px);
+ 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;
+ }
+ }
+}
diff --git a/react-ui/src/pages/ModelDeployment/list.tsx b/react-ui/src/pages/ModelDeployment/list.tsx
new file mode 100644
index 00000000..bfad5a22
--- /dev/null
+++ b/react-ui/src/pages/ModelDeployment/list.tsx
@@ -0,0 +1,283 @@
+/*
+ * @Author: 赵伟
+ * @Date: 2024-04-16 13:58:08
+ * @Description: 模型部署列表
+ */
+import CommonTableCell from '@/components/CommonTableCell';
+import DateTableCell from '@/components/DateTableCell';
+import KFIcon from '@/components/KFIcon';
+import PageTitle from '@/components/PageTitle';
+import { useCacheState } from '@/hooks/pageCacheState';
+import { deleteMirrorReq, getMirrorListReq } from '@/services/mirror';
+import themes from '@/styles/theme.less';
+import { to } from '@/utils/promise';
+import { modalConfirm } from '@/utils/ui';
+import { useNavigate } from '@umijs/max';
+import {
+ App,
+ Button,
+ ConfigProvider,
+ Input,
+ Table,
+ type TablePaginationConfig,
+ type TableProps,
+} from 'antd';
+import { type SearchProps } from 'antd/es/input';
+import classNames from 'classnames';
+import { useEffect, useState } from 'react';
+import styles from './list.less';
+
+export type MirrorData = {
+ id: number;
+ name: string;
+ description: string;
+ create_time: string;
+};
+
+function ModelDeployment() {
+ const navigate = useNavigate();
+ const { message } = App.useApp();
+ const [cacheState, setCacheState] = useCacheState();
+ const [searchText, setSearchText] = useState(cacheState?.searchText);
+ const [inputText, setInputText] = useState(cacheState?.searchText);
+ const [tableData, setTableData] = useState([]);
+ const [total, setTotal] = useState(0);
+ const [pagination, setPagination] = useState>(
+ cacheState?.pagination ?? {
+ current: 1,
+ pageSize: 10,
+ },
+ );
+
+ useEffect(() => {
+ getMirrorList();
+ }, [pagination, searchText]);
+
+ // 获取镜像列表
+ const getMirrorList = async () => {
+ const params: Record = {
+ page: pagination.current - 1,
+ size: pagination.pageSize,
+ name: searchText,
+ image_type: 1,
+ };
+ const [res] = await to(getMirrorListReq(params));
+ if (res && res.data) {
+ const { content = [], totalElements = 0 } = res.data;
+ setTableData(content);
+ setTotal(totalElements);
+ }
+ };
+
+ // 删除镜像
+ const deleteMirror = async (id: number) => {
+ const [res] = await to(deleteMirrorReq(id));
+ if (res) {
+ message.success('删除成功');
+ // 如果是一页的唯一数据,删除时,请求第一页的数据
+ // 否则直接刷新这一页的数据
+ // 避免回到第一页
+ if (tableData.length > 1) {
+ setPagination((prev) => ({
+ ...prev,
+ current: 1,
+ }));
+ } else {
+ getMirrorList();
+ }
+ }
+ };
+
+ // 搜索
+ const onSearch: SearchProps['onSearch'] = (value) => {
+ setSearchText(value);
+ };
+
+ // 查看详情
+ const toDetail = (record: MirrorData) => {
+ navigate(`/modelDeployment/${record.id}`);
+ setCacheState({
+ pagination,
+ searchText,
+ });
+ };
+
+ // 处理删除
+ const handleMirrorDelete = (record: MirrorData) => {
+ modalConfirm({
+ title: '删除后,该镜像将不可恢复',
+ content: '是否确认删除?',
+ onOk: () => {
+ deleteMirror(record.id);
+ },
+ });
+ };
+
+ // 创建镜像
+ const createMirror = () => {
+ navigate(`/modelDeployment/create`);
+ setCacheState({
+ pagination,
+ searchText,
+ });
+ };
+
+ // 分页切换
+ const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter, { action }) => {
+ if (action === 'paginate') {
+ setPagination(pagination);
+ }
+ // console.log(pagination, filters, sorter, action);
+ };
+
+ const columns: TableProps['columns'] = [
+ {
+ title: '序号',
+ dataIndex: 'index',
+ key: 'index',
+ width: 100,
+ align: 'center',
+ render(text, record, index) {
+ return {(pagination.current - 1) * pagination.pageSize + index + 1};
+ },
+ },
+ {
+ title: '服务名称',
+ dataIndex: 'name',
+ key: 'name',
+ width: '30%',
+ render: CommonTableCell(),
+ },
+ {
+ title: '模型',
+ dataIndex: 'version_count',
+ key: 'version_count',
+ width: '20%',
+ render: CommonTableCell(),
+ },
+ {
+ title: '状态',
+ dataIndex: 'version_count',
+ key: 'version_count',
+ width: '10%',
+ render: CommonTableCell(),
+ },
+ {
+ title: '创建人',
+ dataIndex: 'description',
+ key: 'description',
+ render: CommonTableCell(true),
+ width: '20%',
+ ellipsis: { showTitle: false },
+ },
+ {
+ title: '更新时间',
+ dataIndex: 'create_time',
+ key: 'create_time',
+ width: '20%',
+ render: DateTableCell,
+ },
+ {
+ title: '操作',
+ dataIndex: 'operation',
+ width: 350,
+ key: 'operation',
+ render: (_: any, record: MirrorData) => (
+
+ }
+ onClick={() => toDetail(record)}
+ >
+ 编辑
+
+ }
+ onClick={() => toDetail(record)}
+ >
+ 启动
+
+ }
+ onClick={() => toDetail(record)}
+ >
+ 停止
+
+
+ }
+ onClick={() => handleMirrorDelete(record)}
+ >
+ 删除
+
+
+
+ ),
+ },
+ ];
+
+ return (
+
+
+
+
+ setInputText(e.target.value)}
+ style={{ width: 300 }}
+ value={inputText}
+ />
+ }
+ >
+ 创建推理服务
+
+
+
+
+
+ );
+}
+
+export default ModelDeployment;
diff --git a/react-ui/src/pages/Pipeline/editPipeline/globalParamsDrawer.less b/react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.less
similarity index 100%
rename from react-ui/src/pages/Pipeline/editPipeline/globalParamsDrawer.less
rename to react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.less
diff --git a/react-ui/src/pages/Pipeline/editPipeline/globalParamsDrawer.tsx b/react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.tsx
similarity index 95%
rename from react-ui/src/pages/Pipeline/editPipeline/globalParamsDrawer.tsx
rename to react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.tsx
index 7ab95f5d..e61423fd 100644
--- a/react-ui/src/pages/Pipeline/editPipeline/globalParamsDrawer.tsx
+++ b/react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.tsx
@@ -1,7 +1,4 @@
-import {
- getParamComponent,
- getParamRules,
-} from '@/pages/Experiment/experimentText/addExperimentModal';
+import { getParamComponent, getParamRules } from '@/pages/Experiment/components/AddExperimentModal';
import { type PipelineGlobalParam } from '@/types';
import { to } from '@/utils/promise';
import { modalConfirm } from '@/utils/ui';
@@ -9,7 +6,7 @@ import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Drawer, Form, Input, Radio, Tooltip } from 'antd';
import { NamePath } from 'antd/es/form/interface';
import { forwardRef, useImperativeHandle } from 'react';
-import styles from './globalParamsDrawer.less';
+import styles from './index.less';
type GlobalParamsDrawerProps = {
open: boolean;
@@ -22,7 +19,7 @@ const GlobalParamsDrawer = forwardRef(
const [form] = Form.useForm();
useImperativeHandle(ref, () => ({
- getFieldsValue: async () => {
+ validateFields: async () => {
const [values, error] = await to(form.validateFields());
if (!error && values) {
return values;
@@ -30,6 +27,9 @@ const GlobalParamsDrawer = forwardRef(
return Promise.reject(error);
}
},
+ getFieldsValue: () => {
+ return form.getFieldsValue();
+ },
}));
// 处理参数类型变化
@@ -162,7 +162,6 @@ const GlobalParamsDrawer = forwardRef(
)}
- {/* //{contextHolder} */}
);
},
diff --git a/react-ui/src/pages/Pipeline/components/PropsLabel/index.less b/react-ui/src/pages/Pipeline/components/PropsLabel/index.less
new file mode 100644
index 00000000..2df39c02
--- /dev/null
+++ b/react-ui/src/pages/Pipeline/components/PropsLabel/index.less
@@ -0,0 +1,6 @@
+.props-label {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
diff --git a/react-ui/src/pages/Pipeline/components/PropsLabel/index.tsx b/react-ui/src/pages/Pipeline/components/PropsLabel/index.tsx
new file mode 100644
index 00000000..04197b75
--- /dev/null
+++ b/react-ui/src/pages/Pipeline/components/PropsLabel/index.tsx
@@ -0,0 +1,44 @@
+import { Button, Dropdown, type MenuProps } from 'antd';
+import { useEffect } from 'react';
+import styles from './index.less';
+
+type PropsLabelProps = {
+ title: string;
+ menuItems: MenuProps['items'];
+ onClick?: (key: string) => void;
+};
+
+function PropsLabel({ title, menuItems, onClick }: PropsLabelProps) {
+ useEffect(() => {}, []);
+
+ const handleItemClick: MenuProps['onClick'] = (e) => {
+ const keyPath = e.keyPath.reverse();
+ if (keyPath[0] === 'global') {
+ onClick?.(`\${${e.key}}`);
+ } else {
+ onClick?.(`{{${keyPath.join('.')}}}`);
+ }
+ };
+
+ return (
+
+ {title}
+
+
+
+
+ );
+}
+
+export default PropsLabel;
diff --git a/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx b/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx
index e073a449..20c265f2 100644
--- a/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx
+++ b/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx
@@ -40,7 +40,6 @@ export type SelectorTypeInfo = {
getFiles: (params: any) => Promise;
handleVersionResponse: (res: any) => any[];
modalIcon: string;
- buttonIcon: string;
name: string;
litReqParamKey: 'available_range' | 'image_type';
fileReqParamKey: 'models_id' | 'dataset_id';
@@ -71,7 +70,6 @@ export const selectorTypeData: Record res.data || [],
name: '模型',
modalIcon: modelImg,
- buttonIcon: 'local:model-select-button',
litReqParamKey: 'available_range',
fileReqParamKey: 'models_id',
tabItems: [
@@ -92,7 +90,6 @@ export const selectorTypeData: Record res.data || [],
name: '数据集',
modalIcon: datasetImg,
- buttonIcon: 'local:dataset-select-button',
litReqParamKey: 'available_range',
fileReqParamKey: 'dataset_id',
tabItems: [
@@ -115,7 +112,6 @@ export const selectorTypeData: Record {
- const propsRef = useRef();
const navgite = useNavigate();
- // const [contextMenu,setContextMenu]=useState({})
let contextMenu = {};
const locationParams = useParams(); //新版本获取路由参数接口
- let sourceAnchorIdx, targetAnchorIdx;
+
const pipelineContainer = useEmotionCss(() => {
return {
display: 'flex',
@@ -59,8 +58,11 @@ const EditPipeline = () => {
});
const graphRef = useRef();
const paramsDrawerRef = useRef();
+ const propsRef = useRef();
const [paramsDrawerOpen, openParamsDrawer, closeParamsDrawer] = useVisible(false);
- const [globalParam, setGlobalParam] = useState([]);
+ const [globalParam, setGlobalParam, globalParamRef] = useStateRef([]);
+
+ let sourceAnchorIdx, targetAnchorIdx;
const onDragEnd = (val) => {
console.log(val, 'eee');
@@ -93,17 +95,24 @@ const EditPipeline = () => {
}
};
const savePipeline = async (val) => {
- const [res, error] = await to(paramsDrawerRef.current.getFieldsValue());
+ const [res, error] = await to(paramsDrawerRef.current.validateFields());
if (error) {
message.error('全局参数配置有误');
openParamsDrawer();
return;
}
+
+ const duplicateName = findFirstDuplicate(res.global_param || []);
+ if (duplicateName) {
+ message.error('全局参数配置有重复的参数名称:' + duplicateName);
+ openParamsDrawer();
+ return;
+ }
+
const [propsRes, propsError] = await to(propsRef.current.getFieldsValue());
console.log(await to(propsRef.current.getFieldsValue()));
if (propsError) {
message.error('基本信息必填项需配置');
- // handlerClick();
return;
}
propsRef.current.propClose();
@@ -128,12 +137,19 @@ const EditPipeline = () => {
};
const handlerClick = (e) => {
e.stopPropagation();
- // console.log(propsRef, graph);
- propsRef.current.showDrawer(e);
+ if (e.target.get('name') !== 'anchor-point' && e.item) {
+ graph.setItemState(e.item, 'nodeClicked', true);
+ const parentNodes = findAllParentNodes(graph, e.item);
+ // 如果没有打开过全局参数抽屉,获取不到全局参数
+ const globalParams =
+ paramsDrawerRef.current.getFieldsValue().global_param || globalParamRef.current;
+ propsRef.current.showDrawer(e, globalParams, parentNodes);
+ }
};
const getGraphData = (data) => {
+ console.log('graph', graph);
if (graph) {
- console.log(graph);
+ console.log(data);
graph.data(data);
graph.render();
} else {
@@ -275,14 +291,13 @@ const EditPipeline = () => {
if (graph && ret.data && ret.data.dag) {
getGraphData(JSON.parse(ret.data.dag));
}
- // graph&&graph.data(JSON.parse(ret.dag))
- // graph.render()
});
};
const handlerContextMenu = (e) => {
e.stopPropagation();
// this.menuType = e.item._cfg.type;
};
+ // 上下文菜单
const initMenu = () => {
// const selectedNodes = this.selectedNodes;
contextMenu = new G6.Menu({
@@ -330,8 +345,8 @@ const EditPipeline = () => {
initGraph();
};
useEffect(() => {
- getFirstWorkflow(locationParams.id);
initMenu();
+ getFirstWorkflow(locationParams.id);
return () => {
graph.off('node:mouseenter', (e) => {
@@ -449,7 +464,7 @@ const EditPipeline = () => {
},
'rect',
);
- console.log(graphRef, 'graphRef');
+
graph = new G6.Graph({
container: graphRef.current,
grid: true,
@@ -591,6 +606,8 @@ const EditPipeline = () => {
},
// linkCenter: true,
fitView: true,
+ minZoom: 0.5,
+ maxZoom: 3,
fitViewPadding: [320, 320, 220, 320],
});
// graph.on('dblclick', (e) => {
@@ -600,13 +617,7 @@ const EditPipeline = () => {
// 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);
- handlerClick(e);
- }
- });
+ graph.on('node:click', handlerClick);
graph.on('aftercreateedge', (e) => {
// update the sourceAnchor and targetAnchor for the newly added edge
graph.updateItem(e.edge, {
diff --git a/react-ui/src/pages/Pipeline/editPipeline/modelMenus.jsx b/react-ui/src/pages/Pipeline/editPipeline/modelMenus.jsx
index deb3846a..59a5bba1 100644
--- a/react-ui/src/pages/Pipeline/editPipeline/modelMenus.jsx
+++ b/react-ui/src/pages/Pipeline/editPipeline/modelMenus.jsx
@@ -2,23 +2,6 @@ import { getComponentAll } from '@/services/pipeline/index.js';
import { Collapse } from 'antd';
import { useEffect, useState } from 'react';
import Styles from './modelMenus.less';
-const items = [
- {
- key: '1',
- label: 'This is panel header 1',
- children: [1, 2, 3, 4, 5],
- },
- {
- key: '2',
- label: 'This is panel header 2',
- children: [1, 2, 3, 4, 5],
- },
- {
- key: '3',
- label: 'This is panel header 3',
- children: [1, 2, 3, 4, 5],
- },
-];
const ModelMenus = ({ onParDragEnd }) => {
const [modelMenusList, setModelMenusList] = useState([]);
useEffect(() => {
@@ -62,11 +45,13 @@ const ModelMenus = ({ onParDragEnd }) => {
}}
className={Styles.collapseItem}
>
-
+ {ele.icon_path && (
+
+ )}
{ele.component_label}
))
diff --git a/react-ui/src/pages/Pipeline/editPipeline/props.jsx b/react-ui/src/pages/Pipeline/editPipeline/props.jsx
index 58127381..179cd152 100644
--- a/react-ui/src/pages/Pipeline/editPipeline/props.jsx
+++ b/react-ui/src/pages/Pipeline/editPipeline/props.jsx
@@ -1,26 +1,31 @@
import KFIcon from '@/components/KFIcon';
+import SubAreaTitle from '@/components/SubAreaTitle';
import { getComputingResourceReq } from '@/services/pipeline';
import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
import { Button, Drawer, Form, Input, Select } from 'antd';
import { pick } from 'lodash';
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
+import PropsLabel from '../components/PropsLabel';
import ResourceSelectorModal, { ResourceSelectorType } from '../components/ResourceSelectorModal';
-import Styles from './editPipeline.less';
+import styles from './editPipeline.less';
+import { createMenuItems } from './utils';
const { TextArea } = Input;
+
const Props = forwardRef(({ onParentChange }, ref) => {
const [form] = Form.useForm();
-
const [stagingItem, setStagingItem] = useState({});
const [open, setOpen] = useState(false);
- const [selectedModel, setSelectedModel] = useState(undefined);
- const [selectedDataset, setSelectedDataset] = useState(undefined);
- const [resourceStandardList, setResourceStandardList] = useState([]);
+ const [selectedModel, setSelectedModel] = useState(undefined); // 选择的模型,为了再次打开时恢复原来的选择
+ const [selectedDataset, setSelectedDataset] = useState(undefined); // 选择的数据集,为了再次打开时恢复原来的选择
+ const [resourceStandardList, setResourceStandardList] = useState([]); // 资源规模列表
+ const [menuItems, setMenuItems] = useState([]);
useEffect(() => {
getComputingResource();
}, []);
+ // 获取资源规格列表数据
const getComputingResource = async () => {
const params = {
page: 0,
@@ -35,49 +40,22 @@ const Props = forwardRef(({ onParentChange }, ref) => {
const afterOpenChange = () => {
if (!open) {
- console.log(stagingItem, form.getFieldsValue());
- // 禁止校验 guard-for-in
- /* eslint-disable */
- for (let i in form.getFieldsValue()) {
- for (let j in stagingItem.in_parameters) {
- if (i == j) {
- console.log(j, i);
- stagingItem.in_parameters[j].value = form.getFieldsValue()[i];
- }
- }
- for (let p in stagingItem.out_parameters) {
- if (i == p) {
- stagingItem.out_parameters[p].value = form.getFieldsValue()[i];
- }
- }
- for (let k in stagingItem.control_strategy) {
- if (i == k) {
- stagingItem.control_strategy[k].value = form.getFieldsValue()[i];
- }
- }
- }
- /* eslint-enable */
- // setStagingItem({...stagingItem,})
- console.log(stagingItem.control_strategy);
+ console.log('zzzz', form.getFieldsValue());
+ const control_strategy = form.getFieldValue('control_strategy');
+ const in_parameters = form.getFieldValue('in_parameters');
+ const out_parameters = form.getFieldValue('out_parameters');
onParentChange({
...stagingItem,
- control_strategy: JSON.stringify(stagingItem.control_strategy),
- in_parameters: JSON.stringify(stagingItem.in_parameters),
- out_parameters: JSON.stringify(stagingItem.out_parameters),
...form.getFieldsValue(),
+ control_strategy: JSON.stringify(control_strategy),
+ in_parameters: JSON.stringify(in_parameters),
+ out_parameters: JSON.stringify(out_parameters),
});
- // onParentChange({...stagingItem,...form.getFieldsValue()})
}
};
const onClose = () => {
setOpen(false);
};
- const onFinish = (values) => {
- console.log('Success:', values);
- };
- const onFinishFailed = (errorInfo) => {
- console.log('Failed:', errorInfo);
- };
useImperativeHandle(ref, () => ({
getFieldsValue: async () => {
const [propsRes, propsError] = await to(form.validateFields());
@@ -88,47 +66,43 @@ const Props = forwardRef(({ onParentChange }, ref) => {
return Promise.reject(propsError);
}
},
- showDrawer(e) {
+ showDrawer(e, params, parentNodes) {
if (e.item && e.item.getModel()) {
- // console.log(e.item.getModel().in_parameters);
form.resetFields();
- form.setFieldsValue({
- ...e.item.getModel(),
- in_parameters: JSON.parse(e.item.getModel().in_parameters),
- out_parameters: JSON.parse(e.item.getModel().out_parameters),
- control_strategy: JSON.parse(e.item.getModel().control_strategy),
- });
- setStagingItem({
- ...e.item.getModel(),
- in_parameters: JSON.parse(e.item.getModel().in_parameters),
- out_parameters: JSON.parse(e.item.getModel().out_parameters),
- control_strategy: JSON.parse(e.item.getModel().control_strategy),
- });
- // form.setFieldsValue({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters)})
- // setStagingItem({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters)})
- // setTimeout(() => {
- // console.log(stagingItem);
- // }, (500));
+ const model = e.item.getModel();
+ try {
+ const nodeData = {
+ ...model,
+ in_parameters: JSON.parse(model.in_parameters),
+ out_parameters: JSON.parse(model.out_parameters),
+ control_strategy: JSON.parse(model.control_strategy),
+ };
+ setStagingItem({
+ ...nodeData,
+ });
+ form.setFieldsValue({
+ ...nodeData,
+ });
+ } catch (error) {
+ console.log(error);
+ }
setSelectedModel(undefined);
setSelectedDataset(undefined);
setOpen(true);
+
+ // 参数下拉菜单
+ setMenuItems(createMenuItems(params, parentNodes));
}
},
- propClose: async () => {
- setOpen(false);
- const [openRes, propsError] = await to(setOpen(false));
- console.log(setOpen(false));
+ propClose: () => {
+ close();
},
- // propClose() {
-
- // setOpen(false);
- // },
}));
// 选择数据集、模型
const selectResource = (name, item) => {
let type;
- let resource = undefined;
+ let resource;
switch (item.item_type) {
case 'dataset':
type = ResourceSelectorType.Dataset;
@@ -142,41 +116,37 @@ const Props = forwardRef(({ onParentChange }, ref) => {
type = ResourceSelectorType.Mirror;
break;
}
- const { close } = openAntdModal(
- ResourceSelectorModal,
- {
- type,
- defaultExpandedKeys: resource ? [resource.id] : [],
- defaultCheckedKeys: resource ? [`${resource.id}-${resource.version}`] : [],
- defaultActiveTab: resource?.activeTab,
- onOk: (res) => {
- if (res) {
- if (type === ResourceSelectorType.Mirror) {
- form.setFieldValue(name, res);
- } else {
- const jsonObj = pick(res, ['id', 'version', 'path']);
- const value = JSON.stringify(jsonObj);
- form.setFieldValue(name, value);
- }
-
- if (type === ResourceSelectorType.Dataset) {
- setSelectedDataset(res);
- } else if (type === ResourceSelectorType.Model) {
- setSelectedModel(res);
- }
+ const { close } = openAntdModal(ResourceSelectorModal, {
+ type,
+ defaultExpandedKeys: resource ? [resource.id] : [],
+ defaultCheckedKeys: resource ? [`${resource.id}-${resource.version}`] : [],
+ defaultActiveTab: resource?.activeTab,
+ onOk: (res) => {
+ if (res) {
+ if (type === ResourceSelectorType.Mirror) {
+ form.setFieldValue(name, res);
} else {
- if (type === ResourceSelectorType.Dataset) {
- setSelectedDataset(null);
- } else if (type === ResourceSelectorType.Model) {
- setSelectedModel(null);
- }
- form.setFieldValue(name, '');
+ const jsonObj = pick(res, ['id', 'version', 'path']);
+ const value = JSON.stringify(jsonObj);
+ form.setFieldValue(name, { ...item, value });
}
- close();
- },
+
+ if (type === ResourceSelectorType.Dataset) {
+ setSelectedDataset(res);
+ } else if (type === ResourceSelectorType.Model) {
+ setSelectedModel(res);
+ }
+ } else {
+ if (type === ResourceSelectorType.Dataset) {
+ setSelectedDataset(null);
+ } else if (type === ResourceSelectorType.Model) {
+ setSelectedModel(null);
+ }
+ form.setFieldValue(name, '');
+ }
+ close();
},
- true,
- );
+ });
};
// 获取选择数据集、模型后面按钮 icon
@@ -191,16 +161,31 @@ const Props = forwardRef(({ onParentChange }, ref) => {
}
};
+ // 筛选资源规格
const filterResourceStandard = (input, { computing_resource = '' }) => {
return computing_resource.toLocaleLowerCase().includes(input.toLocaleLowerCase());
};
+ // 参数回填
+ const handleParameterClick = (name, value) => {
+ form.setFieldValue(name, value);
+ };
+
// 控制策略
- const controlStrategy = stagingItem.control_strategy;
+ const controlStrategyList = Object.entries(stagingItem.control_strategy ?? {}).map(
+ ([key, value]) => ({ key, value }),
+ );
+
// 输入参数
- const inParameters = stagingItem.in_parameters;
+ const inParametersList = Object.entries(stagingItem.in_parameters ?? {}).map(([key, value]) => ({
+ key,
+ value,
+ }));
+
// 输出参数
- const outParameters = stagingItem.out_parameters;
+ const outParametersList = Object.entries(stagingItem.out_parameters ?? {}).map(
+ ([key, value]) => ({ key, value }),
+ );
return (
<>
@@ -213,15 +198,15 @@ const Props = forwardRef(({ onParentChange }, ref) => {
onClose={onClose}
afterOpenChange={afterOpenChange}
open={open}
- width={420}
+ width={520}
+ className={styles.editPipelineProps}
>
-
}
- title="Ant Design"
- subTitle={intl.formatMessage({ id: 'pages.layouts.userLayout.title' })}
- initialValues={{
- autoLogin: true,
- }}
- actions={[
-
,
-
,
- ]}
- onFinish={async (values) => {
- await handleSubmit(values as API.LoginParams);
- }}
- >
-
-
- {code !== 200 && loginType === 'account' && (
-
- )}
- {type === 'account' && (
- <>
-
,
- }}
- placeholder={intl.formatMessage({
- id: 'pages.login.username.placeholder',
- defaultMessage: '用户名: admin',
- })}
- rules={[
- {
- required: true,
- message: (
-
- ),
- },
- ]}
- />
-
,
- }}
- placeholder={intl.formatMessage({
- id: 'pages.login.password.placeholder',
- defaultMessage: '密码: admin123',
- })}
- rules={[
- {
- required: true,
- message: (
-
- ),
- },
- ]}
- />
-
-
+ {code !== 200 && loginType === 'account' && (
+
+ )}
+ {type === 'account' && (
+ <>
,
}}
- name="code"
placeholder={intl.formatMessage({
- id: 'pages.login.captcha.placeholder',
- defaultMessage: '请输入验证',
+ id: 'pages.login.username.placeholder',
+ defaultMessage: '用户名: admin',
})}
rules={[
{
required: true,
message: (
),
},
]}
/>
-
-
- ,
}}
- preview={false}
- onClick={() => getCaptchaCode()}
+ placeholder={intl.formatMessage({
+ id: 'pages.login.password.placeholder',
+ defaultMessage: '密码: admin123',
+ })}
+ rules={[
+ {
+ required: true,
+ message: (
+
+ ),
+ },
+ ]}
/>
-
-
- >
- )}
-
- {code !== 200 && loginType === 'mobile' &&
}
- {type === 'mobile' && (
- <>
-
,
- }}
- name="mobile"
- placeholder={intl.formatMessage({
- id: 'pages.login.phoneNumber.placeholder',
- defaultMessage: '手机号',
- })}
- rules={[
- {
- required: true,
- message: (
-
- ),
- },
- {
- pattern: /^1\d{10}$/,
- message: (
-
+
+
+ ),
+ },
+ ]}
/>
- ),
- },
- ]}
- />
- ,
- }}
- captchaProps={{
- size: 'large',
- }}
- placeholder={intl.formatMessage({
- id: 'pages.login.captcha.placeholder',
- defaultMessage: '请输入验证码',
- })}
- captchaTextRender={(timing, count) => {
- if (timing) {
- return `${count} ${intl.formatMessage({
- id: 'pages.getCaptchaSecondText',
- defaultMessage: '获取验证码',
- })}`;
- }
- return intl.formatMessage({
- id: 'pages.login.phoneLogin.getVerificationCode',
- defaultMessage: '获取验证码',
- });
- }}
- name="captcha"
- rules={[
- {
- required: true,
- message: (
-
+
+ getCaptchaCode()}
/>
- ),
- },
- ]}
- onGetCaptcha={async (phone) => {
- const result = await getFakeCaptcha({
- phone,
- });
- if (!result) {
- return;
- }
- message.success('获取验证码成功!验证码为:1234');
+
+
+ >
+ )}
+
+ {code !== 200 && loginType === 'mobile' && }
+ {type === 'mobile' && (
+ <>
+ ,
+ }}
+ name="mobile"
+ placeholder={intl.formatMessage({
+ id: 'pages.login.phoneNumber.placeholder',
+ defaultMessage: '手机号',
+ })}
+ rules={[
+ {
+ required: true,
+ message: (
+
+ ),
+ },
+ {
+ pattern: /^1\d{10}$/,
+ message: (
+
+ ),
+ },
+ ]}
+ />
+ ,
+ }}
+ captchaProps={{
+ size: 'large',
+ }}
+ placeholder={intl.formatMessage({
+ id: 'pages.login.captcha.placeholder',
+ defaultMessage: '请输入验证码',
+ })}
+ captchaTextRender={(timing, count) => {
+ if (timing) {
+ return `${count} ${intl.formatMessage({
+ id: 'pages.getCaptchaSecondText',
+ defaultMessage: '获取验证码',
+ })}`;
+ }
+ return intl.formatMessage({
+ id: 'pages.login.phoneLogin.getVerificationCode',
+ defaultMessage: '获取验证码',
+ });
+ }}
+ name="captcha"
+ rules={[
+ {
+ required: true,
+ message: (
+
+ ),
+ },
+ ]}
+ onGetCaptcha={async (phone) => {
+ const result = await getFakeCaptcha({
+ phone,
+ });
+ if (!result) {
+ return;
+ }
+ message.success('获取验证码成功!验证码为:1234');
+ }}
+ />
+ >
+ )}
+
- >
- )}
-
+