Commit fcf4968a authored by chenshouchao's avatar chenshouchao

Merge branch 'feat-20220705-customTemplate' into 'staging'

cn-Feat 20220705 custom template

See merge request !85
parents f1d577f4 b9a6c048
.RadiosBox {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #e6e8eb;
border-radius: 4px;
background-color: #e6e8eb;
cursor: pointer;
height: 32px;
box-sizing: border-box;
padding: 2px;
}
.radio {
height: 28px;
box-sizing: border-box;
font-size: 14px;
color: #565c66;
border-radius: 4px;
line-height: 20px;
padding: 3px 18px;
background-color: #e6e8eb;
display: flex;
align-items: center;
}
.radioActive {
color: #1370ff;
background-color: #fff;
border: 1px solid #e6e8eb;
}
// 按钮样式的单选组
import classnames from "classnames";
import style from "./index.module.css";
type radioOption = {
value: string;
label: string;
};
type IRadioGroupOfButtonStyleProps = {
radioOptions: Array<radioOption>;
value: string;
handleRadio: any;
};
const RadioGroupOfButtonStyle = (props: IRadioGroupOfButtonStyleProps) => {
const { radioOptions, value, handleRadio } = props;
return (
<div className={style.RadiosBox}>
{radioOptions.map((options) => {
return (
<div
key={options.value}
className={classnames({
[style.radio]: true,
[style.radioActive]: value === options.value,
})}
onClick={() => handleRadio(options.value)}
>
{options.label}
</div>
);
})}
</div>
);
};
export default RadioGroupOfButtonStyle;
...@@ -34,7 +34,9 @@ const MoveFile = (props: any) => { ...@@ -34,7 +34,9 @@ const MoveFile = (props: any) => {
const [moveFileSubmitloading, setMoveFileSubmitloading] = useState(false); const [moveFileSubmitloading, setMoveFileSubmitloading] = useState(false);
const [treeData, setTreeData] = useState<any>([]); const [treeData, setTreeData] = useState<any>([]);
const [renderTreeData, setRenderTreeData] = useState<any>([]); const [renderTreeData, setRenderTreeData] = useState<any>([]);
let moveFileDialogRef: any = React.createRef(); const [moveFileDialogRef, setMoveFileDialogRef] = useState<any>(
React.createRef()
);
// 要移动的文件夹 之后用来隐藏文件夹树中同路径的文件夹 // 要移动的文件夹 之后用来隐藏文件夹树中同路径的文件夹
const [moveFolderPathArr, setMoveFolderPathArr] = useState<Array<string>>([]); const [moveFolderPathArr, setMoveFolderPathArr] = useState<Array<string>>([]);
......
...@@ -99,18 +99,20 @@ ...@@ -99,18 +99,20 @@
color: rgba(138, 144, 153, 1); color: rgba(138, 144, 153, 1);
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
width: 72px;
margin-right: 44px;
} }
.taskInfoValue { .taskInfoValue {
color: rgba(30, 38, 51, 1); color: rgba(30, 38, 51, 1);
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
max-width: 210px;
text-overflow: ellipsis;
white-space: nowrap;
display: flex; display: flex;
overflow: hidden;
position: relative; position: relative;
align-items: center; align-items: center;
text-align: left;
word-break: break-all;
flex: 1;
justify-content: flex-end;
} }
.taskInfoValueClick { .taskInfoValueClick {
cursor: pointer; cursor: pointer;
...@@ -159,6 +161,7 @@ ...@@ -159,6 +161,7 @@
background: #ffffff; background: #ffffff;
box-shadow: 0px 3px 10px 0px rgba(0, 24, 57, 0.14); box-shadow: 0px 3px 10px 0px rgba(0, 24, 57, 0.14);
border-radius: 4px; border-radius: 4px;
z-index: 1002;
} }
.option { .option {
padding: 7px 16px; padding: 7px 16px;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-21 20:03:56 * @Date: 2022-06-21 20:03:56
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-06 11:01:44 * @LastEditTime: 2022-07-06 17:05:13
* @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -39,44 +39,44 @@ import { storageUnitFromB } from "@/utils/util"; ...@@ -39,44 +39,44 @@ import { storageUnitFromB } from "@/utils/util";
import styles from "./index.module.css"; import styles from "./index.module.css";
const stateMap = { const stateMap = {
RUNNING: "正在运行", RUNNING: "正在运行",
ABORTED: "运行终止", ABORTED: "运行终止",
FAILED: "运行失败", FAILED: "运行失败",
SUCCEEDED: "运行成功", SUCCEEDED: "运行成功",
}; };
const statusMap = { const statusMap = {
Done: "运行完成", Done: "运行完成",
Running: "正在运行", Running: "正在运行",
Failed: "运行失败", Failed: "运行失败",
Pending: "等待运行", Pending: "等待运行",
}; };
type IStatus = "Done" | "Running" | "Failed" | "Pending"; type IStatus = "Done" | "Running" | "Failed" | "Pending";
let randerOutputs: Array<any> = []; let randerOutputs: Array<any> = [];
const ProjectSubmitWork = observer(() => { const ProjectSubmitWork = observer(() => {
const { currentProjectStore } = useStores(); const { currentProjectStore } = useStores();
const fileToken = toJS(currentProjectStore.currentProjectInfo.filetoken); const fileToken = toJS(currentProjectStore.currentProjectInfo.filetoken);
const projectId = toJS(currentProjectStore.currentProjectInfo.id); const projectId = toJS(currentProjectStore.currentProjectInfo.id);
const [workFlowJobInfo, setWorkFlowJobInfo] = useState<ITaskInfo>(); const [workFlowJobInfo, setWorkFlowJobInfo] = useState<ITaskInfo>();
const [patchInfo, setPatchInfo] = useState<any>(); const [patchInfo, setPatchInfo] = useState<any>();
const [activePatchId, setActivePatchId] = useState<string>(""); const [activePatchId, setActivePatchId] = useState<string>("");
const [overviewActive, setOverviewActive] = useState(true); const [overviewActive, setOverviewActive] = useState(true);
const [activeFlowIndex, setActiveFlowIndex] = useState<number>(0); const [activeFlowIndex, setActiveFlowIndex] = useState<number>(0);
const [showOptions, setShowOptions] = useState<boolean>(false); const [showOptions, setShowOptions] = useState<boolean>(false);
const [randerOutputs1, setRanderOutputs] = useState<Array<any>>([]); const [randerOutputs1, setRanderOutputs] = useState<Array<any>>([]);
const location: any = useLocation(); const location: any = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
const message = useMessage(); const message = useMessage();
const [fullScreenShow, setFullScreenShow] = useState<boolean>(false); const [fullScreenShow, setFullScreenShow] = useState<boolean>(false);
const { name, state } = workFlowJobInfo || {}; const { name, state } = workFlowJobInfo || {};
/** 获取模版数据 */ /** 获取模版数据 */
const { run } = useMyRequest(fetchWorkFlowJob, { const { run } = useMyRequest(fetchWorkFlowJob, {
pollingInterval:1000 * 60, pollingInterval: 1000 * 60,
pollingWhenHidden:false, pollingWhenHidden: false,
onSuccess: (res: IResponse<ITaskInfo>) => { onSuccess: (res: IResponse<ITaskInfo>) => {
getOutouts(res.data.outputs); getOutouts(res.data.outputs);
setWorkFlowJobInfo(res.data); setWorkFlowJobInfo(res.data);
...@@ -85,17 +85,16 @@ const ProjectSubmitWork = observer(() => { ...@@ -85,17 +85,16 @@ const ProjectSubmitWork = observer(() => {
useEffect(() => { useEffect(() => {
const locationInfo: any = location?.state; const locationInfo: any = location?.state;
console.log(333)
run({ run({
id: locationInfo.taskId, id: locationInfo.taskId,
}); });
}, [location?.state, run]); }, [location?.state, run]);
const { run: getworkFlowTaskInfoRun } = useMyRequest(getworkFlowTaskInfo, { const { run: getworkFlowTaskInfoRun } = useMyRequest(getworkFlowTaskInfo, {
onSuccess: (res) => { onSuccess: (res) => {
setPatchInfo(res.data); setPatchInfo(res.data);
}, },
}); });
const goToProjectData = (path: string) => { const goToProjectData = (path: string) => {
path = path.slice(13); path = path.slice(13);
...@@ -103,496 +102,513 @@ const ProjectSubmitWork = observer(() => { ...@@ -103,496 +102,513 @@ const ProjectSubmitWork = observer(() => {
navigate(`/product/cadd/projectData`, { navigate(`/product/cadd/projectData`, {
state: { pathName: path }, state: { pathName: path },
}); });
}
};
/** 返回事件 */
const onBack = useCallback(() => {
navigate('/product/cadd/projectWorkbench', {
state: {type: 'workbenchList'}
})
},[navigate])
const outputPathTransform = (path: string) => {
path = path.slice(13);
return `ProjectData${path}`;
};
const getOutouts = (outputs: any) => {
if (outputs) {
let result = Object.keys(outputs);
let arr = result.map((item) => {
let type = "file";
if (outputs[item].indexOf("dataset") !== -1) {
type = "dataset";
}
return {
name: item,
type,
path: outputs[item],
size: 0,
};
});
arr.forEach(async (item, index) => {
if (item.type === "dataset") {
await getDataSetSize(item, index);
} else {
await getFileSize(item, index);
}
});
randerOutputs = arr;
setRanderOutputs([...randerOutputs]);
} else { } else {
randerOutputs = []; navigate(`/product/cadd/projectData`, {
setRanderOutputs([]); state: { pathName: "/" },
});
} }
}; };
const getDataSetSize = async (item: any, index: number) => { // 通过文件路径获取文件所在文件夹路径 如 输入 /home/cloudam/task_a.out 输出/home/cloudam/
let path = item.path.slice(13); const getFolderPath = (path: string) => {
const lastIndex = path.lastIndexOf("/"); const lastIndex = path.lastIndexOf("/");
if (lastIndex === -1) { if (lastIndex !== -1) {
path = "/";
} else {
path = path.slice(0, lastIndex + 1); path = path.slice(0, lastIndex + 1);
} }
const res = await getDataFind({ return path;
projectId: projectId as string,
path: path,
});
res.data.forEach((item1: any) => {
if (item1.name === item.path.slice(item.path.lastIndexOf("/") + 1)) {
randerOutputs[index].size = `${item1.size}条`;
setRanderOutputs([...randerOutputs]);
}
});
}; };
const getFileSize = (item: any, index: number) => { /** 返回事件 */
let path = item.path.slice(13); const onBack = useCallback(() => {
const lastIndex = path.lastIndexOf("/"); navigate("/product/cadd/projectWorkbench", {
if (lastIndex === -1) { state: { type: "workbenchList" },
path = "/";
} else {
path = path.slice(0, lastIndex + 1);
}
CloudEController.JobOutFileList(
path,
fileToken as string,
projectId as string,
false
)?.then((res) => {
if (Array.isArray(res.data)) {
res.data.forEach((item1) => {
if (item1.name === item.path.slice(item.path.lastIndexOf("/") + 1)) {
randerOutputs[index].size = `${
item1.size ? storageUnitFromB(Number(item1.size)) : "-"
}`;
setRanderOutputs([...randerOutputs]);
}
});
}
}); });
}; }, [navigate]);
// 取消作业 const outputPathTransform = (path: string) => {
const { run: cancelWorkJob } = useMyRequest(cancelWorkflowJob, { path = path.slice(13);
onSuccess: (res: IResponse<boolean>) => { return `ProjectData${path}`;
const { errorCode } = res; };
if (errorCode === 0) {
message.success("操作成功!");
}
onBack()
},
});
// 取消作业 const getOutouts = (outputs: any) => {
const { run: deleteWorkJob } = useMyRequest(deleteWorkflowJob, { if (outputs) {
onSuccess: (res: IResponse<boolean>) => { let result = Object.keys(outputs);
const { errorCode } = res; let arr = result.map((item) => {
if (errorCode === 0) { let type = "file";
message.success("操作成功!"); if (outputs[item].indexOf("dataset") !== -1) {
} type = "dataset";
onBack() }
}, return {
}); name: item,
type,
path: outputs[item],
size: 0,
};
});
arr.forEach(async (item, index) => {
if (item.type === "dataset") {
await getDataSetSize(item, index);
} else {
await getFileSize(item, index);
}
});
randerOutputs = arr;
setRanderOutputs([...randerOutputs]);
} else {
randerOutputs = [];
setRanderOutputs([]);
}
};
const handleBatch = (id: string) => { const getDataSetSize = async (item: any, index: number) => {
setActivePatchId(id); let path = item.path.slice(13);
if (id) { const lastIndex = path.lastIndexOf("/");
setActiveFlowIndex(0); if (lastIndex === -1) {
getworkFlowTaskInfoRun({ path = "/";
jobId: workFlowJobInfo?.id as string, } else {
taskId: id, path = path.slice(0, lastIndex + 1);
}); }
} const res = await getDataFind({
}; projectId: projectId as string,
path: path,
});
res.data.forEach((item1: any) => {
if (item1.name === item.path.slice(item.path.lastIndexOf("/") + 1)) {
randerOutputs[index].size = `${item1.size}条`;
setRanderOutputs([...randerOutputs]);
}
});
};
const randerParameters = useMemo(() => { const getFileSize = (item: any, index: number) => {
if (patchInfo?.children) { let path = item.path.slice(13);
if (patchInfo.children.length > 0) { const lastIndex = path.lastIndexOf("/");
return patchInfo.children[activeFlowIndex].parameters; if (lastIndex === -1) {
} else { path = "/";
return patchInfo?.parameters; } else {
} path = path.slice(0, lastIndex + 1);
} else { }
return patchInfo?.parameters; CloudEController.JobOutFileList(
} path,
}, [activeFlowIndex, patchInfo]); fileToken as string,
projectId as string,
false
)?.then((res) => {
if (Array.isArray(res.data)) {
res.data.forEach((item1) => {
if (item1.name === item.path.slice(item.path.lastIndexOf("/") + 1)) {
randerOutputs[index].size = `${
item1.size ? storageUnitFromB(Number(item1.size)) : "-"
}`;
setRanderOutputs([...randerOutputs]);
}
});
}
});
};
const handleParams = () => { // 取消作业
setOverviewActive(false); const { run: cancelWorkJob } = useMyRequest(cancelWorkflowJob, {
setShowOptions(!showOptions); onSuccess: (res: IResponse<boolean>) => {
}; const { errorCode } = res;
if (errorCode === 0) {
message.success("操作成功!");
}
onBack();
},
});
const handleDownLoad = (path: string) => { // 取消作业
if (path.indexOf("/home/cloudam") !== -1) { const { run: deleteWorkJob } = useMyRequest(deleteWorkflowJob, {
path = path.slice(13); onSuccess: (res: IResponse<boolean>) => {
} const { errorCode } = res;
CloudEController.JobFileDownload( if (errorCode === 0) {
path, message.success("操作成功!");
fileToken as string, }
projectId as string onBack();
); },
}; });
/** 终止任务 */ const handleBatch = (id: string) => {
const onStopJob = useCallback(() => { setActivePatchId(id);
cancelWorkJob({ if (id) {
jobid: workFlowJobInfo?.id || "", setActiveFlowIndex(0);
}); getworkFlowTaskInfoRun({
}, [cancelWorkJob, workFlowJobInfo?.id]); jobId: workFlowJobInfo?.id as string,
taskId: id,
});
}
};
/** 删除任务 */ const randerParameters = useMemo(() => {
const onDeleteJob = useCallback(() => { if (patchInfo?.children) {
deleteWorkJob({ if (patchInfo.children.length > 0) {
id: workFlowJobInfo?.id || "", return patchInfo.children[activeFlowIndex].parameters;
}); } else {
}, [deleteWorkJob, workFlowJobInfo?.id]); return patchInfo?.parameters;
}
} else {
return patchInfo?.parameters;
}
}, [activeFlowIndex, patchInfo]);
return ( const handleParams = () => {
<div className={styles.swBox}> setOverviewActive(false);
{fullScreenShow ? null : ( setShowOptions(!showOptions);
<div className={styles.swHeader}> };
<div className={styles.swHeaderLeft}>
<IconButton
color="primary"
onClick={onBack}
aria-label="upload picture"
component="span"
size="small"
>
<ArrowBackIosNewIcon
sx={{
color: "rgba(194, 198, 204, 1)",
width: "12px",
height: "12px",
}}
/>
</IconButton>
<div className={styles.swTemplateTitle}>{name}</div> const handleDownLoad = (path: string) => {
</div> if (path.indexOf("/home/cloudam") !== -1) {
<div className={styles.swHeaderRight}> path = path.slice(13);
<MyPopconfirm }
title={ CloudEController.JobFileDownload(
state === "RUNNING" path,
? "正在运行的任务终止后将无法重新运行,确认继续吗?" fileToken as string,
: "任务被删除后将无法恢复,确认继续吗?" projectId as string
} );
onConfirm={() => { };
state === "RUNNING" ? onStopJob() : onDeleteJob();
}} /** 终止任务 */
> const onStopJob = useCallback(() => {
<ButtonComponent cancelWorkJob({
text={state === "RUNNING" ? "终止" : "删除"} jobid: workFlowJobInfo?.id || "",
variant="outlined" });
color="secondary" }, [cancelWorkJob, workFlowJobInfo?.id]);
// click={onStopJob}
></ButtonComponent> /** 删除任务 */
</MyPopconfirm> const onDeleteJob = useCallback(() => {
</div> deleteWorkJob({
</div> id: workFlowJobInfo?.id || "",
)} });
<div className={styles.swContent}> }, [deleteWorkJob, workFlowJobInfo?.id]);
{fullScreenShow ? null : (
<div className={styles.swFormBox}> return (
{!activePatchId && ( <div className={styles.swBox}>
<div className={styles.taskInfo}> {fullScreenShow ? null : (
<div className={styles.title}>任务结果</div> <div className={styles.swHeader}>
{workFlowJobInfo?.outputs && ( <div className={styles.swHeaderLeft}>
<div className={styles.taskResults}> <IconButton
{randerOutputs1.map((item, index) => { color="primary"
return ( onClick={onBack}
<div key={index} className={styles.outputLi}> aria-label="upload picture"
<MyPopconfirm component="span"
title="即将跳转至项目数据内该任务的结果目录,确认继续吗?" size="small"
onConfirm={() => goToProjectData(item.path)} >
> <ArrowBackIosNewIcon
<div className={styles.outputLiLeft}> sx={{
<img color: "rgba(194, 198, 204, 1)",
className={styles.outputLiLeftImg} width: "12px",
src={ height: "12px",
item.type === "file" ? fileIcon : dataSetIcon }}
} />
alt="" </IconButton>
/>
{item.name} <div className={styles.swTemplateTitle}>{name}</div>
</div> </div>
</MyPopconfirm> <div className={styles.swHeaderRight}>
<span className={styles.outputLiRight}> <MyPopconfirm
{item.size} title={
</span> state === "RUNNING"
</div> ? "正在运行的任务终止后将无法重新运行,确认继续吗?"
); : "任务被删除后将无法恢复,确认继续吗?"
})} }
</div> onConfirm={() => {
)} state === "RUNNING" ? onStopJob() : onDeleteJob();
{!workFlowJobInfo?.outputs && ( }}
<div className={styles.notResults}>暂无结果文件</div> >
)} <ButtonComponent
<div className={styles.title}>任务信息</div> text={state === "RUNNING" ? "终止" : "删除"}
<div className={styles.taskInfoLi}> variant="outlined"
<div className={styles.taskInfoParams}>任务名称</div> color="secondary"
<div className={styles.taskInfoValue} title={name}> // click={onStopJob}
{name || "-"} ></ButtonComponent>
</div> </MyPopconfirm>
</div> </div>
<div className={styles.taskInfoLi}> </div>
<div className={styles.taskInfoParams}>任务ID</div> )}
<div <div className={styles.swContent}>
className={styles.taskInfoValue} {fullScreenShow ? null : (
title={workFlowJobInfo?.id} <div className={styles.swFormBox}>
> {!activePatchId && (
{workFlowJobInfo?.id || "-"} <div className={styles.taskInfo}>
</div> <div className={styles.title}>任务结果</div>
</div> {workFlowJobInfo?.outputs && (
<div className={styles.taskInfoLi}> <div className={styles.taskResults}>
<div className={styles.taskInfoParams}>输出路径</div> {randerOutputs1.map((item, index) => {
<div return (
className={classNames({ <div key={index} className={styles.outputLi}>
[styles.taskInfoValue]: true, <MyPopconfirm
[styles.taskInfoValueClick]: true, title="即将跳转至项目数据内该任务的结果目录,确认继续吗?"
})} onConfirm={() => goToProjectData(getFolderPath(item.path))}
onClick={() => >
goToProjectData(workFlowJobInfo?.outputPath as string) <div className={styles.outputLiLeft}>
} <img
> className={styles.outputLiLeftImg}
{workFlowJobInfo?.outputPath src={
? outputPathTransform(workFlowJobInfo?.outputPath) item.type === "file" ? fileIcon : dataSetIcon
: "-"} }
</div> alt=""
</div> />
<div className={styles.taskInfoLi}> {item.name}
<div className={styles.taskInfoParams}>运行状态</div> </div>
<div className={styles.taskInfoValue}> </MyPopconfirm>
{state === "SUCCEEDED" && ( <span className={styles.outputLiRight}>
<img {item.size}
className={styles.taskInfoValueIcon} </span>
src={jobSue} </div>
alt="" );
/> })}
)} </div>
{state === "RUNNING" && ( )}
<img {!workFlowJobInfo?.outputs && (
className={styles.taskInfoValueIcon} <div className={styles.notResults}>暂无结果文件</div>
src={jobRun} )}
alt="" <div className={styles.title}>任务信息</div>
/> <div className={styles.taskInfoLi}>
)} <div className={styles.taskInfoParams}>任务名称</div>
{state === "ABORTED" && ( <div className={styles.taskInfoValue} title={name}>
<img {name || "-"}
className={styles.taskInfoValueIcon} </div>
src={jobStop} </div>
alt="" <div className={styles.taskInfoLi}>
/> <div className={styles.taskInfoParams}>任务ID</div>
)} <div
{state === "FAILED" && ( className={styles.taskInfoValue}
<img title={workFlowJobInfo?.id}
className={styles.taskInfoValueIcon} >
src={jobFail} {workFlowJobInfo?.id || "-"}
alt="" </div>
/> </div>
)} <div className={styles.taskInfoLi}>
{state ? stateMap[state] : "-"} <div className={styles.taskInfoParams}>输出路径</div>
</div> <div
</div> className={classNames({
<div className={styles.taskInfoLi}> [styles.taskInfoValue]: true,
<div className={styles.taskInfoParams}>源模板</div> [styles.taskInfoValueClick]: true,
<div className={styles.taskInfoValue}> })}
{workFlowJobInfo?.specTitle || "-"} onClick={() =>
</div> goToProjectData(workFlowJobInfo?.outputPath as string)
</div> }
<div className={styles.taskInfoLi}> >
<div className={styles.taskInfoParams}>源模板版本</div> {workFlowJobInfo?.outputPath
<div className={styles.taskInfoValue}> ? outputPathTransform(workFlowJobInfo?.outputPath)
{workFlowJobInfo?.specVersion || "-"} : "-"}
</div> </div>
</div> </div>
<div className={styles.taskInfoLi}> <div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>花费(元)</div> <div className={styles.taskInfoParams}>运行状态</div>
<div className={styles.taskInfoValue}> <div className={styles.taskInfoValue}>
{workFlowJobInfo?.jobCost || "-"} {state === "SUCCEEDED" && (
</div> <img
</div> className={styles.taskInfoValueIcon}
<div className={styles.taskInfoLi}> src={jobSue}
<div className={styles.taskInfoParams}>创建人</div> alt=""
<div className={styles.taskInfoValue}> />
{workFlowJobInfo?.creator || "-"} )}
</div> {state === "RUNNING" && (
</div> <img
<div className={styles.taskInfoLi}> className={styles.taskInfoValueIcon}
<div className={styles.taskInfoParams}>创建时间</div> src={jobRun}
<div className={styles.taskInfoValue}> alt=""
{workFlowJobInfo?.createTime || "-"} />
</div> )}
</div> {state === "ABORTED" && (
<div className={styles.taskInfoLi}> <img
<div className={styles.taskInfoParams}>运行时间</div> className={styles.taskInfoValueIcon}
<div className={styles.taskInfoValue}> src={jobStop}
{workFlowJobInfo?.costTime || "-"} alt=""
</div> />
</div> )}
<div className={styles.taskInfoLi}> {state === "FAILED" && (
<div className={styles.taskInfoParams}>日志文件</div> <img
<div className={styles.taskInfoValue}> className={styles.taskInfoValueIcon}
{workFlowJobInfo?.logPath && ( src={jobFail}
<span alt=""
className={styles.taskInfoDownload} />
onClick={() => handleDownLoad(workFlowJobInfo?.logPath)} )}
> {state ? stateMap[state] : "-"}
下载 </div>
</span> </div>
)} <div className={styles.taskInfoLi}>
{!workFlowJobInfo?.logPath && "-"} <div className={styles.taskInfoParams}>源模板</div>
</div> <div className={styles.taskInfoValue}>
</div> {workFlowJobInfo?.specTitle || "-"}
</div> </div>
)} </div>
{activePatchId && ( <div className={styles.taskInfoLi}>
<div className={styles.suanziInfo}> <div className={styles.taskInfoParams}>源模板版本</div>
<div className={styles.title}>{patchInfo?.title}</div> <div className={styles.taskInfoValue}>
<div className={styles.tabs}> {workFlowJobInfo?.specVersion || "-"}
<div </div>
className={classNames({ </div>
[styles.tabLi]: true, <div className={styles.taskInfoLi}>
[styles.tabLiAcitve]: overviewActive, <div className={styles.taskInfoParams}>花费(元)</div>
})} <div className={styles.taskInfoValue}>
onClick={() => setOverviewActive(true)} {workFlowJobInfo?.jobCost || "-"}
> </div>
概览 </div>
</div> <div className={styles.taskInfoLi}>
<div <div className={styles.taskInfoParams}>创建人</div>
className={classNames({ <div className={styles.taskInfoValue}>
[styles.tabLi]: true, {workFlowJobInfo?.creator || "-"}
[styles.tabLiAcitve]: !overviewActive, </div>
})} </div>
// onClick={() => setOverviewActive(false)} <div className={styles.taskInfoLi}>
onClick={() => handleParams()} <div className={styles.taskInfoParams}>创建时间</div>
> <div className={styles.taskInfoValue}>
{patchInfo?.children.length > 0 {workFlowJobInfo?.createTime || "-"}
? patchInfo?.children[activeFlowIndex].title </div>
: patchInfo?.title} </div>
{showOptions && patchInfo?.children.length > 0 && ( <div className={styles.taskInfoLi}>
<div className={styles.options}> <div className={styles.taskInfoParams}>运行时间</div>
{patchInfo?.children.map((item: any, index: number) => { <div className={styles.taskInfoValue}>
return ( {workFlowJobInfo?.costTime || "-"}
<div </div>
</div>
<div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>日志文件</div>
<div className={styles.taskInfoValue}>
{workFlowJobInfo?.logPath && (
<span
className={styles.taskInfoDownload}
onClick={() => handleDownLoad(workFlowJobInfo?.logPath)}
>
下载
</span>
)}
{!workFlowJobInfo?.logPath && "-"}
</div>
</div>
</div>
)}
{activePatchId && (
<div className={styles.suanziInfo}>
<div className={styles.title}>{patchInfo?.title}</div>
<div className={styles.tabs}>
<div
className={classNames({
[styles.tabLi]: true,
[styles.tabLiAcitve]: overviewActive,
})}
onClick={() => setOverviewActive(true)}
>
概览
</div>
<div
className={classNames({
[styles.tabLi]: true,
[styles.tabLiAcitve]: !overviewActive,
})}
// onClick={() => setOverviewActive(false)}
onClick={() => handleParams()}
>
{patchInfo?.children.length > 0
? patchInfo?.children[activeFlowIndex].title
: patchInfo?.title}
{showOptions && patchInfo?.children.length > 0 && (
<div className={styles.options}>
{patchInfo?.children.map((item: any, index: number) => {
return (
<div
key={index} key={index}
className={styles.option} className={classNames({
[styles.option]: true,
[styles.optionActive]:
activeFlowIndex === index,
})}
onClick={() => setActiveFlowIndex(index)} onClick={() => setActiveFlowIndex(index)}
> >
{item.title} {item.title}
</div> </div>
); );
})} })}
</div> </div>
)} )}
</div> </div>
</div> </div>
{overviewActive && ( {overviewActive && (
<div className={styles.overview}> <div className={styles.overview}>
<div className={styles.taskInfoLi}> <div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>描述</div> <div className={styles.taskInfoParams}>描述</div>
<div <div
className={classNames({ className={classNames({
[styles.taskInfoValue]: true, [styles.taskInfoValue]: true,
[styles.taskInfoValueShowAll]: true, [styles.taskInfoValueShowAll]: true,
})} })}
> >
{patchInfo?.description} {patchInfo?.description}
</div> </div>
</div> </div>
<div className={styles.taskInfoLi}> <div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>算子版本</div> <div className={styles.taskInfoParams}>算子版本</div>
<div className={styles.taskInfoValue}> <div className={styles.taskInfoValue}>
{patchInfo?.creator || "-"} {patchInfo?.creator || "-"}
</div> </div>
</div> </div>
<div className={styles.taskInfoLi}> <div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>算子状态</div> <div className={styles.taskInfoParams}>算子状态</div>
<div className={styles.taskInfoValue}> <div className={styles.taskInfoValue}>
{patchInfo?.status === "Done" && ( {patchInfo?.status === "Done" && (
<img <img
className={styles.taskInfoValueIcon} className={styles.taskInfoValueIcon}
src={jobSue} src={jobSue}
alt="" alt=""
/> />
)} )}
{patchInfo?.status === "Running" && ( {patchInfo?.status === "Running" && (
<img <img
className={styles.taskInfoValueIcon} className={styles.taskInfoValueIcon}
src={jobRun} src={jobRun}
alt="" alt=""
/> />
)} )}
{patchInfo?.status === "Failed" && ( {patchInfo?.status === "Failed" && (
<img <img
className={styles.taskInfoValueIcon} className={styles.taskInfoValueIcon}
src={jobFail} src={jobFail}
alt="" alt=""
/> />
)} )}
{statusMap[patchInfo?.status as IStatus]} {statusMap[patchInfo?.status as IStatus]}
</div> </div>
</div> </div>
</div> </div>
)} )}
{!overviewActive && ( {!overviewActive && (
<div className={styles.params}> <div className={styles.params}>
{randerParameters.map((parameter: any) => { {randerParameters.map((parameter: any) => {
return ( return (
<div className={styles.taskInfoLi} key={parameter.name}> <div className={styles.taskInfoLi} key={parameter.name}>
<div className={styles.taskInfoParams}> <div className={styles.taskInfoParams}>
{parameter.name} {parameter.name}
</div> </div>
<div className={styles.taskInfoValue}> <div className={styles.taskInfoValue}>
{parameter.value || "-"} {parameter.value || "-"}
</div> </div>
</div> </div>
); );
})} })}
</div> </div>
)} )}
</div> </div>
)} )}
</div> </div>
)} )}
<div <div
className={styles.swFlowBox} className={styles.swFlowBox}
style={fullScreenShow ? { height: "100vh" } : undefined} style={fullScreenShow ? { height: "100vh" } : undefined}
> >
<Flow tasks={workFlowJobInfo?.tasks} onBatchClick={handleBatch} /> <Flow tasks={workFlowJobInfo?.tasks} onBatchClick={handleBatch} />
</div> </div>
</div> </div>
<img <img
className={styles.fullScreenBox} className={styles.fullScreenBox}
src={fullScreenShow ? partialScreen : fullScreen} src={fullScreenShow ? partialScreen : fullScreen}
onClick={() => setFullScreenShow(!fullScreenShow)} onClick={() => setFullScreenShow(!fullScreenShow)}
alt="全屏" alt="全屏"
/> />
</div> </div>
); );
}); });
export default ProjectSubmitWork; export default ProjectSubmitWork;
...@@ -30,7 +30,6 @@ import MyPopconfirm from "@/components/mui/MyPopconfirm"; ...@@ -30,7 +30,6 @@ import MyPopconfirm from "@/components/mui/MyPopconfirm";
import styles from "./index.module.css"; import styles from "./index.module.css";
const ProjectSubmitWork = () => { const ProjectSubmitWork = () => {
const Message = useMessage(); const Message = useMessage();
const { currentProjectStore } = useStores(); const { currentProjectStore } = useStores();
...@@ -68,7 +67,14 @@ const ProjectSubmitWork = () => { ...@@ -68,7 +67,14 @@ const ProjectSubmitWork = () => {
task.parameters.forEach((parameter) => { task.parameters.forEach((parameter) => {
let value: any = undefined; let value: any = undefined;
if (parameter.defaultValue) { if (parameter.defaultValue) {
value = parameter.defaultValue; if (
parameter.domType.toLowerCase() === "multipleselect" ||
parameter.domType.toLowerCase() === "checkbox"
) {
value = parameter.defaultValue.split(",");
} else {
value = parameter.defaultValue;
}
} else if ( } else if (
parameter.domType.toLowerCase() === "multipleselect" || parameter.domType.toLowerCase() === "multipleselect" ||
parameter.domType.toLowerCase() === "checkbox" parameter.domType.toLowerCase() === "checkbox"
...@@ -193,73 +199,84 @@ const ProjectSubmitWork = () => { ...@@ -193,73 +199,84 @@ const ProjectSubmitWork = () => {
return ( return (
<div className={styles.swBox}> <div className={styles.swBox}>
{ fullScreenShow ? null : <div className={styles.swHeader}> {fullScreenShow ? null : (
<div className={styles.swHeaderLeft}> <div className={styles.swHeader}>
<MyPopconfirm <div className={styles.swHeaderLeft}>
title="返回后,当前页面已填写内容将不保存,确认返回吗?" <MyPopconfirm
onConfirm={handleGoBack} title="返回后,当前页面已填写内容将不保存,确认返回吗?"
> onConfirm={handleGoBack}
<IconButton
color="primary"
// onClick={() => handleGoBack()}
aria-label="upload picture"
component="span"
size="small"
> >
<ArrowBackIosNewIcon <IconButton
sx={{ color="primary"
color: "rgba(194, 198, 204, 1)", // onClick={() => handleGoBack()}
width: "12px", aria-label="upload picture"
height: "12px", component="span"
}} size="small"
/> >
</IconButton> <ArrowBackIosNewIcon
</MyPopconfirm> sx={{
color: "rgba(194, 198, 204, 1)",
width: "12px",
height: "12px",
}}
/>
</IconButton>
</MyPopconfirm>
<div className={styles.swTemplateTitle}> <div className={styles.swTemplateTitle}>
{templateConfigInfo?.title} {templateConfigInfo?.title}
</div>
<div className={styles.swTemplateVersionBox}>
<span className={styles.swHeaderLable}>版本:</span>
<span className={styles.swHeaderValue}>
{templateConfigInfo?.languageVersion}
</span>
</div>
<div className={styles.swTemplateUpdateTimeBox}>
<span className={styles.swHeaderLable}>更新时间:</span>
<span className={styles.swHeaderValue}>
{templateConfigInfo?.updateTime
? moment(templateConfigInfo?.updateTime).format(
"YYYY-MM-DD HH:mm:ss"
)
: "-"}
</span>
</div>
<div className={styles.swHeaderGoback}></div>
</div> </div>
<div className={styles.swTemplateVersionBox}> <div className={styles.swHeaderRight}>
<span className={styles.swHeaderLable}>版本:</span> <MyPopconfirm
<span className={styles.swHeaderValue}> title="提交前请先确认参数填写无误,确认提交吗?"
{templateConfigInfo?.languageVersion} onConfirm={handleSubmitForm}
</span> >
</div> <ButtonComponent
<div className={styles.swTemplateUpdateTimeBox}> text="提交任务"
<span className={styles.swHeaderLable}>更新时间:</span> // click={handleSubmitForm}
<span className={styles.swHeaderValue}> ></ButtonComponent>
{templateConfigInfo?.updateTime </MyPopconfirm>
? moment(templateConfigInfo?.updateTime).format(
"YYYY-MM-DD HH:mm:ss"
)
: "-"}
</span>
</div> </div>
<div className={styles.swHeaderGoback}></div>
</div> </div>
<div className={styles.swHeaderRight}> )}
<MyPopconfirm
title="提交前请先确认参数填写无误,确认提交吗?"
onConfirm={handleSubmitForm}
>
<ButtonComponent
text="提交任务"
// click={handleSubmitForm}
></ButtonComponent>
</MyPopconfirm>
</div>
</div>}
<div className={styles.swContent}> <div className={styles.swContent}>
{fullScreenShow ? null : <div className={styles.swFormBox}> {fullScreenShow ? null : (
<ConfigForm <div className={styles.swFormBox}>
onRef={configFormRef} <ConfigForm
onRef={configFormRef}
templateConfigInfo={templateConfigInfo}
setParameter={setParameter}
setSelectedNodeId={setSelectedNodeId}
/>
</div>
)}
<div
className={styles.swFlowBox}
style={fullScreenShow ? { height: "100vh" } : undefined}
>
<WorkFlow
templateConfigInfo={templateConfigInfo} templateConfigInfo={templateConfigInfo}
setParameter={setParameter}
setSelectedNodeId={setSelectedNodeId} setSelectedNodeId={setSelectedNodeId}
selectedNodeId={selectedNodeId}
/> />
</div>}
<div className={styles.swFlowBox} style={fullScreenShow ? { height: "100vh" } : undefined}>
<WorkFlow templateConfigInfo={templateConfigInfo} setSelectedNodeId={setSelectedNodeId} selectedNodeId={selectedNodeId}/>
</div> </div>
</div> </div>
<img <img
......
import { memo, useCallback, useEffect, useMemo, useState } from "react"; import { memo, useCallback, useEffect, useMemo, useState } from "react";
import styles from "../index.module.css"; import styles from "../index.module.css";
import { Box, Typography } from "@mui/material"; import { Box, Typography } from "@mui/material";
import Button from "@/components/mui/Button"; import Button from "@/components/mui/Button";
import Dialog from "@/components/mui/Dialog"; import Dialog from "@/components/mui/Dialog";
import OutlinedInput from "@mui/material/OutlinedInput"; import OutlinedInput from "@mui/material/OutlinedInput";
import RadioGroupOfButtonStyle from "@/components/CommonComponents/RadioGroupOfButtonStyle";
import SearchIcon from "@mui/icons-material/Search"; import SearchIcon from "@mui/icons-material/Search";
import Checkbox from '@mui/material/Checkbox'; import Checkbox from "@mui/material/Checkbox";
import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined'; import CloseOutlinedIcon from "@mui/icons-material/CloseOutlined";
import noData from '../../../../../assets/project/noTemplate.svg' import noData from "../../../../../assets/project/noTemplate.svg";
import _ from "lodash"; import _ from "lodash";
const AddTemplate = (props: any) => { const AddTemplate = (props: any) => {
const { openAddTemplate, closeAddTemplateBlock, addTemplateList, templateSelectCallback, selectTemplateData, addTemplateCallback, searchTemplateNameCallback } = props; const {
openAddTemplate,
closeAddTemplateBlock,
addTemplateList,
templateSelectCallback,
selectTemplateData,
addTemplateCallback,
searchTemplateNameCallback,
} = props;
const [templateType, setTemplateType] = useState("public");
const radioOptions = [
{
value: "public",
label: "公共",
},
{
value: "custom",
label: "自定义",
},
];
const handleRadio = (value: string) => {
setTemplateType(value);
};
return ( return (
<Box className={styles.addTemplateMask} sx={{ display: openAddTemplate ? 'flex' : 'none' }} > <Box
<Box sx={{ height: '50px', display: 'flex', alignItems: 'center' }} > className={styles.addTemplateMask}
<CloseOutlinedIcon sx={{ color: "#ffffff", marginRight: "10px", cursor: "pointer" }} onClick={() => { sx={{ display: openAddTemplate ? "flex" : "none" }}
closeAddTemplateBlock() >
}} /> <Box sx={{ height: "50px", display: "flex", alignItems: "center" }}>
<CloseOutlinedIcon
sx={{ color: "#ffffff", marginRight: "10px", cursor: "pointer" }}
onClick={() => {
closeAddTemplateBlock();
}}
/>
</Box>
<Box className={styles.addTemplateBlock}>
<Box sx={{ padding: "24px 32px" }}>
<Typography
sx={{ fontSize: "18px", fontWeight: "600", color: "#1E2633" }}
>
添加工作流模版
</Typography>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "20px",
}}
>
<OutlinedInput
onChange={(e: any) => {
_.debounce(() => {
searchTemplateNameCallback(e.target.value);
}, 200)();
}}
placeholder="输入关键词搜索"
size="small"
sx={{ width: 340, height: 32, marginTop: "20px" }}
endAdornment={<SearchIcon style={{ color: "#8A9099" }} />}
/>
<Box
sx={{
display: "flex",
justifyContent: "flex-end",
alignItems: "center",
}}
>
<RadioGroupOfButtonStyle
value={templateType}
radioOptions={radioOptions}
handleRadio={handleRadio}
></RadioGroupOfButtonStyle>
<Button
click={addTemplateCallback}
size={"small"}
style={{
marginLeft: "12px",
}}
text={
"添加模版" +
(selectTemplateData.length === 0
? ""
: `(${selectTemplateData.length})`)
}
/>
</Box> </Box>
<Box className={styles.addTemplateBlock}> </Box>
<Box sx={{ padding: "24px 32px" }}>
<Typography sx={{ fontSize: '18px', fontWeight: '600', color: "#1E2633" }}>添加工作流模版</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: "20px" }}>
<OutlinedInput
onChange={(e: any) => {
_.debounce(() => {
searchTemplateNameCallback(e.target.value)
}, 200)();
}}
placeholder="输入关键词搜索"
size="small"
sx={{ width: 340, height: 32, marginTop: "20px" }}
endAdornment={<SearchIcon style={{ color: "#8A9099" }} />}
/>
<Button
click={addTemplateCallback}
size={"small"}
text={'添加模版' + (selectTemplateData.length === 0 ? "" : `(${selectTemplateData.length})`)}
/>
</Box>
{ {addTemplateList.length === 0 && (
addTemplateList.length === 0 && <Box sx={{ <Box
display: 'flex', alignItems: 'center', flexDirection: 'column', minHeight: 'calc(100vh - 376px)', sx={{
justifyContent: 'center' display: "flex",
}}> alignItems: "center",
<img alt="" src={noData} /> flexDirection: "column",
<Typography sx={{ fontSize: '12px', fontWeight: '400', color: '#8A9099' }}>暂未相关模版</Typography> minHeight: "calc(100vh - 376px)",
</Box> justifyContent: "center",
} }}
>
<img alt="" src={noData} />
<Typography
sx={{ fontSize: "12px", fontWeight: "400", color: "#8A9099" }}
>
暂未相关模版
</Typography>
</Box>
)}
<Box sx={{ display: "flex", flexWrap: 'wrap', overflowX: 'hidden', overflowY: 'overlay', marginLeft: '-8px' }} > <Box
{ sx={{
addTemplateList.map((item: any, key: any) => { display: "flex",
return ( flexWrap: "wrap",
<Box className={styles.addTemplateBox} onClick={() => { overflowX: "hidden",
templateSelectCallback(item.id) overflowY: "overlay",
}} marginLeft: "-8px",
sx={{ border: selectTemplateData.includes(item.id) ? '1px solid #1370FF' : "1px solid #EBEDF0;" }} }}
> >
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', }}> {addTemplateList.map((item: any, key: any) => {
<Typography sx={{ fontSize: '14px', fontWeight: '600', color: '#1E2633', marginBottom: "4px", overflow: 'hidden', textOverflow: 'ellipsis' }}>{item.title}</Typography> return (
<Checkbox size="small" sx={{ padding: "0px" }} checked={selectTemplateData.includes(item.id)} /> <Box
</Box> className={styles.addTemplateBox}
<Box sx={{ display: 'flex', marginBottom: "8px" }}> onClick={() => {
<Typography sx={{ fontSize: '12px', fontWeight: '400', color: '#1370FF', marginRight: "24px" }}>版本:{item.version}</Typography> templateSelectCallback(item.id);
<Typography sx={{ fontSize: '12px', fontWeight: '400', color: '#1370FF' }}>更新时间:{item.updateTime}</Typography> }}
</Box> sx={{
<Typography className={styles.templateDescText} >{item.description}</Typography> border: selectTemplateData.includes(item.id)
</Box> ? "1px solid #1370FF"
) : "1px solid #EBEDF0;",
}) }}
} key={item.id}
</Box> >
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<Typography
sx={{
fontSize: "14px",
fontWeight: "600",
color: "#1E2633",
marginBottom: "4px",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
{item.title}
</Typography>
<Checkbox
size="small"
sx={{ padding: "0px" }}
checked={selectTemplateData.includes(item.id)}
/>
</Box>
<Box sx={{ display: "flex", marginBottom: "8px" }}>
<Typography
sx={{
fontSize: "12px",
fontWeight: "400",
color: "#1370FF",
marginRight: "24px",
}}
>
版本:{item.version}
</Typography>
<Typography
sx={{
fontSize: "12px",
fontWeight: "400",
color: "#1370FF",
}}
>
更新时间:{item.updateTime}
</Typography>
</Box>
<Typography className={styles.templateDescText}>
{item.description}
</Typography>
</Box> </Box>
</Box> );
})}
</Box>
</Box> </Box>
); </Box>
</Box>
);
}; };
export default memo(AddTemplate); export default memo(AddTemplate);
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13 * @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-05 18:06:17 * @LastEditTime: 2022-07-06 21:25:00
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -17,239 +17,270 @@ import Add from "@mui/icons-material/Add"; ...@@ -17,239 +17,270 @@ import Add from "@mui/icons-material/Add";
import Button from "@/components/mui/Button"; import Button from "@/components/mui/Button";
import useMyRequest from "@/hooks/useMyRequest"; import useMyRequest from "@/hooks/useMyRequest";
import TemplateBox from "./components/templateBox" import TemplateBox from "./components/templateBox";
import SimpleDialog from "./components/simpleDialog" import SimpleDialog from "./components/simpleDialog";
import AddTemplate from "./components/addTemplate" import AddTemplate from "./components/addTemplate";
import noData from '../../../../assets/project/noTemplate.svg' import noData from "../../../../assets/project/noTemplate.svg";
import { import {
getWorkbenchTemplate, getWorkbenchTemplate,
deleteWorkbenchTemplate, deleteWorkbenchTemplate,
getAddWorkbenchTemplate, getAddWorkbenchTemplate,
addWorkbenchTemplate addWorkbenchTemplate,
} from "@/api/workbench_api"; } from "@/api/workbench_api";
import usePass from "@/hooks/usePass"; import usePass from "@/hooks/usePass";
import ReactFlowEdit from '@/views/WorkFlowEdit' import WorkFlowEdit from "@/views/WorkFlowEdit";
import { useStores } from "@/store"; import { useStores } from "@/store";
import { ICustomTemplate } from "./interface";
import styles from "./index.module.css"; import styles from "./index.module.css";
const ProjectMembers = observer(() => { const ProjectMembers = observer(() => {
const { currentProjectStore } = useStores(); const { currentProjectStore } = useStores();
const projectIdData = toJS(currentProjectStore.currentProjectInfo.id); const projectIdData = toJS(currentProjectStore.currentProjectInfo.id);
const isPass = usePass(); const isPass = usePass();
/** 搜索模板名称 */ /** 搜索模板名称 */
const [templateName, setTemplateName] = useState(""); const [templateName, setTemplateName] = useState("");
/** 模板列表 */ /** 模板列表 */
const [templateList, setTemplateList] = useState([]); const [templateList, setTemplateList] = useState([]);
/** 选中的模板id */ /** 选中的模板id */
const [templateId, setTemplateId] = useState(''); const [templateId, setTemplateId] = useState("");
/** 简单弹窗(删除模板) */ /** 简单弹窗(删除模板) */
const [openDialog, setOpenDialog] = useState(false); const [openDialog, setOpenDialog] = useState(false);
/** 增加模板 */ /** 增加模板 */
const [openAddTemplate, setOpenAddTemplate] = useState(false); const [openAddTemplate, setOpenAddTemplate] = useState(false);
/** 可增加模板列表 */ /** 可增加模板列表 */
const [addTemplateList, setAddTemplateList] = useState([]); const [addTemplateList, setAddTemplateList] = useState([]);
/** 已选择增加的模板列表 */ /** 已选择增加的模板列表 */
const [selectTemplateData, setSelectTemplateData] = useState<string[]>([]); const [selectTemplateData, setSelectTemplateData] = useState<string[]>([]);
// 获取模板列表 /** 是否显示自定义模版编辑并带有参数 */
const { run: getTemplateInfo } = useMyRequest(getWorkbenchTemplate, { const [customTemplateInfo, setCustomTemplateInfo] = useState<ICustomTemplate>(
onSuccess: (result: any) => { {
setTemplateList(result.data); show: false,
}, }
}); );
// 删除模板 // 获取模板列表
const { run: delTemplate } = useMyRequest(deleteWorkbenchTemplate, { const { run: getTemplateInfo } = useMyRequest(getWorkbenchTemplate, {
onSuccess: (result: any) => { onSuccess: (result: any) => {
setOpenDialog(false); setTemplateList(result.data);
getTemplateInfo({ },
projectId: currentProjectStore.currentProjectInfo.id as string, });
title: templateName
}); // 删除模板
}, const { run: delTemplate } = useMyRequest(deleteWorkbenchTemplate, {
}); onSuccess: (result: any) => {
setOpenDialog(false);
// 添加工作流模板-获取模板列表 getTemplateInfo({
const { run: getAddTemplateList } = useMyRequest(getAddWorkbenchTemplate, { projectId: currentProjectStore.currentProjectInfo.id as string,
onSuccess: (result: any) => { title: templateName,
setAddTemplateList(result.data) });
setOpenAddTemplate(true); },
}, });
});
// 添加工作流模板-获取模板列表
// 项目管理员-添加工作流模板-提交 const { run: getAddTemplateList } = useMyRequest(getAddWorkbenchTemplate, {
const { run: addTemplate } = useMyRequest(addWorkbenchTemplate, { onSuccess: (result: any) => {
onSuccess: (result: any) => { setAddTemplateList(result.data);
setOpenAddTemplate(false) setOpenAddTemplate(true);
getTemplateInfo({ },
projectId: currentProjectStore.currentProjectInfo.id as string, });
});
setSelectTemplateData([]) // 项目管理员-添加工作流模板-提交
}, const { run: addTemplate } = useMyRequest(addWorkbenchTemplate, {
}); onSuccess: (result: any) => {
setOpenAddTemplate(false);
useEffect(() => { getTemplateInfo({
getTemplateInfo({ projectId: currentProjectStore.currentProjectInfo.id as string,
projectId: currentProjectStore.currentProjectInfo.id as string, });
}); setSelectTemplateData([]);
}, [currentProjectStore.currentProjectInfo.id, getTemplateInfo]); },
});
useEffect(() => { useEffect(() => {
console.log('projectIdData: ', projectIdData); getTemplateInfo({
}, [projectIdData]) projectId: currentProjectStore.currentProjectInfo.id as string,
});
/** 删除模板 */ }, [currentProjectStore.currentProjectInfo.id, getTemplateInfo]);
const deleteTemplate = () => {
delTemplate({ useEffect(() => {
projectId: currentProjectStore.currentProjectInfo.id as string, console.log("projectIdData: ", projectIdData);
workflowSpecId: templateId, }, [projectIdData]);
})
}; /** 删除模板 */
const deleteTemplate = () => {
/** 打开弹窗 */ delTemplate({
const startDialog = (id: string) => { projectId: currentProjectStore.currentProjectInfo.id as string,
setTemplateId(id) workflowSpecId: templateId,
setOpenDialog(true); });
}; };
/** 关闭弹窗 */ /** 打开弹窗 */
const closeDialog = () => { const startDialog = (id: string) => {
setOpenDialog(false); setTemplateId(id);
}; setOpenDialog(true);
};
/** 增加模板 */
const addTemplateBlock = () => { /** 关闭弹窗 */
getAddTemplateList({ const closeDialog = () => {
projectId: currentProjectStore.currentProjectInfo.id as string, setOpenDialog(false);
productId: 'cadd', };
})
}; /** 增加模板 */
const addTemplateBlock = () => {
/** 关闭增加模板 */ getAddTemplateList({
const closeAddTemplateBlock = () => { projectId: currentProjectStore.currentProjectInfo.id as string,
setOpenAddTemplate(false); productId: "cadd",
setSelectTemplateData([]) });
}; };
/** 增加模板操作 */ /** 关闭增加模板 */
const addTemplateCallback = () => { const closeAddTemplateBlock = () => {
addTemplate({ setOpenAddTemplate(false);
projectId: currentProjectStore.currentProjectInfo.id as string, setSelectTemplateData([]);
workflowSpecIds: selectTemplateData };
})
} /** 增加模板操作 */
const addTemplateCallback = () => {
/** 搜索模板 */ addTemplate({
const searchTemplateNameCallback = (data: any) => { projectId: currentProjectStore.currentProjectInfo.id as string,
getAddTemplateList({ workflowSpecIds: selectTemplateData,
projectId: currentProjectStore.currentProjectInfo.id as string, });
productId: 'cadd', };
title: data
}) /** 搜索模板 */
} const searchTemplateNameCallback = (data: any) => {
getAddTemplateList({
const templateSelectCallback = (data: string) => { projectId: currentProjectStore.currentProjectInfo.id as string,
let list: string[] = [...selectTemplateData] productId: "cadd",
if (selectTemplateData.filter(e => e === data).length > 0) { title: data,
list = list.filter(e => e !== data) });
setSelectTemplateData(list) };
} else {
list.push(data) const templateSelectCallback = (data: string) => {
setSelectTemplateData(list) let list: string[] = [...selectTemplateData];
} if (selectTemplateData.filter((e) => e === data).length > 0) {
list = list.filter((e) => e !== data);
} setSelectTemplateData(list);
} else {
const searchChange = (data: any) => { list.push(data);
setTemplateName(data.length > 30 ? data.slice(0, 30) : data); setSelectTemplateData(list);
} }
};
useEffect(() => {
setTimeout(() => { const searchChange = (data: any) => {
getTemplateInfo({ setTemplateName(data.length > 30 ? data.slice(0, 30) : data);
projectId: currentProjectStore.currentProjectInfo.id as string, };
title: templateName
}); useEffect(() => {
}, 300) setTimeout(() => {
}, [templateName]); getTemplateInfo({
projectId: currentProjectStore.currentProjectInfo.id as string,
return ( title: templateName,
<Box className={styles.headerBox}> });
}, 300);
<Box className={styles.tabBox} > }, [templateName]);
<OutlinedInput
onChange={(e: any) => { return (
searchChange(e.target.value) <Box className={styles.headerBox}>
}} <Box className={styles.tabBox}>
value={templateName} <OutlinedInput
placeholder="输入关键词搜索" onChange={(e: any) => {
size="small" searchChange(e.target.value);
sx={{ width: 340, height: 32 }} }}
endAdornment={<SearchIcon style={{ color: "#8A9099" }} />} value={templateName}
/> placeholder="输入关键词搜索"
size="small"
{ sx={{ width: 340, height: 32 }}
templateList.length > 0 && isPass("PROJECT_WORKBENCH_FLOES_ADD", 'MANAGER') && endAdornment={<SearchIcon style={{ color: "#8A9099" }} />}
<Button text={'添加工作流模版'} img={<Add />} click={addTemplateBlock} size={'small'} /> />
}
{templateList.length > 0 &&
</Box> isPass("PROJECT_WORKBENCH_FLOES_ADD", "MANAGER") && (
<Button
{ text={"添加工作流模版"}
templateList.length === 0 && templateName.length > 0 && img={<Add />}
<Box sx={{ click={addTemplateBlock}
display: 'flex', alignItems: 'center', flexDirection: 'column', minHeight: 'calc(100vh - 376px)', size={"small"}
justifyContent: 'center' />
}}> )}
<img alt="" src={noData} /> </Box>
<Typography sx={{ fontSize: '12px', fontWeight: '400', color: '#8A9099' }}>暂未开启模版</Typography>
</Box> {templateList.length === 0 && templateName.length > 0 && (
} <Box
{ sx={{
templateList.length > 0 && <Box sx={{ display: "flex", flexWrap: 'wrap', marginLeft: "-8px" }} > display: "flex",
{ alignItems: "center",
templateList && templateList.length > 0 && templateList.map((item, key) => { flexDirection: "column",
return <TemplateBox data={item} startDialog={startDialog} /> minHeight: "calc(100vh - 376px)",
}) justifyContent: "center",
} }}
</Box> >
} <img alt="" src={noData} />
{ <Typography
templateList.length === 0 && templateName.length === 0 && isPass("PROJECT_WORKBENCH_FLOES_ADD", 'MANAGER') && <Box className={styles.addNewTemplate} sx={{ fontSize: "12px", fontWeight: "400", color: "#8A9099" }}
onClick={addTemplateBlock} >
> 暂未开启模版
<Add sx={{ color: "#565C66", fontSize: "20px", width: "30px", height: '30px' }} /> </Typography>
<Typography sx={{ fontSize: '14px', fontWeight: '400', color: '#8A9099', marginTop: "15px" }}>添加工作流模版</Typography> </Box>
</Box> )}
} {templateList.length > 0 && (
<Box sx={{ display: "flex", flexWrap: "wrap", marginLeft: "-8px" }}>
<AddTemplate {templateList &&
openAddTemplate={openAddTemplate} templateList.length > 0 &&
closeAddTemplateBlock={closeAddTemplateBlock} templateList.map((item, key) => {
addTemplateList={addTemplateList} return <TemplateBox data={item} startDialog={startDialog} />;
templateSelectCallback={templateSelectCallback} })}
selectTemplateData={selectTemplateData} </Box>
addTemplateCallback={addTemplateCallback} )}
searchTemplateNameCallback={searchTemplateNameCallback} {templateList.length === 0 &&
/> templateName.length === 0 &&
isPass("PROJECT_WORKBENCH_FLOES_ADD", "MANAGER") && (
{/* <ReactFlowEdit/> */} <Box className={styles.addNewTemplate} onClick={addTemplateBlock}>
<Add
<SimpleDialog sx={{
text={'确认移除该模板吗?'} color: "#565C66",
title={'删除模板'} fontSize: "20px",
openDialog={openDialog} width: "30px",
closeDialog={closeDialog} height: "30px",
onConfirm={deleteTemplate} }}
/> />
<Typography
</Box> sx={{
); fontSize: "14px",
fontWeight: "400",
color: "#8A9099",
marginTop: "15px",
}}
>
添加工作流模版
</Typography>
</Box>
)}
<AddTemplate
openAddTemplate={openAddTemplate}
closeAddTemplateBlock={closeAddTemplateBlock}
addTemplateList={addTemplateList}
templateSelectCallback={templateSelectCallback}
selectTemplateData={selectTemplateData}
addTemplateCallback={addTemplateCallback}
searchTemplateNameCallback={searchTemplateNameCallback}
/>
{customTemplateInfo?.show ? <WorkFlowEdit /> : null}
<SimpleDialog
text={"确认移除该模板吗?"}
title={"删除模板"}
openDialog={openDialog}
closeDialog={closeDialog}
onConfirm={deleteTemplate}
/>
</Box>
);
}); });
export default memo(ProjectMembers); export default memo(ProjectMembers);
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-07-06 14:44:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-06 14:47:28
* @FilePath: /bkunyun/src/views/Project/ProjectWorkbench/workbenchTemplate/interface.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export interface ICustomTemplate{
show?: boolean;
id?: string;
}
\ No newline at end of file
import ReactFlow, { import ReactFlow, {
Controls, Controls,
Background, Background,
useNodesState, useNodesState,
useEdgesState, useEdgesState,
Handle, Handle,
Position, Position,
ReactFlowProps, ReactFlowProps,
Node, Node,
} from "react-flow-renderer"; } from "react-flow-renderer";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
...@@ -14,7 +14,11 @@ import classNames from "classnames"; ...@@ -14,7 +14,11 @@ import classNames from "classnames";
import jobFail from "@/assets/project/jobFail.svg"; import jobFail from "@/assets/project/jobFail.svg";
import jobRun from "@/assets/project/jobRun.svg"; import jobRun from "@/assets/project/jobRun.svg";
import jobSue from "@/assets/project/jobSue.svg"; import jobSue from "@/assets/project/jobSue.svg";
import { IEdge, IExecutionStatus, ITask } from "../../ProjectSubmitWork/interface"; import {
IEdge,
IExecutionStatus,
ITask,
} from "../../ProjectSubmitWork/interface";
import { IBatchNode, ILine } from "./interface"; import { IBatchNode, ILine } from "./interface";
import styles from "./index.module.css"; import styles from "./index.module.css";
...@@ -27,300 +31,317 @@ import styles from "./index.module.css"; ...@@ -27,300 +31,317 @@ import styles from "./index.module.css";
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
interface IProps extends ReactFlowProps { interface IProps extends ReactFlowProps {
tasks?: ITask[]; tasks?: ITask[];
/** 点击batch事件 */ /** 点击batch事件 */
onBatchClick?: (val: string) => void; onBatchClick?: (val: string) => void;
setSelectedNodeId?: (val:string) => void; setSelectedNodeId?: (val: string) => void;
selectedNodeId?: string; selectedNodeId?: string;
} }
/** 获取imgUrl */ /** 获取imgUrl */
const getImgUrl = (type: IExecutionStatus) => { const getImgUrl = (type: IExecutionStatus) => {
if(type === 'Done'){ if (type === "Done") {
return jobSue return jobSue;
} }
if(type === 'Failed'){ if (type === "Failed") {
return jobFail return jobFail;
} }
if(type === 'Running'){ if (type === "Running") {
return jobRun return jobRun;
} }
return undefined return undefined;
} };
/** 自定义batch节点 */ /** 自定义batch节点 */
const BatchNode = (props: IBatchNode) => { const BatchNode = (props: IBatchNode) => {
const { data } = props; const { data } = props;
const { dotStatus, style, isFlowNode, label, selectedStatus } = data; const { dotStatus, style, isFlowNode, label, selectedStatus } = data;
return ( return (
<div <div
className={classNames({ className={classNames({
[styles.batchNode]: true, [styles.batchNode]: true,
[styles.selectedBatchBox]: selectedStatus, [styles.selectedBatchBox]: selectedStatus,
[styles.selectBatchNode]: selectedStatus, [styles.selectBatchNode]: selectedStatus,
})} })}
style={style} style={style}
> >
{dotStatus?.isInput ? ( {dotStatus?.isInput ? (
<Handle <Handle
style={{ background: "#fff ", border: "1px solid #D1D6DE", left: 20 }} style={{ background: "#fff ", border: "1px solid #D1D6DE", left: 20 }}
type="target" type="target"
position={Position.Top} position={Position.Top}
/> />
) : null} ) : null}
<div <div
className={classNames({ className={classNames({
[styles.batchRotate]: isFlowNode, [styles.batchRotate]: isFlowNode,
})} })}
> >
{label || ""} {label || ""}
{data.isCheck && <span className={styles.successDot}></span>} {data.isCheck && <span className={styles.successDot}></span>}
</div> </div>
{dotStatus?.isOutput ? ( {dotStatus?.isOutput ? (
<Handle <Handle
style={{ background: "#fff ", border: "1px solid #D1D6DE", left: 20 }} style={{ background: "#fff ", border: "1px solid #D1D6DE", left: 20 }}
type="source" type="source"
position={Position.Bottom} position={Position.Bottom}
/> />
) : null} ) : null}
</div> </div>
); );
}; };
/** 自定义flow节点 */ /** 自定义flow节点 */
const FlowNode = (props: any) => { const FlowNode = (props: any) => {
const { data } = props; const { data } = props;
return ( return (
<div <div
className={classNames({ className={classNames({
[styles.flowNode]: true, [styles.flowNode]: true,
})} })}
> >
{data?.dotStatus?.isInput ? ( {data?.dotStatus?.isInput ? (
<Handle <Handle
style={{ background: "#C2C6CC ", left: 12 }} style={{ background: "#C2C6CC ", left: 12 }}
type="target" type="target"
position={Position.Top} position={Position.Top}
/> />
) : null} ) : null}
<div style={{ display: "flex", alignItems: "center" }}> <div style={{ display: "flex", alignItems: "center" }}>
{data?.label || ""} {data?.label || ""}
{data.isCheck && <span className={styles.successDot}></span>} {data.isCheck && <span className={styles.successDot}></span>}
{getImgUrl(data.executionStatus) && <img style={{ marginLeft: "6px" }} src={getImgUrl(data.executionStatus)} alt="" />} {getImgUrl(data.executionStatus) && (
</div> <img
{data?.dotStatus?.isOutput ? ( style={{ marginLeft: "6px" }}
<Handle src={getImgUrl(data.executionStatus)}
style={{ background: "#C2C6CC ", left: 12 }} alt=""
type="source" />
position={Position.Bottom} )}
id="a" </div>
/> {data?.dotStatus?.isOutput ? (
) : null} <Handle
</div> style={{ background: "#C2C6CC ", left: 12 }}
); type="source"
position={Position.Bottom}
id="a"
/>
) : null}
</div>
);
}; };
const Flow = (props: IProps) => { const Flow = (props: IProps) => {
const { tasks, onBatchClick, setSelectedNodeId, selectedNodeId } = props; const { tasks, onBatchClick, setSelectedNodeId, selectedNodeId } = props;
/** 自定义的节点类型 */ /** 自定义的节点类型 */
const nodeTypes = useMemo(() => { const nodeTypes = useMemo(() => {
return { batchNode: BatchNode, flowNode: FlowNode }; return { batchNode: BatchNode, flowNode: FlowNode };
}, []); }, []);
/** 内部维护的选择的节点Id */
const [inSideNodeId, setInSideNodeId] = useState<string>("");
/** 获取是否有输入节点或者是否有输出节点 */ /** 获取是否有输入节点或者是否有输出节点 */
const nodesInputAndOutputStatus = useCallback( const nodesInputAndOutputStatus = useCallback(
(id: string) => { (id: string) => {
/** 所有的连线 */ /** 所有的连线 */
const lineArr: IEdge[] = []; const lineArr: IEdge[] = [];
tasks?.length && tasks?.length &&
tasks.forEach((item) => { tasks.forEach((item) => {
lineArr.push(...item.edges); lineArr.push(...item.edges);
}); });
/** 所有的输入节点ID */ /** 所有的输入节点ID */
const isInput = lineArr?.some((item) => item.target === id); const isInput = lineArr?.some((item) => item.target === id);
/** 所有的输出节点ID */ /** 所有的输出节点ID */
const isOutput = lineArr?.some((item) => item.source === id); const isOutput = lineArr?.some((item) => item.source === id);
return { return {
isInput, isInput,
isOutput, isOutput,
}; };
}, },
[tasks] [tasks]
); );
/** 获取是否有流节点 */ /** 获取是否有流节点 */
const isFlowNode = useCallback( const isFlowNode = useCallback(
(id: string) => { (id: string) => {
return ( return (
tasks?.length && tasks?.length &&
tasks?.some((item) => { tasks?.some((item) => {
return item.parentNode === id; return item.parentNode === id;
}) })
); );
}, },
[tasks] [tasks]
); );
/** 通过子flow节点计算batch节点的样式 */ /** 通过子flow节点计算batch节点的样式 */
const getBatchStyle = useCallback( const getBatchStyle = useCallback(
(value: ITask) => { (value: ITask) => {
const positionXArr: number[] = []; const positionXArr: number[] = [];
const positionYArr: number[] = []; const positionYArr: number[] = [];
tasks?.length && tasks?.length &&
tasks?.forEach((item) => { tasks?.forEach((item) => {
if (item.parentNode === value.id) { if (item.parentNode === value.id) {
positionXArr.push(item.position?.x || 0); positionXArr.push(item.position?.x || 0);
positionYArr.push(item.position?.y || 0); positionYArr.push(item.position?.y || 0);
} }
}); });
positionXArr.sort((a, b) => { positionXArr.sort((a, b) => {
return a - b; return a - b;
}); });
positionYArr.sort((a, b) => { positionYArr.sort((a, b) => {
return a - b; return a - b;
}); });
let width = 176, let width = 176,
height = 22; height = 22;
if (positionXArr?.length) { if (positionXArr?.length) {
const val = positionXArr[positionXArr.length - 1] + 150; const val = positionXArr[positionXArr.length - 1] + 150;
width = val > 176 ? val : width; width = val > 176 ? val : width;
} }
if (positionYArr?.length) { if (positionYArr?.length) {
const val = positionYArr[positionYArr.length - 1]; const val = positionYArr[positionYArr.length - 1];
height = val > 22 ? val : height; height = val > 22 ? val : height;
} }
return { return {
width, width,
height, height,
}; };
}, },
[tasks] [tasks]
); );
/** 生成初始化node节点 */ /** 生成初始化node节点 */
const initialNodes = useMemo(() => { const initialNodes = useMemo(() => {
const val: any = []; const val: any = [];
tasks?.length && tasks?.length &&
tasks.forEach((item) => { tasks.forEach((item) => {
val.push({ val.push({
id: item.id, id: item.id,
type: item.type === "BATCH" ? "batchNode" : "flowNode", type: item.type === "BATCH" ? "batchNode" : "flowNode",
data: { data: {
label: item.title || "", label: item.title || "",
...(item.type === "BATCH" ...(item.type === "BATCH"
? { ? {
/** 是否有流节点 */ /** 是否有流节点 */
isFlowNode: isFlowNode(item.id), isFlowNode: isFlowNode(item.id),
/** 选中状态 */ /** 选中状态 */
selectedStatus: selectedNodeId === item.id, selectedStatus: selectedNodeId
} ? selectedNodeId === item.id
: {}), : inSideNodeId === item.id,
isCheck: item.isCheck, }
executionStatus: item.executionStatus, : {}),
/** 输入输出圆点状态 */ isCheck: item.isCheck,
dotStatus: nodesInputAndOutputStatus(item.id), executionStatus: item.executionStatus,
/** 样式 */ /** 输入输出圆点状态 */
style: { dotStatus: nodesInputAndOutputStatus(item.id),
...getBatchStyle(item), /** 样式 */
padding: isFlowNode(item.id) ? "20px" : "12px 20px", style: {
}, ...getBatchStyle(item),
}, padding: isFlowNode(item.id) ? "20px" : "12px 20px",
position: { x: Number(item.position.x), y: Number(item.position.y) }, },
...(item.type === "BATCH" ? { style: { zIndex: -1 } } : {}), },
...(item.parentNode ? { parentNode: item.parentNode } : {}), position: { x: Number(item.position.x), y: Number(item.position.y) },
...(item.type === "BATCH" ? { extent: "parent" } : {}), ...(item.type === "BATCH" ? { style: { zIndex: -1 } } : {}),
}); ...(item.parentNode ? { parentNode: item.parentNode } : {}),
}); ...(item.type === "BATCH" ? { extent: "parent" } : {}),
return val; });
}, [ });
tasks, return val;
isFlowNode, }, [
selectedNodeId, tasks,
nodesInputAndOutputStatus, isFlowNode,
getBatchStyle, selectedNodeId,
]); inSideNodeId,
nodesInputAndOutputStatus,
getBatchStyle,
]);
/** 生成初始化的连线节点 */ /** 生成初始化的连线节点 */
const initialEdges = useMemo(() => { const initialEdges = useMemo(() => {
const val: ILine[] = []; const val: ILine[] = [];
tasks?.length && tasks?.length &&
tasks.forEach((item) => { tasks.forEach((item) => {
item.edges.forEach((every) => { item.edges.forEach((every) => {
const newLine = { const newLine = {
...every, ...every,
batchId: item.parentNode ? item.parentNode : item.id, batchId: item.parentNode ? item.parentNode : item.id,
}; };
val.push(newLine); val.push(newLine);
}, []); }, []);
}); });
return val.map((item: ILine) => { return val.map((item: ILine) => {
return { const newSelectId = selectedNodeId ? selectedNodeId : inSideNodeId;
id: item.id, return {
source: item.source, id: item.id,
target: item.target, source: item.source,
type: "smoothstep", target: item.target,
...(item?.batchId === selectedNodeId type: "smoothstep",
? { style: { stroke: "#1370FF" }, animated: true } ...(item?.batchId === newSelectId
: {}), ? { style: { stroke: "#1370FF" }, animated: true }
labelStyle: { fill: "#8A9099" }, : {}),
labelBgStyle: { fill: "#F7F8FA " }, labelStyle: { fill: "#8A9099" },
label: item.label ? `(${item.label})` : "", labelBgStyle: { fill: "#F7F8FA " },
}; label: item.label ? `(${item.label})` : "",
}); };
}, [selectedNodeId, tasks]); });
}, [inSideNodeId, selectedNodeId, tasks]);
/** flowNode点击事件 */ /** flowNode点击事件 */
const onNodeClick = (e: any, node: Node) => { const onNodeClick = (e: any, node: Node) => {
tasks?.forEach((item) => {
tasks?.forEach((item) => { if (item.id === node.id) {
if (item.id === node.id) { if (item.parentNode) {
if (item.parentNode) { setSelectedNodeId
setSelectedNodeId && setSelectedNodeId(item.parentNode); ? setSelectedNodeId(item.parentNode)
onBatchClick && onBatchClick(item.parentNode); : setInSideNodeId(item.parentNode);
document.getElementById(`point${item.parentNode}`)?.scrollIntoView(true) onBatchClick && onBatchClick(item.parentNode);
} else { document
setSelectedNodeId && setSelectedNodeId(node.id); .getElementById(`point${item.parentNode}`)
onBatchClick && onBatchClick(node.id || ""); ?.scrollIntoView(true);
document.getElementById(`point${node.id}`)?.scrollIntoView(true) } else {
} setSelectedNodeId
} ? setSelectedNodeId(node.id)
}); : setInSideNodeId(node.id);
}; onBatchClick && onBatchClick(node.id || "");
document.getElementById(`point${node.id}`)?.scrollIntoView(true);
}
}
});
};
const handlePaneClick = () => { const handlePaneClick = () => {
setSelectedNodeId && setSelectedNodeId(''); setSelectedNodeId ? setSelectedNodeId("") : setInSideNodeId("");
onBatchClick && onBatchClick(''); onBatchClick && onBatchClick("");
} };
/** node节点 */ /** node节点 */
const [nodes, setNodes] = useNodesState(initialNodes); const [nodes, setNodes] = useNodesState(initialNodes);
/** 连线数组 */ /** 连线数组 */
const [edges, setEdges] = useEdgesState(initialEdges); const [edges, setEdges] = useEdgesState(initialEdges);
useEffect(() => { useEffect(() => {
setEdges(initialEdges); setEdges(initialEdges);
}, [initialEdges, setEdges]); }, [initialEdges, setEdges]);
useEffect(() => { useEffect(() => {
setNodes(initialNodes); setNodes(initialNodes);
}, [initialNodes, setNodes]); }, [initialNodes, setNodes]);
return ( return (
<ReactFlow <ReactFlow
nodes={nodes} nodes={nodes}
edges={edges} edges={edges}
fitView fitView
proOptions={{ hideAttribution: true, account: "" }} proOptions={{ hideAttribution: true, account: "" }}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
onPaneClick={handlePaneClick} onPaneClick={handlePaneClick}
onNodeClick={onNodeClick} onNodeClick={onNodeClick}
{...props} {...props}
> >
<Controls /> <Controls />
<Background color="#aaa" gap={16} /> <Background color="#aaa" gap={16} />
</ReactFlow> </ReactFlow>
); );
}; };
export default Flow; export default Flow;
.operatorItemBox {
background-color: #fff;
border-radius: 4px;
cursor: grab;
padding: 16px 16px 0 24px;
}
.dragBox {
background-color: #f5f6f7;
}
.operatorItemTitle {
user-select: none;
font-size: 14px;
color: #1e2633;
}
.operatorItemText {
color: #8a9099;
margin: 8px 0;
font-size: 12px;
user-select: none;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.labelBox {
user-select: none;
display: inline-block;
border-radius: 2px;
font-size: 12px;
padding: 2px 8px;
}
.searchBox {
padding: 0 24px 16px 24px;
}
.footerBox {
display: flex;
align-items: center;
padding-bottom: 16px;
border-bottom: 1px solid #f0f2f5;
}
.operatorListBox {
height: 100%;
}
.listBox {
overflow-y: scroll;
height: calc(100% - 48px);
}
import { OutlinedInput } from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import classNames from "classnames";
import { useCallback, useState } from "react";
import { mockData } from "./mock";
import { IOperatorItemProps } from "./interface";
import styles from "./index.module.css";
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-07-06 15:16:01
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-06 21:23:19
* @FilePath: /bkunyun/src/views/WorkFlowEdit/components/OperatorList/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
const OperatorItem = (props: IOperatorItemProps) => {
const { info } = props;
const [isDragStyle, setIsDragStyle] = useState<boolean>(false);
/** 拖拽开始 */
const onDragStart = useCallback(() => {
setIsDragStyle(true);
}, []);
/** 拖拽结束 */
const onDragEnd = useCallback((e: React.DragEvent<HTMLDivElement>) => {
console.log(e);
setIsDragStyle(false);
}, []);
return (
<div
className={classNames({
[styles.operatorItemBox]: true,
[styles.dragBox]: isDragStyle,
})}
draggable={true}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
>
<h2 className={styles.operatorItemTitle}>说什么呢啊</h2>
<div className={styles.operatorItemText}>
STU utility
是一个R-packa标处理目标处理,目标处理目标处理标处理目标处理后期委屈好委屈农,博啊发布丢我被欺安度切换阿斯顿几切换,i的亲戚我好奇你eqeqeweqeqeeqeqeqeqeq。
</div>
<div className={styles.footerBox}>
<span
className={styles.labelBox}
style={{
background: true ? "#EBF3FF" : "#E3FAEC",
color: true ? "#1370FF" : "#02AB83",
}}
>
公共平台
</span>
</div>
</div>
);
};
const OperatorList = () => {
return (
<div className={styles.operatorListBox}>
<div className={styles.searchBox}>
<OutlinedInput
onChange={(e: any) => {
console.log(e.target.value);
}}
// value={templateName}
placeholder="输入关键词搜索"
size="small"
sx={{ height: 32, width: "100%" }}
endAdornment={<SearchIcon style={{ color: "#8A9099" }} />}
/>
</div>
<div className={styles.listBox}>
{mockData.map((item) => {
return <OperatorItem key={item.id} info={item} />;
})}
</div>
</div>
);
};
export default OperatorList;
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-07-06 15:32:11
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-06 15:32:42
* @FilePath: /bkunyun/src/views/WorkFlowEdit/components/OperatorList/interface.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export interface IOperatorItemProps {
info: any
}
\ No newline at end of file
export const mockData = [
{
"id": "批式Node ID (后端定义)",
"title": "Docking(Vina)",
"description": "这是一段Docking(Vina)算子的描述",
"version": "1.0.0",
"allVersions": ["1.0.0"],
"updateTime": "2022/07/05",
"tags": ["公共算子"],
"position": {
"x": null,
"y": null
},
"type": "BATCH",
"parentNode": "",
"data": {
"status": "wait",
"parameters": [
{
"id": "",
"show": true,
"name": "smi_in",
"required": true,
"domType": "fileSelect",
"dataType": "string",
"value": "",
"description": "",
"validators": [
{
"helpText": "请选择smi文件作为输入",
"regex": "^.[s][m][i]$"
}
],
"choices": [],
"parameterGroup": "in"
},
{
"id": "",
"show": true,
"name": "receptor_in",
"required": true,
"domType": "fileSelect",
"dataType": "string",
"value": "",
"description": "",
"validators": [
{
"helpText": "请选择pdb文件作为输入",
"regex": "^.[p][d][b]$"
}
],
"choices": [],
"parameterGroup": "in"
},
{
"id": "",
"show": true,
"name": "dataset_out",
"required": true,
"domType": "input",
"dataType": "dataset",
"value": "",
"description": "",
"validators": [
{
"helpText": "仅支持中文、英文、数据以及下划线",
"regex": "^[\u4E00-\u9FA5A-Za-z0-9_]+$"
}
],
"choices": [],
"parameterGroup": "out"
},
{
"id": "",
"show": true,
"name": "pdb_out",
"required": true,
"domType": "input",
"dataType": "file",
"value": "",
"description": "",
"validators": [
{
"helpText": "仅支持中文、英文、数据以及下划线",
"regex": "^[\u4E00-\u9FA5A-Za-z0-9_]+$"
}
],
"choices": [],
"parameterGroup": "out"
}
]
},
"edges": []
},
{
"id": "流式Node ID 1 (后端定义)",
"title": "RecordFileReader",
"description": "这是一段RecordFileReader算子的描述",
"version": "1.0.0",
"allVersions": ["1.0.0"],
"updateTime": "2022/07/05",
"tags": ["公共算子"],
"position": {
"x": 0,
"y": 0
},
"type": "FLOW",
"parentNode": "批式Node ID",
"data": {
"status": "wait",
"parameters": [
{
"id": "",
"show": true,
"name": "raw",
"required": false,
"domType": "radio",
"dataType": "boolean",
"value": false,
"description": "",
"validators": [],
"choices": [
{
"key": "true",
"value": true
},
{
"key": "false",
"value": false
}
],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "sep",
"required": false,
"domType": "input",
"dataType": "string",
"value": "",
"description": "",
"validators": [],
"choices": [],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "contains_sep",
"required": false,
"domType": "radio",
"dataType": "boolean",
"value": false,
"description": "",
"validators": [],
"choices": [
{
"key": "true",
"value": true
},
{
"key": "false",
"value": false
}
],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "encoding",
"required": false,
"domType": "input",
"dataType": "string",
"value": "UTF-8",
"description": "",
"validators": [],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": true,
"name": "chunk_size",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1000,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": true,
"name": "compression",
"required": false,
"domType": "input",
"dataType": "string",
"value": "",
"description": "",
"validators": [],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": false,
"name": "cpus",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "ntasks",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "partition",
"required": false,
"domType": "input",
"dataType": "string",
"value": "c-4-1",
"description": "",
"validators": "",
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "parallelism",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
}
]
},
"edges": [
{
"id": "",
"source": "流式Node ID 1",
"target": "流式Node ID 2"
}
]
},
{
"id": "流式Node ID 2 (后端定义)",
"title": "Standard",
"description": "这是一段Standard算子的描述",
"version": "1.0.0",
"allVersions": ["1.0.0"],
"updateTime": "2022/07/05",
"tags": ["公共算子"],
"position": {
"x": 0,
"y": 0
},
"type": "FLOW",
"parentNode": "批式Node ID",
"data": {
"status": "wait",
"parameters": [
{
"id": "",
"show": false,
"name": "cpus",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"name": "ntasks",
"required": false,
"domType": "input",
"dataType": "int",
"value": 4,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "partition",
"required": false,
"domType": "input",
"dataType": "string",
"value": "c-4-1",
"description": "",
"validators": "",
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "parallelism",
"required": false,
"domType": "input",
"dataType": "int",
"value": 6,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
}
]
},
"edges": [
{
"id": "",
"source": "流式Node ID 2",
"target": "流式Node ID 3"
}
]
},
{
"id": "流式Node ID 3 (后端定义)",
"title": "Docking",
"description": "这是一段Docking算子的描述",
"version": "1.0.0",
"allVersions": ["1.0.0"],
"updateTime": "2022/07/05",
"tags": ["公共算子"],
"position": {
"x": 0,
"y": 0
},
"type": "FLOW",
"parentNode": "批式Node ID",
"data": {
"status": "wait",
"parameters": [
{
"id": "",
"show": true,
"name": "core_num",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "centerX",
"required": true,
"domType": "input",
"dataType": "int",
"value": "",
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "centerY",
"required": true,
"domType": "input",
"dataType": "int",
"value": "",
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "centerZ",
"required": true,
"domType": "input",
"dataType": "int",
"value": "",
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "boxSizeX",
"required": true,
"domType": "input",
"dataType": "int",
"value": "",
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "boxSizeY",
"required": true,
"domType": "input",
"dataType": "int",
"value": "",
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "boxSizeZ",
"required": true,
"domType": "input",
"dataType": "int",
"value": "",
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "poses_num",
"required": false,
"domType": "input",
"dataType": "int",
"value": 9,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": true,
"name": "verbosity",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": true,
"name": "exhaustiveness",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": false,
"name": "cpus",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "ntasks",
"required": false,
"domType": "input",
"dataType": "int",
"value": 4,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "partition",
"required": false,
"domType": "input",
"dataType": "string",
"value": "c-4-1",
"description": "",
"validators": [],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "parallelism",
"required": false,
"domType": "input",
"dataType": "int",
"value": 6,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
}
]
},
"edges": [
{
"id": "",
"source": "流式Node ID 3",
"target": "流式Node ID 4"
},
{
"id": "",
"source": "流式Node ID 3",
"target": "流式Node ID 5"
}
]
},
{
"id": "流式Node ID 4 (后端定义)",
"title": "Hitlist",
"description": "这是一段Hitlist算子的描述",
"version": "1.0.0",
"allVersions": ["1.0.0"],
"updateTime": "2022/07/05",
"tags": ["公共算子"],
"position": {
"x": 0,
"y": 0
},
"type": "FLOW",
"parentNode": "批式Node ID",
"data": {
"status": "wait",
"parameters": [
{
"id": "",
"show": false,
"name": "cpus",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"name": "ntasks",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "partition",
"required": false,
"domType": "input",
"dataType": "string",
"value": "c-4-1",
"description": "",
"validators": "",
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "parallelism",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
}
]
},
"edges": [
{
"id": "",
"source": "流式Node ID 4",
"target": "流式Node ID 6"
}
]
},
{
"id": "流式Node ID 5 (后端定义)",
"title": "DatasetWritwer",
"description": "这是一段DatasetWritwer算子的描述",
"version": "1.0.0",
"allVersions": ["1.0.0"],
"updateTime": "2022/07/05",
"tags": ["公共算子"],
"position": {
"x": 0,
"y": 0
},
"type": "FLOW",
"parentNode": "批式Node ID",
"data": {
"status": "wait",
"parameters": [
{
"id": "",
"show": true,
"name": "compression",
"required": false,
"domType": "input",
"dataType": "string",
"value": "snappy",
"description": "",
"validators": [],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": true,
"name": "chunk_size",
"required": false,
"domType": "input",
"dataType": "int",
"value": 100,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": false,
"name": "cpus",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"name": "ntasks",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "partition",
"required": false,
"domType": "input",
"dataType": "string",
"value": "c-4-1",
"description": "",
"validators": "",
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "parallelism",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
}
]
},
"edges": []
},
{
"id": "流式Node ID 6 (后端定义)",
"title": "RecordFileWriter",
"description": "这是一段RecordFileWriter算子的描述",
"version": "1.0.0",
"allVersions": ["1.0.0"],
"updateTime": "2022/07/05",
"tags": ["公共算子"],
"position": {
"x": 0,
"y": 0
},
"type": "FLOW",
"parentNode": "批式Node ID",
"data": {
"status": "wait",
"parameters": [
{
"id": "",
"show": true,
"name": "many",
"required": false,
"domType": "radio",
"dataType": "boolean",
"value": "false",
"description": "",
"validators": [],
"choices": [
{
"key": "true",
"value": "true"
},
{
"key": "false",
"value": "false"
}
],
"parameterGroup": "basis"
},
{
"id": "",
"show": true,
"name": "compression",
"required": false,
"domType": "input",
"dataType": "string",
"value": "snappy",
"description": "",
"validators": [],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": true,
"name": "chunk_size",
"required": false,
"domType": "input",
"dataType": "int",
"value": 100,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": true,
"name": "suffix",
"required": false,
"domType": "input",
"dataType": "string",
"value": ".txt",
"description": "",
"validators": [],
"choices": [],
"parameterGroup": "senior"
},
{
"id": "",
"show": false,
"name": "cpus",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"name": "ntasks",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "partition",
"required": false,
"domType": "input",
"dataType": "string",
"value": "c-4-1",
"description": "",
"validators": "",
"choices": [],
"parameterGroup": "hardware"
},
{
"id": "",
"show": false,
"name": "parallelism",
"required": false,
"domType": "input",
"dataType": "int",
"value": 1,
"description": "",
"validators": [
{
"helpText": "请输入非零的正整数",
"regex": "^[1-9]\\d*$"
}
],
"choices": [],
"parameterGroup": "hardware"
}
]
},
"edges": []
},
{
"id": "批式Node ID (后端定义)",
"title": "gromaxRun",
"description": "这是一段gromaxRun算子的描述",
"version": "1.0.0",
"allVersions": ["1.0.0"],
"updateTime": "2022/07/05",
"tags": ["公共算子"],
"position": {
"x": null,
"y": null
},
"type": "BATCH",
"parentNode": "",
"data": {
"status": "wait",
"parameters": [
{
"id": "",
"show": true,
"name": "tpr_path",
"required": true,
"domType": "pathSelect",
"dataType": "string",
"value": "",
"description": "",
"validators": [],
"choices": [],
"parameterGroup": "in"
},
{
"id": "",
"show": true,
"name": "pdb_in",
"required": true,
"domType": "fileSelect",
"dataType": "file",
"value": "",
"description": "",
"validators": [
{
"helpText": "请选择pdb文件作为输入",
"regex": "^.[p][d][b]$"
}
],
"choices": [],
"parameterGroup": "in"
},
{
"id": "",
"show": true,
"name": "result_out",
"required": true,
"domType": "input",
"dataType": "string",
"value": "fep",
"description": "",
"validators": [
{
"helpText": "仅支持中文、英文、数据以及下划线",
"regex": "^[\u4E00-\u9FA5A-Za-z0-9_]+$"
}
],
"choices": [],
"parameterGroup": "out"
}
]
},
"edges": []
},
{
"id": "批式Node ID (后端定义)",
"title": "pdbToTpr",
"description": "这是一段pdbToTpr算子的描述",
"version": "1.0.0",
"allVersions": ["1.0.0"],
"updateTime": "2022/07/05",
"tags": ["公共算子"],
"position": {
"x": null,
"y": null
},
"type": "BATCH",
"parentNode": "",
"data": {
"status": "wait",
"parameters": [
{
"id": "",
"show": true,
"name": "pdb_in",
"required": true,
"domType": "fileSelect",
"dataType": "file",
"value": "",
"description": "",
"validators": [
{
"helpText": "请选择pdb文件作为输入",
"regex": "^.[p][d][b]$"
}
],
"choices": [],
"parameterGroup": "in"
},
{
"id": "",
"show": true,
"name": "pdb_out",
"required": true,
"domType": "input",
"dataType": "file",
"value": "",
"description": "",
"validators": [
{
"helpText": "仅支持中文、英文、数据以及下划线",
"regex": "^[\u4E00-\u9FA5A-Za-z0-9_]+$"
}
],
"choices": [],
"parameterGroup": "out"
},
{
"id": "",
"show": false,
"name": "tpr_path",
"required": true,
"domType": "pathSelect",
"dataType": "string",
"value": "",
"description": "",
"validators": [],
"choices": [],
"parameterGroup": "out"
}
]
},
"edges": []
}
]
.swBox { .swBox {
position: fixed; position: fixed;
z-index: 1000; z-index: 1000;
top: 0; top: 0;
left: 0; left: 0;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
background-color: RGBA(247, 248, 250, 1); background-color: RGBA(247, 248, 250, 1);
overflow-y: scroll; overflow-y: scroll;
} }
.swHeader { .swHeader {
z-index: 1001; z-index: 1001;
position: sticky; position: sticky;
top: 0; top: 0;
height: 56px; height: 56px;
background-color: #fff; background-color: #fff;
box-shadow: 0px 3px 10px 0px rgba(0, 24, 57, 0.04); box-shadow: 0px 3px 10px 0px rgba(0, 24, 57, 0.04);
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0 24px; padding: 0 24px;
} }
.swHeaderLeft { .swHeaderLeft {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
} }
.swContent { .swContent {
display: flex; display: flex;
height: calc(100vh - 56px); height: calc(100vh - 56px);
} }
.swFormBox { .swFormBox {
background-color: #fff; background-color: #fff;
border-right: 1xp solid rgba(235, 237, 240, 1); border-right: 1xp solid rgba(235, 237, 240, 1);
width: 608px; width: 360px;
overflow-y: scroll; /* overflow-y: scroll; */
box-sizing: border-box; box-sizing: border-box;
padding: 36px; }
} .swFlowBox {
.swFlowBox { flex: 1;
flex: 1; height: calc(100vh - 56px);
height: calc(100vh - 56px); }
}
\ No newline at end of file
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-21 20:03:56 * @Date: 2022-06-21 20:03:56
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-05 16:31:28 * @LastEditTime: 2022-07-06 18:35:24
* @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
import React, { useState } from "react"; import React, { useState } from "react";
import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew"; import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
...@@ -14,64 +14,61 @@ import { useLocation, useNavigate } from "react-router-dom"; ...@@ -14,64 +14,61 @@ import { useLocation, useNavigate } from "react-router-dom";
import MyPopconfirm from "@/components/mui/MyPopconfirm"; import MyPopconfirm from "@/components/mui/MyPopconfirm";
import ButtonComponent from "@/components/mui/Button"; import ButtonComponent from "@/components/mui/Button";
import { ITemplateConfig } from "../Project/ProjectSubmitWork/interface"; import { ITemplateConfig } from "../Project/ProjectSubmitWork/interface";
import OperatorList from "./components/OperatorList";
import styles from './index.module.css' import styles from "./index.module.css";
const WorkFlowEdit = () => { const WorkFlowEdit = () => {
const [templateConfigInfo, setTemplateConfigInfo] = const [templateConfigInfo, setTemplateConfigInfo] =
useState<ITemplateConfig>(); useState<ITemplateConfig>();
const location: any = useLocation(); const location: any = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
return (
return ( <div className={styles.swBox}>
<div className={styles.swBox}> <div className={styles.swHeader}>
<div className={styles.swHeader}> <div className={styles.swHeaderLeft}>
<div className={styles.swHeaderLeft}> <MyPopconfirm
<MyPopconfirm title="返回后,当前页面已填写内容将不保存,确认返回吗?"
title="返回后,当前页面已填写内容将不保存,确认返回吗?" onConfirm={() => console.log(11)}
onConfirm={()=>console.log(11)} >
> <IconButton
<IconButton color="primary"
color="primary" // onClick={() => handleGoBack()}
// onClick={() => handleGoBack()} aria-label="upload picture"
aria-label="upload picture" component="span"
component="span" size="small"
size="small" >
> <ArrowBackIosNewIcon
<ArrowBackIosNewIcon sx={{
sx={{ color: "rgba(194, 198, 204, 1)",
color: "rgba(194, 198, 204, 1)", width: "12px",
width: "12px", height: "12px",
height: "12px", }}
}} />
/> </IconButton>
</IconButton> </MyPopconfirm>
</MyPopconfirm> </div>
</div> <div className={styles.swHeaderRight}>
<div className={styles.swHeaderRight}> <MyPopconfirm
<MyPopconfirm title="提交前请先确认参数填写无误,确认提交吗?"
title="提交前请先确认参数填写无误,确认提交吗?" onConfirm={() => console.log(2)}
onConfirm={()=>console.log(2)} >
> <ButtonComponent
<ButtonComponent text="保存"
text="保存" // click={handleSubmitForm}
// click={handleSubmitForm} ></ButtonComponent>
></ButtonComponent> </MyPopconfirm>
</MyPopconfirm> </div>
</div> </div>
</div> <div className={styles.swContent}>
<div className={styles.swContent}> <div className={styles.swFormBox}>
<div className={styles.swFormBox}> <OperatorList />
左侧 </div>
</div> <div className={styles.swFlowBox}>右侧</div>
<div className={styles.swFlowBox}> </div>
右侧 </div>
</div> );
</div>
</div>
);
}; };
export default WorkFlowEdit; export default WorkFlowEdit;
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