Commit 22733b60 authored by 吴永生#A02208's avatar 吴永生#A02208

feat: 查看模版接口联调

parent 2e9b040a
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-13 09:56:57
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-13 09:59:29
* @LastEditTime: 2022-06-23 14:42:26
* @FilePath: /bkunyun/src/api/api_manager.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -29,6 +29,7 @@ const RESTAPI = {
API_WORKBENCH_DELETE_TEMPLATE: `${BACKEND_API_URI_PREFIX}/cpp/workbench/project/workflowspec`, //项目管理员-删除工作流模板
API_WORKBENCH_ADD_TEMPLATE_LIST: `${BACKEND_API_URI_PREFIX}/cpp/workbench/product/workflowspec`, //项目管理员-添加工作流模板-模板列表
API_WORKBENCH_ADD_TEMPLATE: `${BACKEND_API_URI_PREFIX}/cpp/workbench/project/workflowspec`, //项目管理员-添加工作流模板-提交
API_FETCH_TEMPLATE_INFO: `${BACKEND_API_URI_PREFIX}/cpp/workbench/workflowspec`, //项目管理员-添加工作流模板-提交
};
export default RESTAPI;
......@@ -176,7 +176,7 @@ export function useHttp(raw?: boolean) {
export default rawHttp;
export interface IResponse<T> {
errorCode: number;
massage: string;
errorCode?: number;
massage?: string;
data: T;
}
......@@ -202,6 +202,16 @@ const getDataFileDelPackage = (params: getDataFileDelPackageParams) => {
});
};
// 点击使用模版,获取模版数据
const fetchTemplateConfigInfo = (params: {id: string}) => {
return request({
url: `${Api.API_FETCH_TEMPLATE_INFO}/${params.id}`,
method: "get",
});
};
export {
current,
menu,
......@@ -218,4 +228,5 @@ export {
getDataFileMovePackage,
getDataFileDel,
getDataFileDelPackage,
fetchTemplateConfigInfo
};
......@@ -14,14 +14,14 @@ import fileSelectIcon from "@/assets/project/fileSelect.svg";
import questionMark from "@/assets/project/questionMark.svg";
type ConfigFormProps = {
templateConfigInfo: ITemplateConfig;
templateConfigInfo?: ITemplateConfig;
setParameter: any;
};
const ConfigForm = (props: ConfigFormProps) => {
const { templateConfigInfo, setParameter } = props;
const [name, setName] = useState<string>(
`${templateConfigInfo.title}_${moment(new Date()).format(
`${templateConfigInfo?.title}_${moment(new Date()).format(
"YYYY_MM_DD_HH_mm"
)}`
); // 任务名称
......@@ -47,7 +47,7 @@ const ConfigForm = (props: ConfigFormProps) => {
const renderTasks: IRenderTasks = useMemo(() => {
const result: IRenderTasks = [];
templateConfigInfo.tasks.forEach((task, taskIndex) => {
templateConfigInfo?.tasks.forEach((task, taskIndex) => {
if (task.type === "BATCH") {
result.push({ ...task, flows: [] });
} else {
......@@ -126,7 +126,7 @@ const ConfigForm = (props: ConfigFormProps) => {
<div className={styles.templateDescBox}>
<div className={styles.templateDescTitle}>模板描述</div>
<div className={styles.templateDesc}>
{templateConfigInfo.description}
{templateConfigInfo?.description || ""}
</div>
</div>
<div
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-21 15:25:25
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-22 10:40:30
* @LastEditTime: 2022-06-23 18:20:50
* @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/WorkFlow/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -10,7 +10,7 @@ import Flow from "../../components/Flow";
import { ITemplateConfig } from "../interface";
interface IProps {
templateConfigInfo: ITemplateConfig;
templateConfigInfo?: ITemplateConfig;
}
const WorkFlow = (props: IProps) => {
const { templateConfigInfo } = props;
......
......@@ -2,13 +2,12 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-21 20:03:56
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-22 17:29:16
* @LastEditTime: 2022-06-23 18:25:23
* @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { useState } from "react";
import { useEffect, useState } from "react";
import styles from "./index.module.css";
import { templateConfigJson } from "./mock";
import ConfigForm from "./ConfigForm";
import WorkFlow from "./WorkFlow";
import ButtonComponent from "@/components/mui/Button";
......@@ -16,11 +15,28 @@ import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
import IconButton from "@mui/material/IconButton";
import { ITemplateConfig } from "./interface";
import _ from "lodash";
import useMyRequest from "@/hooks/useMyRequest";
import { fetchTemplateConfigInfo } from "@/api/project_api";
import { useLocation } from "react-router-dom";
import { IResponse } from "@/api/http";
const ProjectSubmitWork = () => {
const [templateConfigInfo, setTemplateConfigInfo] = useState<ITemplateConfig>(
templateConfigJson as any
);
const [templateConfigInfo, setTemplateConfigInfo] =
useState<ITemplateConfig>();
const location: any = useLocation();
/** 获取模版数据 */
const { run } = useMyRequest(fetchTemplateConfigInfo, {
onSuccess: (res: IResponse<ITemplateConfig>) => {
setTemplateConfigInfo(res.data);
},
});
useEffect(() => {
run({
id: location?.state?.id,
});
}, [location?.state?.id, run]);
const setParameter = (value: any, taskId: string, parameterName: string) => {
const result: ITemplateConfig = _.cloneDeep(templateConfigInfo);
......@@ -60,18 +76,18 @@ const ProjectSubmitWork = () => {
</IconButton>
<div className={styles.swTemplateTitle}>
{templateConfigInfo.title}
{templateConfigInfo?.title}
</div>
<div className={styles.swTemplateVersionBox}>
<span className={styles.swHeaderLable}>版本:</span>
<span className={styles.swHeaderValue}>
{templateConfigInfo.version}
{templateConfigInfo?.version}
</span>
</div>
<div className={styles.swTemplateUpdateTimeBox}>
<span className={styles.swHeaderLable}>更新时间:</span>
<span className={styles.swHeaderValue}>
{templateConfigInfo.updateTime}
{templateConfigInfo?.updateTime}
</span>
</div>
<div className={styles.swHeaderGoback}></div>
......
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-17 14:48:57
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-23 14:53:30
* @FilePath: /bkunyun/src/views/Project/ProjectWorkbench/workbenchTemplate/components/templateBox.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { memo, useCallback, useEffect, useMemo, useState } from "react";
import styles from "../index.module.css";
import { Box, Typography } from "@mui/material";
import Button from "@mui/material/Button";
import usePass from "@/hooks/usePass";
import { useNavigate } from "react-router-dom";
const TemplateBox = (props: any) => {
const info = props.data
const isPass = usePass();
const info = props.data;
const isPass = usePass();
const navigate = useNavigate();
const addTemplateBlock = useCallback(
(id: string) => {
navigate(`/product/cadd/projectSubmitWork`, {
state: { id },
});
},
[navigate]
);
return (
<Box className={styles.templateBlock} >
<Box>
<Typography sx={{ fontSize: '14px', fontWeight: '600', color: '#1E2633', marginBottom: "4px", overflow: 'hidden', textOverflow: 'ellipsis' }}>{info.title}</Typography>
<Box sx={{ display: 'flex', marginBottom: "8px" }} >
<Typography sx={{ fontSize: '12px', fontWeight: '400', color: '#1370FF', marginRight: "24px" }}>版本:{info.version}</Typography>
<Typography sx={{ fontSize: '12px', fontWeight: '400', color: '#1370FF' }}>更新时间:{info.updateTime}</Typography>
</Box>
<Typography className={styles.templateDescText} >{info.description}</Typography>
</Box>
<Box sx={{
display: 'flex', justifyContent: 'end'
}} >
{
isPass("PROJECT_WORKBENCH_FLOES_USE", 'MANAGER') && <Button
style={{ backgroundColor: "#F0F2F5", color: "#565C66" }}
variant="contained"
onClick={() => { props.startDialog(info.id) }}
size="small"
>
删除模版
</Button>
}
{
isPass("PROJECT_WORKBENCH_FLOES_USE", 'USER') && <Button
style={{ backgroundColor: "#1370FF", marginLeft: "12px" }}
variant="contained"
// onClick={addTemplateBlock}
size="small"
>
使用模版
</Button>
}
</Box>
return (
<Box className={styles.templateBlock}>
<Box>
<Typography
sx={{
fontSize: "14px",
fontWeight: "600",
color: "#1E2633",
marginBottom: "4px",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
{info.title}
</Typography>
<Box sx={{ display: "flex", marginBottom: "8px" }}>
<Typography
sx={{
fontSize: "12px",
fontWeight: "400",
color: "#1370FF",
marginRight: "24px",
}}
>
版本:{info.version}
</Typography>
<Typography
sx={{ fontSize: "12px", fontWeight: "400", color: "#1370FF" }}
>
更新时间:{info.updateTime}
</Typography>
</Box>
);
<Typography className={styles.templateDescText}>
{info.description}
</Typography>
</Box>
<Box
sx={{
display: "flex",
justifyContent: "end",
}}
>
{isPass("PROJECT_WORKBENCH_FLOES_USE", "MANAGER") && (
<Button
style={{ backgroundColor: "#F0F2F5", color: "#565C66" }}
variant="contained"
onClick={() => {
props.startDialog(info.id);
}}
size="small"
>
删除模版
</Button>
)}
{isPass("PROJECT_WORKBENCH_FLOES_USE", "USER") && (
<Button
style={{ backgroundColor: "#1370FF", marginLeft: "12px" }}
variant="contained"
onClick={() => addTemplateBlock(info.id)}
size="small"
>
使用模版
</Button>
)}
</Box>
</Box>
);
};
export default memo(TemplateBox);
.batchNode {
background-color: #fff;
border-radius: 4px;
padding: 16px;
/* padding: 12px 20px; */
border: 1px solid #e6e8eb;
border-left: 4px solid #e6e8eb;
display: flex;
align-items: center;
}
.selectBatchNode {
......@@ -11,7 +13,7 @@
}
.batchRotate {
transform: rotate(-90deg);
transform: translateX(-50%) rotate(-90deg);
}
.flowNode {
......
import { ITemplateConfig, IEdge } from "../../ProjectSubmitWork/interface";
import {
ITemplateConfig,
IEdge,
ITask,
} from "../../ProjectSubmitWork/interface";
import ReactFlow, {
addEdge,
MiniMap,
Controls,
Background,
......@@ -8,11 +11,13 @@ import ReactFlow, {
useEdgesState,
Handle,
Position,
ReactFlowProps,
} from "react-flow-renderer";
import { useCallback, useMemo } from "react";
import classNames from "classnames";
import { IBatchNode, ILine } from "./interface";
import styles from "./index.module.css";
import classNames from "classnames";
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-22 10:15:22
......@@ -21,33 +26,49 @@ import classNames from "classnames";
* @FilePath: /bkunyun/src/views/Project/components/Flow/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
interface IProps {
data: ITemplateConfig;
interface IProps extends ReactFlowProps {
data?: ITemplateConfig;
}
const BatchNode = (props: any) => {
/** 自定义batch节点 */
const BatchNode = (props: IBatchNode) => {
const { data } = props;
const { dotStatus, style, isFlowNode, label } = data;
return (
<div
className={classNames({
[styles.batchNode]: true,
[styles.selectBatchNode]: false,
})}
style={data.style}
style={style}
title="2222"
>
<Handle type="target" position={Position.Top} />
{dotStatus?.isInput ? (
<Handle
style={{ background: "#fff ", border: "1px solid #D1D6DE" }}
type="target"
position={Position.Top}
/>
) : null}
<div
className={classNames({
[styles.batchRotate]: false,
[styles.batchRotate]: isFlowNode,
})}
>
{data.label || ""}
{label || ""}
</div>
<Handle type="source" position={Position.Bottom} id="a" />
{dotStatus?.isOutput ? (
<Handle
style={{ background: "#fff ", border: "1px solid #D1D6DE" }}
type="source"
position={Position.Bottom}
/>
) : null}
</div>
);
};
/** 自定义flow节点 */
const FlowNode = (props: any) => {
const { data } = props;
return (
......@@ -56,9 +77,22 @@ const FlowNode = (props: any) => {
[styles.flowNode]: true,
})}
>
<Handle type="target" position={Position.Top} />
<div>{data.label || ""}</div>
<Handle type="source" position={Position.Bottom} id="a" />
{data?.dotStatus?.isInput ? (
<Handle
style={{ background: "#C2C6CC " }}
type="target"
position={Position.Top}
/>
) : null}
<div>{data?.label || ""}</div>
{data?.dotStatus?.isOutput ? (
<Handle
style={{ background: "#C2C6CC " }}
type="source"
position={Position.Bottom}
id="a"
/>
) : null}
</div>
);
};
......@@ -66,17 +100,53 @@ const FlowNode = (props: any) => {
const Flow = (props: IProps) => {
const { data } = props;
/** 自定义的节点类型 */
const nodeTypes = useMemo(() => {
return { batchNode: BatchNode, flowNode: FlowNode };
}, []);
const getBatchStyle = useCallback(
/** 获取是否有输入节点或者是否有输出节点 */
const nodesInputAndOutputStatus = useCallback(
(id: string) => {
/** 所有的连线 */
const lineArr: IEdge[] = [];
data?.tasks?.length &&
data?.tasks.forEach((item) => {
lineArr.push(...item.edges);
});
/** 所有的输入节点ID */
const isInput = lineArr?.some((item) => item.target === id);
/** 所有的输出节点ID */
const isOutput = lineArr?.some((item) => item.source === id);
return {
isInput,
isOutput,
};
},
[data?.tasks]
);
/** 获取是否有流节点 */
const isFlowNode = useCallback(
(id: string) => {
return (
data?.tasks?.length &&
data?.tasks?.some((item) => {
return item.parentNode === id;
})
);
},
[data?.tasks]
);
/** 通过子flow节点计算batch节点的样式 */
const getBatchStyle = useCallback(
(value: ITask) => {
const positionXArr: number[] = [];
const positionYArr: number[] = [];
data?.tasks?.length &&
data?.tasks?.forEach((item) => {
if (item.parentNode === id) {
if (item.parentNode === value.id) {
positionXArr.push(item.position?.x || 0);
positionYArr.push(item.position?.y || 0);
}
......@@ -88,14 +158,14 @@ const Flow = (props: IProps) => {
return a - b;
});
let width = 176,
height = 44;
height = 22;
if (positionXArr?.length) {
const val = positionXArr[positionXArr.length - 1] - positionXArr[0];
width = val ? val : width;
const val = positionXArr[positionXArr.length - 1] + 150;
width = val > 176 ? val : width;
}
if (positionXArr?.length) {
const val = positionYArr[positionYArr.length - 1] - positionYArr[0];
height = val ? val : height;
const val = positionYArr[positionYArr.length - 1];
height = val > 22 ? val : height;
}
return {
width,
......@@ -105,34 +175,43 @@ const Flow = (props: IProps) => {
[data?.tasks]
);
/** 生成初始化node节点 */
const initialNodes = useMemo(() => {
const val: any = [];
data?.tasks?.length &&
data?.tasks.forEach((item) => {
if (item.type === "BATCH") {
console.log(getBatchStyle(item.id));
}
val.push({
id: item.id,
type: item.type === "BATCH" ? "batchNode" : "flowNode",
data: { label: item.title || "", style: getBatchStyle(item.id) },
data: {
label: item.title || "",
/** 是否有流节点 */
isFlowNode: isFlowNode(item.id),
/** 输入输出圆点状态 */
dotStatus: nodesInputAndOutputStatus(item.id),
/** 样式 */
style: {
...getBatchStyle(item),
padding: isFlowNode(item.id) ? "20px" : "12px 20px",
},
},
position: item.position,
// style: { width: 176, height: 500 },
...(item.type === "BATCH" ? { style: getBatchStyle(item.id) } : {}),
...(item.type === "BATCH" ? { style: { zIndex: -1 } } : {}),
...(item.parentNode ? { parentNode: item.parentNode } : {}),
...(item.type === "BATCH" ? { extent: "parent" } : {}),
});
});
return val;
}, [data?.tasks, getBatchStyle]);
}, [data?.tasks, getBatchStyle, isFlowNode, nodesInputAndOutputStatus]);
/** 生成初始化的连线节点 */
const initialEdges = useMemo(() => {
const val: any = [];
const val: ILine[] = [];
data?.tasks?.length &&
data?.tasks.forEach((item) => {
val.push(...item.edges);
});
val.map((item: IEdge) => {
val.map((item: ILine) => {
return {
id: item.id,
label: item.label,
......@@ -143,43 +222,18 @@ const Flow = (props: IProps) => {
return val;
}, [data]);
console.log(initialEdges, initialNodes, 11111);
const [nodes, setNodes, onNodesChange] = useNodesState<any>(initialNodes);
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = (params: any) => setEdges((eds) => addEdge(params, eds));
const onInit = (reactFlowInstance: any) =>
console.log("flow loaded:", reactFlowInstance);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
onInit={onInit}
fitView
attributionPosition="top-right"
proOptions={{ hideAttribution: true, account: "" }}
nodeTypes={nodeTypes}
{...props}
>
<MiniMap
// nodeStrokeColor={(n) => {
// if (n.style?.background) return n.style.background;
// if (n.type === "input") return "#0041d0";
// if (n.type === "output") return "#ff0072";
// if (n.type === "default") return "#1a192b";
// return "#eee";
// }}
// nodeColor={(n) => {
// if (n.style?.background) return n.style.background;
// return "#fff";
// }}
nodeBorderRadius={2}
/>
<Controls />
<Background color="#aaa" gap={16} />
</ReactFlow>
......
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-23 11:00:29
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-23 11:19:59
* @FilePath: /bkunyun/src/views/Project/components/Flow/interface.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { CSSProperties } from "react";
/** 线的参数 */
export interface ILine {
id: string,
label: string,
source: string,
target: string,
}
export interface IDotStatus {
isInput: boolean;
isOutput: boolean
}
export interface IBatchNodeData {
label: string;
isFlowNode: boolean;
dotStatus: IDotStatus;
style?: CSSProperties;
}
export interface IBatchNode {
data: IBatchNodeData
}
\ No newline at end of file
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