Commit 5daf5172 authored by wuyongsheng's avatar wuyongsheng

feat: 构建算子添加联调

parent 7d03e5ab
<?xml version="1.0" encoding="UTF-8"?>
<svg width="240px" height="249px" viewBox="0 0 240 249" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>矩形备份 26</title>
<defs>
<rect id="path-1" x="0" y="0" width="240" height="249" rx="8"></rect>
<filter x="-14.5%" y="-17.6%" width="129.1%" height="135.2%" filterUnits="objectBoundingBox" id="filter-3">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="3" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.803607664 0 0 0 0 0.598314046 0 0 0 0 0.12907149 0 0 0 1 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="path-4" x="0" y="0" width="110" height="91" rx="5.5"></rect>
</defs>
<g id="新" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="矩形备份-26">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="蒙版" fill="#FFFFFF" xlink:href="#path-1"></use>
<rect id="矩形备份-6" fill="#FFB919" mask="url(#mask-2)" x="0" y="0" width="240" height="165"></rect>
<g id="编组-8备份-3" mask="url(#mask-2)">
<g transform="translate(65.000000, 37.000000)" id="编组-19">
<g id="矩形" filter="url(#filter-3)">
<mask id="mask-5" fill="white">
<use xlink:href="#path-4"></use>
</mask>
<use id="蒙版" fill="#FFFFFF" xlink:href="#path-4"></use>
<rect fill="#136EFA" mask="url(#mask-5)" x="0" y="0" width="2.5" height="91.4251263"></rect>
<rect fill="#C2C6CC" mask="url(#mask-5)" x="15" y="8.76679293" width="25" height="5.00959596"></rect>
</g>
<g id="编组-7" transform="translate(15.250000, 26.070581)">
<path d="M11.25,5.51515152 L7.125,5.51515152 C5.60621694,5.51515152 4.375,6.74636845 4.375,8.26515152 L4.375,46.8863636 C4.375,48.4051467 5.60621694,49.6363636 7.125,49.6363636 L11.25,49.6363636 L11.25,49.6363636" id="路径-4" stroke="#979797" stroke-width="1.375"></path>
<rect id="矩形备份-10" fill="#F0F2F5" x="0" y="0" width="45" height="11.030303" rx="2.75"></rect>
<rect id="矩形备份-11" fill="#F0F2F5" x="0" y="22.0606061" width="24.75" height="11.030303" rx="2.75"></rect>
<rect id="矩形备份-15" fill="#F0F2F5" x="39.875" y="33.332197" width="24.75" height="11.030303" rx="2.75"></rect>
<rect id="矩形备份-12" fill="#F0F2F5" x="0" y="44.1212121" width="24.75" height="11.030303" rx="2.75"></rect>
<rect id="矩形备份-4" fill="#C2C6CC" x="2.25" y="3.20453283" width="32.5" height="3.75719697"></rect>
<rect id="矩形备份-5" fill="#C2C6CC" x="2.25" y="25.7477146" width="13.75" height="3.75719697"></rect>
<rect id="矩形备份-8" fill="#C2C6CC" x="2.25" y="48.2908965" width="13.75" height="3.75719697"></rect>
<rect id="矩形备份-9" fill="#C2C6CC" x="42.25" y="37.0193056" width="13.75" height="3.75719697"></rect>
<ellipse id="椭圆形备份-3" fill="#0DD09B" cx="39" cy="5.00959596" rx="1.25" ry="1.25239899"></ellipse>
<ellipse id="椭圆形备份-4" fill="#0DD09B" cx="20.25" cy="27.5527778" rx="1.25" ry="1.25239899"></ellipse>
<ellipse id="椭圆形备份-5" fill="#0DD09B" cx="20.25" cy="50.0959596" rx="1.25" ry="1.25239899"></ellipse>
<ellipse id="椭圆形备份-6" fill="#0DD09B" cx="60.25" cy="38.8243687" rx="1.25" ry="1.25239899"></ellipse>
<line x1="4.125" y1="38.7726641" x2="39.875" y2="38.8473485" id="路径-6" stroke="#979797" stroke-width="1.375"></line>
</g>
</g>
</g>
<text id="构建批式算子" mask="url(#mask-2)" font-family="PingFangSC-Medium, PingFang SC" font-size="16" font-weight="400" line-spacing="24" fill="#1E2633">
<tspan x="72" y="198.000567">构建批式算子</tspan>
</text>
<text id="仅支持shell脚本" mask="url(#mask-2)" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="20" fill="#8A9099">
<tspan x="77.484" y="222">仅支持shell脚本</tspan>
</text>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="240px" height="249px" viewBox="0 0 240 249" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 40备份 8</title>
<defs>
<rect id="path-1" x="0" y="0" width="240" height="249" rx="8"></rect>
<rect id="path-3" x="0" y="0" width="96.2033898" height="23.9201798" rx="4.80638562"></rect>
<filter x="-8.8%" y="-27.2%" width="117.7%" height="171.1%" filterUnits="objectBoundingBox" id="filter-5">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.114151225 0 0 0 0 0.59037626 0 0 0 0 0.477660275 0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
<rect id="path-6" x="0" y="0" width="96.2033898" height="23.9201798" rx="4.80638562"></rect>
<filter x="-8.8%" y="-27.2%" width="117.7%" height="171.1%" filterUnits="objectBoundingBox" id="filter-8">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.114151225 0 0 0 0 0.59037626 0 0 0 0 0.477660275 0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
<rect id="path-9" x="0" y="0" width="96.2033898" height="23.9201798" rx="4.80638562"></rect>
<filter x="-8.8%" y="-27.2%" width="117.7%" height="171.1%" filterUnits="objectBoundingBox" id="filter-11">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.114151225 0 0 0 0 0.59037626 0 0 0 0 0.477660275 0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="新" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-40备份-8">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="蒙版" fill="#FFFFFF" xlink:href="#path-1"></use>
<rect id="矩形" fill="#02AB83" mask="url(#mask-2)" x="0" y="0" width="240" height="165"></rect>
<g id="编组-2" mask="url(#mask-2)">
<g transform="translate(56.000000, 40.000000)">
<g id="矩形">
<mask id="mask-4" fill="white">
<use xlink:href="#path-3"></use>
</mask>
<g id="蒙版">
<use fill="black" fill-opacity="1" filter="url(#filter-5)" xlink:href="#path-3"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-3"></use>
</g>
<rect fill="#136EFA" mask="url(#mask-4)" x="0" y="0" width="2.18644068" height="24.0319281"></rect>
<rect fill="#C2C6CC" mask="url(#mask-4)" x="13.1186441" y="7.64652257" width="50.2881356" height="8.73888294"></rect>
<ellipse id="椭圆形备份-3" fill="#0DD09B" mask="url(#mask-4)" cx="77.6186441" cy="12.015964" rx="3.27966102" ry="3.2770811"></ellipse>
</g>
<g id="矩形" transform="translate(32.796610, 30.586090)">
<mask id="mask-7" fill="white">
<use xlink:href="#path-6"></use>
</mask>
<g id="蒙版">
<use fill="black" fill-opacity="1" filter="url(#filter-8)" xlink:href="#path-6"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-6"></use>
</g>
<rect fill="#136EFA" mask="url(#mask-7)" x="0" y="0" width="2.18644068" height="24.0319281"></rect>
<rect fill="#C2C6CC" mask="url(#mask-7)" x="13.1186441" y="7.64652257" width="50.2881356" height="8.73888294"></rect>
<ellipse id="椭圆形备份-3" fill="#0DD09B" mask="url(#mask-7)" cx="77.6186441" cy="12.015964" rx="3.27966102" ry="3.2770811"></ellipse>
</g>
<g id="矩形" transform="translate(4.372881, 60.079820)">
<mask id="mask-10" fill="white">
<use xlink:href="#path-9"></use>
</mask>
<g id="蒙版">
<use fill="black" fill-opacity="1" filter="url(#filter-11)" xlink:href="#path-9"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-9"></use>
</g>
<rect fill="#136EFA" mask="url(#mask-10)" x="0" y="0" width="2.18644068" height="24.0319281"></rect>
<rect fill="#C2C6CC" mask="url(#mask-10)" x="13.1186441" y="7.64652257" width="50.2881356" height="8.73888294"></rect>
<ellipse id="椭圆形备份-3" fill="#0DD09B" mask="url(#mask-10)" cx="77.6186441" cy="12.015964" rx="3.27966102" ry="3.2770811"></ellipse>
</g>
</g>
</g>
<text id="构建流式算子" mask="url(#mask-2)" font-family="PingFangSC-Medium, PingFang SC" font-size="16" font-weight="400" line-spacing="24" fill="#1E2633">
<tspan x="72" y="198">构建流式算子</tspan>
</text>
<text id="仅支持Python脚本" mask="url(#mask-2)" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="20" fill="#8A9099">
<tspan x="70.836" y="222">仅支持Python脚本</tspan>
</text>
</g>
</g>
</svg>
\ No newline at end of file
......@@ -77,14 +77,19 @@ const MyMenu = (props: IMyMenuProps) => {
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
setAnchorEl(event.currentTarget);
event.stopPropagation();
};
const handleClose = (value: string) => {
setAnchorEl(null);
};
const handleMenuClick = (value: string) => {
const handleMenuClick = (
value: string,
event: React.MouseEvent<HTMLLIElement, MouseEvent>
) => {
setValue && setValue(value);
setAnchorEl(null);
event.stopPropagation();
};
return (
......@@ -124,7 +129,7 @@ const MyMenu = (props: IMyMenuProps) => {
{options.map((option, index) => {
return (
<MenuItem
onClick={() => handleMenuClick(option.value)}
onClick={(e) => handleMenuClick(option.value, e)}
selected={value === option.value}
key={index}
>
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2021-12-04 15:46:25
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 18:41:04
* @LastEditTime: 2022-10-24 17:39:24
* @FilePath: /lionet-slb-pc/src/components/SearchView/components/Collapse.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -49,6 +49,8 @@ interface IProps
isTitle?: boolean;
/** 是否显示提示文案 */
error?: boolean;
/** 默认值 */
defaultValue?: string;
/** 提示文案 */
helpertext?: string;
}
......@@ -64,6 +66,7 @@ export default function MySelect(props: IProps) {
fullWidth,
error = false,
helpertext,
defaultValue,
...other
} = props;
......@@ -193,7 +196,7 @@ export default function MySelect(props: IProps) {
},
});
const [insideValue, setInsideValue] = useState<string>("");
const [insideValue, setInsideValue] = useState<string>(defaultValue || "");
const handleChange = (e: SelectChangeEvent<unknown>) => {
setInsideValue(e.target.value as string);
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-18 10:54:54
* @LastEditTime: 2022-10-24 20:23:02
* @FilePath: /bkunyun/src/router/index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
import { AnyMap } from "immer/dist/internal";
......@@ -14,6 +14,7 @@ import * as React from "react";
import NotFound from "@/views/404";
import Demo from "@/views/demo";
import SeeEnv from "@/views/ResourceCenter/UserResources/UserResourcesEnvironment/SeeEnv";
import OperatorDetails from "@/views/ResourceCenter/UserResources/WorkflowOperator/components/OperatorDetails";
import ProjectSetting from "@/views/Project/ProjectSetting";
import ProjectData from "@/views/Project/ProjectData";
import ProjectWorkbench from "@/views/Project/ProjectWorkbench";
......@@ -53,9 +54,9 @@ export const elements: {
}) => JSX.Element | any;
} = {
Demo: Demo,
SeeTemplate: Demo,
UserResources: UserResources,
SeeEnv: SeeEnv,
OperatorDetails: OperatorDetails,
ProjectSetting: ProjectSetting,
ProjectData: ProjectData,
ProjectWorkbench: ProjectWorkbench,
......
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-24 17:32:00
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-24 19:42:23
* @FilePath: /bkunyun/src/views/CustomOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import React, { useCallback, useEffect, useState } from "react";
import { observer } from "mobx-react-lite";
import FullScreenDrawer from "@/components/CommonComponents/FullScreenDrawer";
import MyButton from "@/components/mui/MyButton";
import OperatorList from "./components/OperatorList";
// import Flow from "../Project/components/Flow";
import { useMessage } from "@/components/MySnackbar";
import BatchOperatorFlow from "../Project/components/Flow/components/BatchOperatorFlow";
import SaveOperator from "./components/SaveOperator";
import { ITask } from "../Project/ProjectSubmitWork/interface";
import _ from "lodash";
import styles from "./index.module.css";
import useCheckOperator from "./useCheckOperator";
type IProps = {
setShowCustomOperator: any;
......@@ -19,133 +27,24 @@ type IProps = {
const CustomOperator = observer((props: IProps) => {
const { setShowCustomOperator, initOperatorList, productId } = props;
const Message = useMessage();
const [operatorList, setOperatorList] = useState<ITask[]>(initOperatorList);
const [saveFormDialog, setSaveFormDialog] = useState(false);
const [inputActive, setInputActive] = useState(false);
// const [showCustomOperator, setShowCustomOperator] = useState(false);
const { handleCheck } = useCheckOperator(operatorList, () =>
setSaveFormDialog(true)
);
/** 设置选中唯一标识符 */
const handleNodeClick = useCallback((val: string) => {
// setSelectTaskId(val);
}, []);
// 判断 每个流算子必须至少有一条连接线。
const checkHasOneLine = (sourceArr: string[], targetArr: string[]) => {
const all = _.uniq([...sourceArr, ...targetArr]);
if (all.length === operatorList.length) {
return true;
} else {
return false;
}
// _.uniq([2, 1, 2]);
};
// 判断 每个起始算子(可以有多个起始点)的输入必须为文件的路径输入或数据集的路径输入。
const checkIn = (targetArr: string[]) => {
const uniqTargetArr = _.uniq(targetArr);
if (uniqTargetArr.length === operatorList.length) {
// 流节点连成一个圈了
return false;
}
let check = true;
operatorList.forEach((flowNode) => {
if (uniqTargetArr.indexOf(flowNode.id) === -1) {
// 该节点的输入没有连线 也就是说这个节点是起点
const inArr = flowNode.parameters.filter(
(parameter) => parameter.parameterGroup === "in"
);
if (inArr.length > 0) {
if (
!inArr.some((inItem) => {
return (
(inItem.domType || "").toLowerCase() === "dataset" ||
(inItem.domType || "").toLowerCase() === "path" ||
(inItem.domType || "").toLowerCase() === "file"
);
})
) {
check = false;
}
} else {
// 起点没有输入
check = false;
}
}
});
return check;
};
// 判断 起码有一个结尾算子(可以有多个结尾点)的输出必须为文件保存或数据集保存。
const checkOut = (sourceArr: string[]) => {
const uniqSourceArr = _.uniq(sourceArr);
if (uniqSourceArr.length === operatorList.length) {
// 流节点连成一个圈了
return false;
}
let check = true;
operatorList.forEach((flowNode) => {
if (uniqSourceArr.indexOf(flowNode.id) === -1) {
// 该节点的输入没有连线 也就是说这个节点是起点
const outArr = flowNode.parameters.filter(
(parameter) => parameter.parameterGroup === "out"
);
if (outArr.length > 0) {
if (
!outArr.some((outItem) => {
return (
(outItem.domType || "").toLowerCase() === "dataset" ||
(outItem.domType || "").toLowerCase() === "file" ||
(outItem.domType || "").toLowerCase() === "path" ||
(outItem.domType || "").toLowerCase() === "input"
);
})
) {
check = false;
}
} else {
// 起点没有输入
check = false;
}
}
});
return check;
};
useEffect(() => {
sessionStorage.setItem("operatorList", JSON.stringify(operatorList));
}, [operatorList]);
const handleCheck = () => {
if (operatorList.length === 0) {
Message.error("内容不能为空!");
return;
}
let sourceArr: string[] = [];
let targetArr: string[] = [];
operatorList.forEach((flowNode) => {
flowNode.edges.forEach((edge) => {
edge.source && sourceArr.push(edge.source);
edge.target && targetArr.push(edge.target);
});
});
if (!checkHasOneLine([...sourceArr], [...targetArr])) {
Message.error("部分算子没有流程线,请检查流程!");
return;
}
if (!checkIn([...targetArr])) {
Message.error("每个流程第一步需读取文件/数据集,请检查流程!");
return;
}
if (!checkOut([...sourceArr])) {
Message.error(
"每个流程最后一步必须将数据写入为文件/数据集,请检查流程!"
);
return;
}
setSaveFormDialog(true);
};
return (
<FullScreenDrawer handleClose={setShowCustomOperator} zIndex={1100}>
<div className={styles.customOperator}>
......
import { useMessage } from "@/components/MySnackbar";
import _ from "lodash";
import { useState } from "react";
import { ITask } from "../Project/ProjectSubmitWork/interface";
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-24 18:08:47
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-24 18:31:12
* @FilePath: /bkunyun/src/views/CustomOperator/useCheckOperator.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
const useCheckOperator = (
operatorList: ITask[],
successCallBack?: any,
nullText?: string
) => {
const Message = useMessage();
const [checkStatus, setCheckStatus] = useState<boolean>(false);
// 判断 每个起始算子(可以有多个起始点)的输入必须为文件的路径输入或数据集的路径输入。
const checkIn = (targetArr: string[]) => {
const uniqTargetArr = _.uniq(targetArr);
if (uniqTargetArr.length === operatorList.length) {
// 流节点连成一个圈了
return false;
}
let check = true;
operatorList.forEach((flowNode) => {
if (uniqTargetArr.indexOf(flowNode.id) === -1) {
// 该节点的输入没有连线 也就是说这个节点是起点
const inArr = flowNode.parameters.filter(
(parameter) => parameter.parameterGroup === "in"
);
if (inArr.length > 0) {
if (
!inArr.some((inItem) => {
return (
(inItem.domType || "").toLowerCase() === "dataset" ||
(inItem.domType || "").toLowerCase() === "path" ||
(inItem.domType || "").toLowerCase() === "file"
);
})
) {
check = false;
}
} else {
// 起点没有输入
check = false;
}
}
});
return check;
};
// 判断 起码有一个结尾算子(可以有多个结尾点)的输出必须为文件保存或数据集保存。
const checkOut = (sourceArr: string[]) => {
const uniqSourceArr = _.uniq(sourceArr);
if (uniqSourceArr.length === operatorList.length) {
// 流节点连成一个圈了
return false;
}
let check = true;
operatorList.forEach((flowNode) => {
if (uniqSourceArr.indexOf(flowNode.id) === -1) {
// 该节点的输入没有连线 也就是说这个节点是起点
const outArr = flowNode.parameters.filter(
(parameter) => parameter.parameterGroup === "out"
);
if (outArr.length > 0) {
if (
!outArr.some((outItem) => {
return (
(outItem.domType || "").toLowerCase() === "dataset" ||
(outItem.domType || "").toLowerCase() === "file" ||
(outItem.domType || "").toLowerCase() === "path" ||
(outItem.domType || "").toLowerCase() === "input"
);
})
) {
check = false;
}
} else {
// 起点没有输入
check = false;
}
}
});
return check;
};
// 判断 每个流算子必须至少有一条连接线。
const checkHasOneLine = (sourceArr: string[], targetArr: string[]) => {
const all = _.uniq([...sourceArr, ...targetArr]);
if (all.length === operatorList.length) {
return true;
} else {
return false;
}
// _.uniq([2, 1, 2]);
};
const handleCheck = () => {
if (operatorList.length === 0) {
Message.error(nullText || "内容不能为空!");
return;
}
let sourceArr: string[] = [];
let targetArr: string[] = [];
operatorList.forEach((flowNode) => {
flowNode.edges.forEach((edge) => {
edge.source && sourceArr.push(edge.source);
edge.target && targetArr.push(edge.target);
});
});
if (!checkHasOneLine([...sourceArr], [...targetArr])) {
Message.error("部分算子没有流程线,请检查流程!");
return;
}
if (!checkIn([...targetArr])) {
Message.error("每个流程第一步需读取文件/数据集,请检查流程!");
return;
}
if (!checkOut([...sourceArr])) {
Message.error(
"每个流程最后一步必须将数据写入为文件/数据集,请检查流程!"
);
return;
}
setCheckStatus(true);
successCallBack();
};
return { handleCheck, checkStatus };
};
export default useCheckOperator;
......@@ -64,6 +64,9 @@
}
.codeTitle {
background-color: rgba(230, 233, 237, 1);
display: flex;
justify-content: space-between;
align-items: center;
padding: 11px 20px;
color: rgba(30, 38, 51, 1);
font-size: 14px;
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-18 16:12:55
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-24 13:40:27
* @LastEditTime: 2022-10-24 20:39:26
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/components/AddOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -11,6 +11,7 @@ import classNames from "classnames";
import { observer } from "mobx-react";
import { Base64 } from "js-base64";
import _ from "lodash";
import { toJS } from "mobx";
import MyInput from "@/components/mui/MyInput";
import MySelect from "@/components/mui/MySelect";
......@@ -26,20 +27,24 @@ import { useStores } from "@/store";
import useMyRequest from "@/hooks/useMyRequest";
import { getActorEnvOptions, saveOperator } from "@/api/resourceCenter";
import { IOperatorAddFormData } from "../../interface";
import { checkFormData, checkParamsConfig, initCode } from "./utils";
import { checkFormData, checkParamsConfig, initCode, text } from "./utils";
import batchOperator from "@/assets/resourceCenter/batchOperator.svg";
import flowOperator from "@/assets/resourceCenter/flowOperator.svg";
import { useMessage } from "@/components/MySnackbar";
import style from "./index.module.css";
import { useMessage } from "@/components/MySnackbar";
import MyPopover from "@/components/mui/MyPopover";
import useCheckOperator from "@/views/CustomOperator/useCheckOperator";
interface IAddOperator {
pageType: string;
setPageType: (val: string) => void;
productId: string;
detailsId: string;
}
type IBuildType = "ENVIRONMENT" | "OPERATOR";
const AddOperator = observer((props: IAddOperator) => {
const { setPageType } = props;
const { pageType, setPageType, detailsId } = props;
const Message = useMessage();
/** 创建类型 BATCH - 批算子; FLOW - 流算子*/
......@@ -53,6 +58,10 @@ const AddOperator = observer((props: IAddOperator) => {
const [inputActive, setInputActive] = useState(false);
/** 产品store */
const { productListStore } = useStores();
/** 产品第一项 默认所属产品值 */
const defaultProduct = toJS(productListStore?.productList)?.length
? toJS(productListStore?.productList[0])?.value
: undefined;
/** 应用环境下拉 */
const [actorEnvOptions, setActorEnvOptions] = useState([]);
/** 参数配置 */
......@@ -64,6 +73,14 @@ const AddOperator = observer((props: IAddOperator) => {
const [formData, setFormData] = useState<IOperatorAddFormData>({});
/** 表单数据修改 */
const [formErrors, setFormErrors] = useState<IOperatorAddFormData>({});
/** 是否打开帮助手册 */
const [tipsOpen, setTipsOpen] = useState<boolean>(false);
const { handleCheck, checkStatus } = useCheckOperator(
operatorList,
() => {},
"流程编排不能为空"
);
const buildTypeList = useMemo(() => {
return [
......@@ -80,7 +97,8 @@ const AddOperator = observer((props: IAddOperator) => {
[formData]
);
const paramsConfigBlur = () => {
const paramsConfigBlur = useCallback(() => {
console.log(2);
if (code === "") return;
let result;
try {
......@@ -89,11 +107,11 @@ const AddOperator = observer((props: IAddOperator) => {
setCode(result);
const checkErrorArr = checkParamsConfig(result);
if (checkErrorArr.length) {
Message.error("参数配置不正确!");
setFormErrors({
...formErrors,
...{ parameters: "参数配置不正确!" },
});
// Message.error("参数配置不正确!");
// setFormErrors({
// ...formErrors,
// ...{ parameters: "参数配置不正确!" },
// });
const codeDom = document.getElementById("paramsConfig");
const all = codeDom?.getElementsByClassName("cm-line");
......@@ -107,7 +125,7 @@ const AddOperator = observer((props: IAddOperator) => {
setTimeout(() => {
allArr[x].style =
"text-decoration: wavy underline; text-decoration-color: #ff4e4e;";
}, 500);
}, 200);
}
}
} else {
......@@ -120,7 +138,7 @@ const AddOperator = observer((props: IAddOperator) => {
Message.error("JSON格式不正确!");
console.log("JSON格式不正确!");
}
};
}, [Message, code, formErrors]);
/** 获取应用环境下拉 */
const { run: fetchActorEnvOptions } = useMyRequest(getActorEnvOptions, {
......@@ -145,11 +163,17 @@ const AddOperator = observer((props: IAddOperator) => {
};
const handleSubmit = useCallback(() => {
const resultErrors = checkFormData(formData, taskType);
const resultErrors = checkFormData(formData, batchBuildType);
// paramsConfigBlur();
formErrors?.parameters
? setFormErrors({ ...resultErrors, parameters: formErrors.parameters })
: setFormErrors({ ...resultErrors });
if (Object.getOwnPropertyNames(resultErrors)?.length) return;
if (taskType === "BATCH" && batchBuildType === "OPERATOR") {
handleCheck();
}
if (Object.getOwnPropertyNames(resultErrors)?.length || !checkStatus)
return;
let newParameters = [];
try {
newParameters = JSON.parse(code);
......@@ -157,42 +181,78 @@ const AddOperator = observer((props: IAddOperator) => {
console.log(err);
}
console.log(operatorList, "operatorList");
const params = {
...formData,
command: Base64.encode(command),
parameters: newParameters,
...(batchBuildType === "ENVIRONMENT" && taskType === "BATCH"
? { command: Base64.encode(command) }
: {}),
parameters:
batchBuildType === "OPERATOR" && taskType === "BATCH"
? operatorList
: newParameters,
type: taskType,
};
saveOperator(params).then((res) => {
console.log(res, "333");
/** 基于流算子没有应用环境字段 */
if (batchBuildType === "OPERATOR") {
delete params.envId;
}
saveOperator(params).then((res: any) => {
if (res?.message === "success") {
Message.success("构建成功");
} else {
Message.success(res?.message || "构建失败");
}
});
}, [code, command, formData, formErrors, taskType]);
}, [
Message,
batchBuildType,
checkStatus,
code,
command,
formData,
formErrors.parameters,
handleCheck,
operatorList,
taskType,
]);
return (
<div className={style.addOperatorBox}>
<div className={style.left}>
<SwitchBatchFolw
bottomImg={flowOperator}
topImg={batchOperator}
active={taskType}
setActive={setTaskType}
setActive={(e: "BATCH" | "FLOW") => {
setBatchBuildType("ENVIRONMENT");
setTaskType(e);
}}
goBack={() => setPageType("")}
></SwitchBatchFolw>
/>
</div>
<div className={style.right}>
<div className={style.title}>
{taskType === "BATCH" ? "批式算子信息" : "流式算子信息"}
</div>
<div
style={{
paddingBottom: batchBuildType === "ENVIRONMENT" ? "20px" : "2px",
}}
>
<RadioGroupOfButtonStyle
RadiosBoxStyle={{ width: 236 }}
value={batchBuildType}
radioOptions={buildTypeList}
handleRadio={handleRadio}
/>
</div>
{taskType === "BATCH" ? (
<div
style={{
paddingBottom: batchBuildType === "ENVIRONMENT" ? "20px" : "2px",
}}
>
<RadioGroupOfButtonStyle
RadiosBoxStyle={{ width: 236 }}
value={batchBuildType}
radioOptions={buildTypeList}
handleRadio={handleRadio}
/>
</div>
) : null}
<div className={style.content}>
<div
className={classNames({
......@@ -237,6 +297,7 @@ const AddOperator = observer((props: IAddOperator) => {
}}
/>
</FormItemBox>
<FormItemBox
label="所属产品"
labelClassName={style.labelClassName}
......@@ -250,6 +311,7 @@ const AddOperator = observer((props: IAddOperator) => {
<MySelect
fullWidth
options={productListStore?.productList || []}
defaultValue={defaultProduct}
value={formData?.productId}
onChange={(e) => {
changeFormData({ productId: e });
......@@ -297,7 +359,20 @@ const AddOperator = observer((props: IAddOperator) => {
{batchBuildType === "ENVIRONMENT" ? (
<div className={style.codeBox}>
<div className={style.codeTitle}>参数配置</div>
<div className={style.codeTitle}>
<span>参数配置</span>
<MyPopover
open={tipsOpen}
changeOpen={(val) => setTipsOpen(val)}
content={<pre>{text}</pre>}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
>
<span style={{ color: "#1370FF" }}>帮助手册</span>
</MyPopover>
</div>
<div className={style.code}>
<Code
id="paramsConfig"
......@@ -336,42 +411,45 @@ const AddOperator = observer((props: IAddOperator) => {
</div>
)}
</div>
<div className={style.parameterConfigBox}>
<div className={style.codeTitle}>
{batchBuildType === "ENVIRONMENT" ? "运行脚本" : "流程编排"}
</div>
{batchBuildType === "ENVIRONMENT" ? (
<div className={style.code}>
<Code
value={command}
onChange={(e: string) => setCommand(e)}
height="350px"
/>
{taskType === "FLOW" ? null : (
<div className={style.parameterConfigBox}>
<div className={style.codeTitle}>
{batchBuildType === "ENVIRONMENT" ? "运行脚本" : "流程编排"}
</div>
) : (
<div
id="customOperatorFlow"
style={{ position: "relative", height: 400 }}
>
<OperatorList
operatorList={operatorList}
setOperatorList={setOperatorList}
setInputActive={setInputActive}
productId="cadd"
/>
<BatchOperatorFlow
tasks={operatorList}
setTasks={setOperatorList}
type="edit"
// onFlowNodeClick={handleNodeClick}
flowNodeDraggable={true}
ListenState={!inputActive}
showVersion={true}
showControls={false}
/>
</div>
)}
</div>
{batchBuildType === "ENVIRONMENT" ? (
<div className={style.code}>
<Code
value={command}
onChange={(e: string) => setCommand(e)}
height="350px"
/>
</div>
) : (
<div
id="customOperatorFlow"
style={{ position: "relative", height: 400 }}
>
<OperatorList
operatorList={operatorList}
setOperatorList={setOperatorList}
setInputActive={setInputActive}
productId={formData.productId || defaultProduct || ""}
/>
<BatchOperatorFlow
tasks={operatorList}
setTasks={setOperatorList}
type="edit"
// onFlowNodeClick={handleNodeClick}
flowNodeDraggable={true}
ListenState={!inputActive}
showVersion={true}
showControls={false}
/>
</div>
)}
</div>
)}
<div className={style.buttonBox}>
<MyButton text="开始构建" onClick={handleSubmit} />
</div>
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-20 17:36:14
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-24 11:00:49
* @LastEditTime: 2022-10-24 15:52:56
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/components/AddOperator/utils.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -10,7 +10,9 @@
import { IOperatorAddFormData } from "../../interface";
export const checkFormData = (formData: IOperatorAddFormData, taskType: 'BATCH' | 'FLOW')=>{
type IBuildType = "ENVIRONMENT" | "OPERATOR";
export const checkFormData = (formData: IOperatorAddFormData, taskType: IBuildType)=>{
const reg = new RegExp("^[A-Za-z0-9\u4e00-\u9fa5]{1,15}$");
const result:IOperatorAddFormData = {}
......@@ -30,7 +32,7 @@ export const checkFormData = (formData: IOperatorAddFormData, taskType: 'BATCH'
if(!formData?.productId){
result.productId = '请选择所属产品'
}
if(taskType ==='BATCH' && !formData?.envId){
if(taskType ==='ENVIRONMENT' && !formData?.envId){
result.envId = '请选择应用环境'
}
......@@ -96,4 +98,51 @@ export const initCode = [{
"message" : "请输入PDB或PDBQT文件"
}
]
}]
\ No newline at end of file
}]
export const text = `{
// 参数名, 必填,在15字符以内,仅限大小写字母、数字、"_",且只能以大小写字母开头
"name" : "timeout",
/** 参数类型。可选值有 STRING:字符串、FILE:文件、DATASET:数据集、INT:整型、FLOAT:单精度浮点型、
* DOUBLE:多精度浮点型、BOOLEAN:布尔值、ARRAY_STRING:字符串数组、ARRAY_FILE:文件数组、ARRAY_DATASET:数据集数组、
* ARRAY_INT:整型数组、ARRAY_FLOAT:单精度浮点型数组、ARRAY_DOUBLE:多精度浮点型数组、ARRAY_BOOLEAN:布尔值数组
*/
"classType" : "INT",
// 是否必填。在使用该算子时是否必须输入改参数的值
"required" : false,
// 默认值
"defaultValue" : 10000,
// 参数描述。在300字符以内
"description" : "",
// 是否隐藏,隐藏就在页面不显示该参数 必填
"hidden" : true,
// 页面展示的参数的名称
"title" : "",
// 参数展示的顺序优先级
"order" : 0,
/**
* 前端填值的方式 PATH:路径选择器、DATASET:数据集选择器、FILE:文件选择器、INPUT:输入框、
* SELECT:下拉框、MULTIPLESELECT:多选下拉框、RADIO:单选按钮、CHECKBOX:多选按钮
*/
"domType" : "INPUT",
// 选项。当domType为SELECT、MULTIPLESELECT、RADIO、CHECKBOX时生效。以对象数组的形式保存
"choices" : [
{
// 在前端展示的值
"label" : "是",
//选中时传递服务使用的值
"value" : "true"
},
],
//用于校验输入值。以对象数组的形式保存
"validators" : [
{
//正则表达式
"regex" : "^.*\\.(pdb|PDB|pdbqt|PDBQT)$",
//不符合正则时的报错信息
"message" : "请输入PDB或PDBQT文件"
}
]
}`
\ No newline at end of file
......@@ -3,6 +3,7 @@
height: 108px;
padding: 20px;
margin: 0 20px 20px 0;
cursor: pointer;
background: linear-gradient(180deg, #f5f7fa 0%, #ffffff 100%);
box-shadow: 0px 3px 12px 0px rgba(3, 47, 105, 0.09);
border-radius: 6px;
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-20 11:39:55
* @LastEditTime: 2022-10-24 20:38:16
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -13,16 +13,20 @@ import { observer } from "mobx-react";
import { useStores } from "@/store";
import { useMemo } from "react";
import MyMenu from "@/components/mui/MyMenu";
import { useNavigate } from "react-router-dom";
interface IProps {
operatorInfo: IOperatorInfo;
setPageType: (val: string) => void;
setDetailsId: (val: string) => void;
}
const OperatorCard = observer((props: IProps) => {
const navigate = useNavigate();
const {
operatorInfo: { title = "", type, version, productId, createTime },
operatorInfo: { title = "", type, version, productId, createdTime, id },
setPageType,
setDetailsId,
} = props;
/** 产品store */
const { productListStore } = useStores();
......@@ -37,11 +41,18 @@ const OperatorCard = observer((props: IProps) => {
const onClickOperation = (val: string) => {
if (val === "upgrade") {
setPageType("edit");
setDetailsId(id || "");
}
};
const handleToSeeOperator = (item: any) => {
navigate("/utility/resourceCenter/userResources/seeFloe", {
state: { id: item.id },
});
};
return (
<div className={styles.itemBox}>
<div className={styles.itemBox} onClick={handleToSeeOperator}>
<div className={styles.itemHeaderBox}>
<div className={styles.startTitleBox}>{title?.slice(0, 1)}</div>
<div style={{ flex: 1, marginLeft: 12 }}>
......@@ -59,7 +70,7 @@ const OperatorCard = observer((props: IProps) => {
zIndex: 1601,
}}
>
<span className={styles.operationBox}>d</span>
<span className={styles.operationBox}>大大</span>
</MyMenu>
</div>
<div className={styles.itemContentBox}>
......@@ -70,7 +81,7 @@ const OperatorCard = observer((props: IProps) => {
算子版本:<span>{`V${version}`}</span>
</p>
<p className={styles.infoBox}>
创建时间:<span>{createTime}</span>
创建时间:<span>{createdTime}</span>
</p>
</div>
</div>
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-20 17:11:22
* @LastEditTime: 2022-10-24 20:40:16
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -35,6 +35,8 @@ const WorkflowOperator = observer(() => {
type: "all",
});
const [detailsId, setDetailsId] = useState<string>("");
const [list, setList] = useState<IOperatorInfo[]>();
const { run: getList } = useMyRequest(getOperatorList, {
......@@ -121,20 +123,27 @@ const WorkflowOperator = observer(() => {
<MyButton
text="构建算子"
startIcon={<Add />}
onClick={() => setPageType("add")}
onClick={() => {
setPageType("add");
setDetailsId("");
}}
></MyButton>
</div>
<div className={styles.contentBox}>
{list?.map((item) => {
return (
<OperatorCard operatorInfo={item} setPageType={setPageType} />
<OperatorCard
setDetailsId={setDetailsId}
operatorInfo={item}
setPageType={setPageType}
/>
);
})}
</div>
</div>
{pageType && (
<AddOperator
productId="cadd"
detailsId={detailsId}
setPageType={setPageType}
pageType={pageType}
/>
......
......@@ -2,18 +2,19 @@
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-19 20:50:18
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-24 11:29:18
* @LastEditTime: 2022-10-24 20:38:09
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/interface.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export type IOperatorType = 'BATCH' | 'FLOW'
export interface IOperatorInfo{
id: string;
title: string;
type: IOperatorType;
version: string;
productId: string;
createTime: string;
createdTime: string;
}
export enum operatorType {
......
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-18 09:32:40
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-24 14:57:06
* @FilePath: /bkunyun/src/views/ResourceCenter/components/SwitchBatchFolw/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import batchImg from "@/assets/resourceCenter/batchImg.svg";
import flowImg from "@/assets/resourceCenter/flowImg.svg";
import classNames from "classnames";
import goback from "@/assets/project/goback.svg";
import style from "./index.module.css";
type ISwitchBatchFolwProps = {
topImg?: string;
bottomImg?: string;
active: "BATCH" | "FLOW";
setActive: any;
goBack: any;
};
const SwitchBatchFolw = (props: ISwitchBatchFolwProps) => {
const { active, setActive, goBack } = props;
const { active, setActive, goBack, topImg, bottomImg } = props;
return (
<div className={style.switchBatchFolw}>
<div className={style.goBackBox}>
......@@ -36,7 +47,7 @@ const SwitchBatchFolw = (props: ISwitchBatchFolwProps) => {
[style.itemImg]: true,
[style.activeImg]: active === "BATCH",
})}
src={batchImg}
src={topImg || batchImg}
alt=""
/>
{active === "BATCH" && <div className={style.arrow}></div>}
......@@ -53,7 +64,7 @@ const SwitchBatchFolw = (props: ISwitchBatchFolwProps) => {
[style.itemImg]: true,
[style.activeImg]: active === "FLOW",
})}
src={flowImg}
src={bottomImg || flowImg}
alt=""
/>
{active === "FLOW" && <div className={style.arrow}></div>}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment