From 278d50faa6eda7622b2c165a5b1d240a356050bc Mon Sep 17 00:00:00 2001 From: yms Date: Tue, 11 Mar 2025 18:17:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=96=B0=E9=9C=80?= =?UTF-8?q?=E6=B1=82=E7=9A=84=E5=BC=80=E5=8F=91=E5=B7=A5=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/customForm/components/Radio/index.tsx | 9 +- src/pages/Setting/Flow/X6ActionContext.ts | 2 +- .../components/conditionSelect.module.css | 14 + .../Flow/components/conditionSelect.tsx | 124 +++++++++ src/pages/Setting/Flow/index.tsx | 108 +++++++- .../Setting/Flow/nodes/baseCondition.tsx | 89 +++++++ src/pages/Setting/Flow/nodes/condition.tsx | 6 +- src/pages/Setting/Flow/nodes/conditionBtn.tsx | 12 +- src/pages/Setting/Flow/nodes/gateway.tsx | 3 + .../Flow/setting/applyForNodeSetting.tsx | 4 +- .../baseConditionNodeSetting.module.css | 11 + .../Flow/setting/baseConditionNodeSetting.tsx | 78 ++++++ .../Flow/setting/examineNodeSetting.tsx | 130 ++++++---- .../setting/examineOptionalNodeSetting.tsx | 12 + src/pages/Setting/Flow/util.ts | 243 +++++++++++++----- src/pages/User/SubmitApplication/index.tsx | 4 + vite.config.ts | 18 +- 17 files changed, 737 insertions(+), 130 deletions(-) create mode 100644 src/pages/Setting/Flow/components/conditionSelect.module.css create mode 100644 src/pages/Setting/Flow/components/conditionSelect.tsx create mode 100644 src/pages/Setting/Flow/nodes/baseCondition.tsx create mode 100644 src/pages/Setting/Flow/setting/baseConditionNodeSetting.module.css create mode 100644 src/pages/Setting/Flow/setting/baseConditionNodeSetting.tsx diff --git a/src/customForm/components/Radio/index.tsx b/src/customForm/components/Radio/index.tsx index d8004c6..76b03f9 100644 --- a/src/customForm/components/Radio/index.tsx +++ b/src/customForm/components/Radio/index.tsx @@ -7,7 +7,12 @@ interface FormRadioProps { value?: any; } -const FormRadio: React.FC = ({ option, onChange, value }) => { +const FormRadio: React.FC = ({ + option, + onChange, + value, + ...other +}) => { let optionTemp: any = [...option]; optionTemp = optionTemp.map((v: any) => { if (typeof v === "string") return { label: v, value: v }; @@ -18,7 +23,7 @@ const FormRadio: React.FC = ({ option, onChange, value }) => { {optionTemp.map((item: any) => { return ( - + {item.label} ); diff --git a/src/pages/Setting/Flow/X6ActionContext.ts b/src/pages/Setting/Flow/X6ActionContext.ts index 532bc98..55afd96 100644 --- a/src/pages/Setting/Flow/X6ActionContext.ts +++ b/src/pages/Setting/Flow/X6ActionContext.ts @@ -1,7 +1,7 @@ import React from "react"; const X6ActionContext = React.createContext<{ - addCondition?: (id: string) => void; + addCondition?: (id: string, base?: boolean) => void; openSetting?: (id: string) => void; actionNode?: any; onDelete?: (id: string) => void; diff --git a/src/pages/Setting/Flow/components/conditionSelect.module.css b/src/pages/Setting/Flow/components/conditionSelect.module.css new file mode 100644 index 0000000..a39fba8 --- /dev/null +++ b/src/pages/Setting/Flow/components/conditionSelect.module.css @@ -0,0 +1,14 @@ +.itemContainer { + border: 1px solid #ccc; + padding: 16px; + margin-bottom: 16px; + border-radius: 8px; + position: relative; +} + +.closeBtn { + position: absolute; + right: 8px; + top: 8px; + z-index: 100; +} diff --git a/src/pages/Setting/Flow/components/conditionSelect.tsx b/src/pages/Setting/Flow/components/conditionSelect.tsx new file mode 100644 index 0000000..694a1fc --- /dev/null +++ b/src/pages/Setting/Flow/components/conditionSelect.tsx @@ -0,0 +1,124 @@ +import { CloseOutlined, PlusOutlined } from "@ant-design/icons"; +import { Button, Checkbox, Select } from "antd"; +import { useEffect, useMemo, useState } from "react"; +import { useFormContext } from "../.."; +import styles from "./conditionSelect.module.css"; +import { isEqual } from "lodash"; +import { useUpdateEffect } from "ahooks"; + +interface ConditionSelectProps { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onChange: (value: any) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any; +} + +const ConditionSelect = (props: ConditionSelectProps) => { + const { onChange, value } = props; + const [conditions, setConditions] = useState< + { key?: string; value?: string[]; name?: string }[] + >([]); + const { formConfigList } = useFormContext(); + + const add = () => { + setConditions([ + ...conditions, + { key: undefined, value: undefined, name: undefined }, + ]); + }; + + useEffect(() => { + if (value && !isEqual(value, conditions)) { + setConditions(value); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [value]); + const SelectOptions = useMemo(() => { + const filterList = formConfigList?.filter((v) => { + return v.componentType === "Radio" || v.componentType === "Checkbox"; + }); + + return filterList?.map((v) => { + return { + label: v.label, + value: v.key, + option: v.componentOption.option || v.componentOption.options, + }; + }); + }, [formConfigList]); + + const onConditionChange = (value: string, index: number) => { + const newConditions = [...conditions]; + newConditions[index].key = value; + setConditions(newConditions); + }; + + const findOption = (key?: string) => { + return SelectOptions.find((v) => v.value === key)?.option; + }; + + const onValueChange = (value: string[], index: number) => { + const newConditions = [...conditions]; + newConditions[index].value = value; + setConditions(newConditions); + }; + + /** 删除条件 */ + const onDelete = (index: number) => { + setConditions((prev) => { + return prev.filter((_, i) => { + return i !== index; + }); + }); + }; + /** 抛出更改 */ + useUpdateEffect(() => { + if (!isEqual(value, conditions)) { + onChange(conditions); + } + }, [conditions]); + + return ( +
+ {conditions?.map((v, index) => { + return ( +
+ +
当前选择条件
+ +
值为
+ {v.key ? ( + onValueChange(v, index)} + > + ) : ( +
暂无条件
+ )} +
+ ); + })} + +
+ ); +}; + +export default ConditionSelect; diff --git a/src/pages/Setting/Flow/index.tsx b/src/pages/Setting/Flow/index.tsx index 2e72d18..a48d030 100644 --- a/src/pages/Setting/Flow/index.tsx +++ b/src/pages/Setting/Flow/index.tsx @@ -27,11 +27,14 @@ import { generateUUID } from "@/customForm/utils"; import { createRoot } from "react-dom/client"; import ConditionBtn from "./nodes/conditionBtn"; import ConditionNode from "./nodes/condition"; +import BaseConditionNode from "./nodes/baseCondition"; import ConditionNodeSetting from "./setting/conditionNodeSetting"; import ExamineOptionalNodeSetting from "./setting/examineOptionalNodeSetting"; import useGlobalData from "@/store/useGlobalData"; import X6ActionContext from "./X6ActionContext"; +import BaseConditionNodeSetting from "./setting/baseConditionNodeSetting"; + const X6ReactPortalProvider = Portal.getProvider(); register({ @@ -65,6 +68,15 @@ register({ effect: ["data"], component: ConditionNode, }); + +register({ + shape: "custom-base-condition-node", + width: 297, + height: 108, + effect: ["data"], + component: BaseConditionNode, +}); + const Flow = () => { /** 当前操作对象 */ const [actionNode, setActionNode] = useState(getDefaultNodeByType(1)); @@ -120,6 +132,7 @@ const Flow = () => { const renderGraph = () => { const newX6Data = buildLineX6data(originalXNodesData.current); + console.log(newX6Data); setX6Data(newX6Data); }; @@ -156,6 +169,10 @@ const Flow = () => { interacting: { nodeMovable: false, }, + mousewheel: { + enabled: true, + modifiers: ["ctrl", "meta"], + }, //@ts-ignore onEdgeLabelRendered: (args) => { @@ -199,6 +216,9 @@ const Flow = () => { if (type === "可选审批") { addOptionalNode(edgeData); } + if (type === "条件选择") { + addBaseConditionElement(edgeData); + } }; /** 增加可选审批节点 */ @@ -281,7 +301,7 @@ const Flow = () => { renderGraph(); }; - /** 增加条件节点 */ + /** 增加街镇条件节点 */ const addConditionElement = (edgeData: { target: Node.Metadata; source: Node.Metadata; @@ -332,8 +352,68 @@ const Flow = () => { originalXNodesData.current = [ ...cloneX6Data, addCondition, + conditionsNodeData, condition1, + ]; + /** 渲染 */ + renderGraph(); + }; + + /** 增加基础条件节点 */ + const addBaseConditionElement = (edgeData: { + target: Node.Metadata; + source: Node.Metadata; + }) => { + const { source, target } = edgeData; + let cloneX6Data = cloneDeep(originalXNodesData.current); + + const nodeData = getDefaultNodeByType(7); + + const addCondition: Node.Metadata = { + id: generateUUID(), + shape: "custom-addCondition-node", + data: { + type: "addConditionBtn", + pid: source.id, + base: true, + }, + }; + + const conditionsNodeData = { + id: generateUUID(), + shape: "custom-base-condition-node", + data: { + conditionType: 0, + nodeType: 7, + pid: addCondition.id, + }, + }; + + const condition1: Node.Metadata = { + id: nodeData.elementId, + shape: "custom-base-condition-node", + data: { + ...nodeData, + elementAlias: "条件1", + conditionType: 1, + pid: addCondition.id, + }, + }; + + cloneX6Data = cloneX6Data.map((v) => { + if (v.id === target.id) { + v.data.pid = v.data.pid?.replace(source.id, conditionsNodeData.id); + } + if (v.id === "endEvent") { + v.data.pid = v.data.pid + "," + condition1.id; + } + return v; + }); + originalXNodesData.current = [ + ...cloneX6Data, + addCondition, conditionsNodeData, + condition1, ]; /** 渲染 */ renderGraph(); @@ -357,6 +437,8 @@ const Flow = () => { openNotification("已更新节点信息"); }; + console.log(originalXNodesData); + const onSave = () => { const metaData = mode === "edit" @@ -394,9 +476,18 @@ const Flow = () => { } } + if (data.nodeType === 7) { + if ( + data.conditionType !== 0 && + (!data.condition || data?.condition?.length == 0) + ) { + ErrObj.noStreet = `条件节点${data.conditionType}不能为空`; + } + } + if (data.nodeType === 1) { const scopeKeys = data.scopeOfApplication?.split(","); - console.log(data.scopeOfApplication); + data.scopeCodeOfApplication = scopeKeys ?.map((keys: string) => { return mainBodyTypes.find((body) => body.id === keys)?.name; @@ -451,15 +542,15 @@ const Flow = () => { }); }; /** @description 增加条件节点 */ - const addCondition = (nodeId: string) => { - const nodeData = getDefaultNodeByType(4); + const addCondition = (nodeId: string, base = false) => { + const nodeData = getDefaultNodeByType(base ? 6 : 4); let cloneX6Data = cloneDeep(originalXNodesData.current); const childrenNum = cloneX6Data.filter( (v) => v.data.pid === nodeId )?.length; const condition: Node.Metadata = { id: nodeData.elementId, - shape: "custom-condition-node", + shape: base ? "custom-base-condition-node" : "custom-condition-node", data: { ...nodeData, pid: nodeId, @@ -476,6 +567,7 @@ const Flow = () => { /** 渲染 */ renderGraph(); }; + /** @description 删除节点 */ const onDelete = (nodeId: string) => { console.log("onDelete", nodeId); @@ -521,7 +613,7 @@ const Flow = () => { } /** 删除条件 */ - if (nodeType === 4) { + if (nodeType === 4 || nodeType === 6) { const childNodes = nodeMap.childMap.get(pid) ?? []; if (childNodes.length > 2) { linksDelete(nodeId); @@ -535,6 +627,7 @@ const Flow = () => { linksDelete(nodeId); } } + /** 删除可选节点 */ if (nodeType === 5) { /** 先删除本身节点 */ @@ -595,6 +688,9 @@ const Flow = () => { onUpdate={onUpdate} /> )} + {actionNode?.nodeType === 7 && ( + + )} {!actionNode && (
diff --git a/src/pages/Setting/Flow/nodes/baseCondition.tsx b/src/pages/Setting/Flow/nodes/baseCondition.tsx new file mode 100644 index 0000000..4857143 --- /dev/null +++ b/src/pages/Setting/Flow/nodes/baseCondition.tsx @@ -0,0 +1,89 @@ +import { CloseOutlined, RightOutlined } from "@ant-design/icons"; +import classNames from "classnames"; +import styles from "./index.module.css"; +import { Node } from "@antv/x6"; + +import { Button } from "antd"; +import { useContext, useMemo } from "react"; +import X6ActionContext from "../X6ActionContext"; + +type BaseNodeProps = { + streetName?: string; + elementName: string; + elementAlias: string; + nodeType: 1 | 3 | 4 | 5; + conditionType?: number; +}; + +/** 条件节点 */ +const BaseConditionNode = ({ node }: { node: Node }) => { + const { conditionType, elementAlias } = node.getData(); + + const { openSetting, actionNode, onDelete } = useContext(X6ActionContext); + + /** 是否选中 */ + const active = actionNode?.elementId === node.id; + + const showClose = useMemo(() => { + if (conditionType === 0) { + return false; + } + return true; + }, [conditionType]); + + const getConditionTitle = () => { + if (conditionType === 0) { + return "默认条件"; + } + + return elementAlias; + }; + + const getConditionContent = () => { + if (conditionType === 0) { + return "未满足其他条件分支的情况,将使用默认流程"; + } + + return "请选择条件"; + }; + return ( +
+
+ +
+
{getConditionTitle()}
+ + {showClose && ( + + )} +
+
{ + if (conditionType) { + openSetting?.(node.id); + } + }} + > +
{getConditionContent()}
+ +
+
+ ); +}; + +export default BaseConditionNode; diff --git a/src/pages/Setting/Flow/nodes/condition.tsx b/src/pages/Setting/Flow/nodes/condition.tsx index 4408ee2..4cdd3ff 100644 --- a/src/pages/Setting/Flow/nodes/condition.tsx +++ b/src/pages/Setting/Flow/nodes/condition.tsx @@ -15,8 +15,8 @@ type BaseNodeProps = { conditionType?: number; }; -/** 基础节点 */ -const ConditionNode = ({ node }: { node: Node }) => { +/** 街镇条件节点 */ +const StreetConditionNode = ({ node }: { node: Node }) => { const { streetName, conditionType } = node.getData(); const { openSetting, actionNode, onDelete } = useContext(X6ActionContext); @@ -85,4 +85,4 @@ const ConditionNode = ({ node }: { node: Node }) => { ); }; -export default ConditionNode; +export default StreetConditionNode; diff --git a/src/pages/Setting/Flow/nodes/conditionBtn.tsx b/src/pages/Setting/Flow/nodes/conditionBtn.tsx index 59a52a1..ba523f1 100644 --- a/src/pages/Setting/Flow/nodes/conditionBtn.tsx +++ b/src/pages/Setting/Flow/nodes/conditionBtn.tsx @@ -6,8 +6,18 @@ import { Node } from "@antv/x6"; const ConditionBtn = ({ node }: { node: Node }) => { const { addCondition } = useContext(X6ActionContext); const id = node.id; + return ( -
addCondition!(id)}> +
{ + if (node.getData().base) { + addCondition!(id, true); + return; + } + addCondition!(id); + }} + > 添加条件
); diff --git a/src/pages/Setting/Flow/nodes/gateway.tsx b/src/pages/Setting/Flow/nodes/gateway.tsx index 7e83445..c4feb23 100644 --- a/src/pages/Setting/Flow/nodes/gateway.tsx +++ b/src/pages/Setting/Flow/nodes/gateway.tsx @@ -45,6 +45,9 @@ const GatewayNode = (props: GatewayNodeProps) => { + {edgeData.source.id === "startEvent" && ( + + +
+
+ ); +}; + +export default BaseConditionNodeSetting; diff --git a/src/pages/Setting/Flow/setting/examineNodeSetting.tsx b/src/pages/Setting/Flow/setting/examineNodeSetting.tsx index f21569e..736a121 100644 --- a/src/pages/Setting/Flow/setting/examineNodeSetting.tsx +++ b/src/pages/Setting/Flow/setting/examineNodeSetting.tsx @@ -3,8 +3,9 @@ import { ComponentsType } from "@/customForm/interfaces"; import { Button, Flex, Form, Input } from "antd"; import FormSelect from "../components/formSelect"; import styles from "./examineNodeSetting.module.css"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { isEqual } from "lodash"; +import { useBoolean } from "ahooks"; const ActionDescChange = (props: { value: string[]; @@ -57,51 +58,70 @@ interface ExamineNodeSettingProps { } const ExamineNodeSetting = (props: ExamineNodeSettingProps) => { const { onUpdate, value } = props; + const [showNextUser, { setFalse, setTrue }] = useBoolean(true); + const FormConfig = useMemo( + () => + [ + { + label: "审批节点名称", + componentType: "TextInput", + key: "elementAlias", + config: { + placeholder: "请输入", + size: "large", + }, + }, + { + label: "节点图标", + componentType: "ImgUpload", + key: "icon", + config: { + placeholder: "请上传图标", + }, + }, + { + label: "指定审批人", + componentType: "UserSelect", + key: "approverList", + config: { + placeholder: "请输入", + }, + }, + { + label: "审批通过方式", + componentType: "Radio", + key: "signType", + config: { + option: [ + { label: "会签(所有审批人通过)", value: 2 }, + { label: "或签(任意一人审批通过)", value: 3 }, + ], + }, + }, + { + label: "", + //@ts-ignore + component: , + key: "forwardAndReverseName", + }, - const FormConfig = [ - { - label: "审批节点名称", - componentType: "TextInput", - key: "elementAlias", - config: { - placeholder: "请输入", - size: "large", - }, - }, - { - label: "节点图标", - componentType: "ImgUpload", - key: "icon", - config: { - placeholder: "请上传图标", - }, - }, - { - label: "指定审批人", - componentType: "UserSelect", - key: "approverList", - config: { - placeholder: "请输入", - }, - }, - { - label: "", - //@ts-ignore - component: , - key: "forwardAndReverseName", - }, - { - label: "启用下一节点由当前审批人选择", - key: "enableSelectNextNodeUser", - componentType: "Checkbox", - }, - { - label: "审批内容", - //@ts-ignore - component: , - key: "examineKeys", - }, - ]; + { + label: "审批内容", + //@ts-ignore + component: , + key: "examineKeys", + }, + showNextUser + ? { + label: "启用下一节点由当前审批人选择", + key: "enableSelectNextNodeUser", + componentType: "Checkbox", + } + : undefined, + ].filter((v) => v), + + [showNextUser] + ); const [form] = Form.useForm(); useEffect(() => { @@ -109,6 +129,7 @@ const ExamineNodeSetting = (props: ExamineNodeSettingProps) => { form.setFieldsValue(value); } }, [value]); + const onFinish = (values: any) => { onUpdate(values); }; @@ -116,8 +137,23 @@ const ExamineNodeSetting = (props: ExamineNodeSettingProps) => {
添加审批节点
-
- {FormConfig.map((item) => { + { + // console.log(value); + if (value.signType === 2) { + setFalse(); + form.setFieldValue("enableSelectNextNodeUser", false); + } + if (value.signType === 3) { + setTrue(); + } + }} + layout="vertical" + onFinish={onFinish} + > + {FormConfig.map((item: any) => { return ( { placeholder: "请输入", }, }, + { + label: "审批通过方式(该节点只能为或签)", + componentType: "Radio", + key: "signType", + config: { + disabled: true, + option: [ + { label: "会签(所有审批人通过)", value: 2 }, + { label: "或签(任意一人审批通过)", value: 3 }, + ], + }, + }, { label: "", //@ts-ignore diff --git a/src/pages/Setting/Flow/util.ts b/src/pages/Setting/Flow/util.ts index 2a2447b..9e32b5e 100644 --- a/src/pages/Setting/Flow/util.ts +++ b/src/pages/Setting/Flow/util.ts @@ -14,6 +14,7 @@ function clearNodes(nodes: Node.Metadata[]) { const withoutBtnNodes = tempNode.filter((node) => { return node.data?.type !== "addConditionBtn"; }); + /** 将增加条件节点移除掉,同时连接子父节点 */ withoutBtnNodes.forEach((node) => { const pBtn = addConditionBtnNodes.find((btn) => { return btn.id === node.data.pid; @@ -22,7 +23,9 @@ function clearNodes(nodes: Node.Metadata[]) { node.data.pid = pBtn.data.pid; } }); + const { childMap } = NodeMap(withoutBtnNodes); + const conditionTypeNodes = withoutBtnNodes.filter((node) => { return node.data?.nodeType === 4; }); @@ -44,7 +47,6 @@ function clearNodes(nodes: Node.Metadata[]) { allChild?.map((v) => v.data?.streetId).filter((v) => v) ?? []; // debugger; const str = streetIds.reduce((pre, cur, index) => { - console.log(index, streetIds); if (index === 0) { if (streetIds.length == 1) { return "${" + "streetId!='" + cur + "'}"; @@ -69,11 +71,75 @@ function clearNodes(nodes: Node.Metadata[]) { } } }); + /** 处理条件节点 */ + const baseConditionNodes = withoutBtnNodes.filter((node) => { + return node.data?.nodeType === 7; + }); + + const withoutBaseConditionNodes = withoutBtnNodes.filter((node) => { + return node.data?.nodeType !== 7; + }); + + withoutBaseConditionNodes.forEach((node) => { + const pBtn = baseConditionNodes.find((btn) => { + const pids = node.data.pid?.split(",") ?? []; + return pids.includes(btn.id); + }); + if (pBtn) { + node.data.pid = replacePids(node.data.pid, pBtn.id!, pBtn.data.pid); + if (pBtn.data.conditionType === 0) { + const allChild = childMap.get(pBtn.data.pid!); + const conditions = + allChild + ?.map((v) => v.data?.condition) + .flat() + .filter((v) => v) ?? []; + + const str = conditions + .map( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (v: any) => + "!${flowHelper.containsAll(" + + v.key + + "," + + "'" + + v.value + + "'" + + ")" + ) + .join("&&"); + + node.data = { + ...node.data, + conditionExpression: "${" + str + "}", + }; + } else { + const str = pBtn.data.condition + .map( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (v: any) => + "flowHelper.containsAll(" + + v.key + + "," + + "'" + + v.value + + "'" + + ")" + ) + .join("&&"); + node.data = { + ...node.data, + conditionExpression: "${" + str + "}", + }; + } + } + }); - return withoutConditionTypeNodes; + return withoutBaseConditionNodes; } export function buildElementParams(nodes: Node.Metadata[]) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any let newResult = [] as any[]; const newNodes = clearNodes(nodes); console.log(newNodes, nodes); @@ -81,16 +147,20 @@ export function buildElementParams(nodes: Node.Metadata[]) { const createEdgeAndNode = (node: Node.Metadata, child: Node.Metadata) => { const childData = child.data; const nodeData = node.data; + /** 条件节点 */ - let edges: any[] = [ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const edges: any[] = [ { elementId: generateUUID(), elementName: "连接线", elementType: 3, + from: node.id, to: child.id, }, ]; + /** 父节点为条件节点 */ if (childData.conditionExpression) { edges[0] = { @@ -99,12 +169,12 @@ export function buildElementParams(nodes: Node.Metadata[]) { conditionExpression: childData.conditionExpression, }; } - /** 父节点为条件节点 */ + /** 父节点审批节点 */ if (nodeData.nodeType === 3 || nodeData.nodeType === 5) { edges[0] = { ...edges[0], conditionType: 2, - approveResult: 2, //固定 + approveResult: nodeData.signType === 2 ? 1 : 2, //固定 }; } @@ -168,6 +238,7 @@ const nodeWidthMapping = { "custom-gateWay-node": 40, "custom-addCondition-node": 124, "custom-condition-node": 297, + "custom-base-condition-node": 297, }; export function NodeMap(nodes: Node.Metadata[]) { @@ -198,6 +269,71 @@ export function NodeMap(nodes: Node.Metadata[]) { }; } +/** 按层级分组 */ +export function buildLevel( + nodes: Node.Metadata[], + childMap: Map +) { + const treeListByLevel: any[] = []; + // 记录每个节点的最大层级 + const nodeLevelMap = new Map(); + + // 第一次遍历:计算每个节点可能的最大层级 + const calculateMaxLevel = (node: any, level: number) => { + const nodeId = node.id!; + + // 如果节点已经有层级,且当前计算的层级更大,则更新层级 + if (nodeLevelMap.has(nodeId)) { + nodeLevelMap.set(nodeId, Math.max(nodeLevelMap.get(nodeId)!, level)); + } else { + nodeLevelMap.set(nodeId, level); + } + + // 递归处理子节点 + const children = childMap.get(nodeId) || []; + children.forEach((child) => { + calculateMaxLevel(child, level + 1); + }); + }; + + // 第二次遍历:根据最大层级放置节点 + const setData = (node: any) => { + const nodeId = node.id!; + const level = nodeLevelMap.get(nodeId)!; + + if (!treeListByLevel[level]) { + treeListByLevel[level] = []; + } + + // 避免重复添加节点 + if (!treeListByLevel[level].some((n) => n.id === nodeId)) { + treeListByLevel[level].push(node); + } + + // 递归处理子节点 + const children = childMap.get(nodeId) || []; + children.forEach((child) => { + setData(child); + }); + }; + + // 找出所有根节点并计算层级 + nodes.forEach((node) => { + if (!node.data.pid) { + calculateMaxLevel(node, 0); + } + }); + + // 再次遍历,根据计算好的层级放置节点 + nodes.forEach((node) => { + if (!node.data.pid) { + setData(node); + } + }); + + return treeListByLevel; +} + /** * 构建线数据 * 根据节点 pid 管理,从根节点开始更新每个节点的 y 和 x 值 @@ -212,63 +348,40 @@ export function buildLineX6data(nodes: Node.Metadata[]): { const { childMap } = NodeMap(nodes); const edges: Edge.Metadata[] = []; - let maxLevel = 0; - - // 递归更新每个节点的 x 和 y 值 - const updatePosition = (node: any, level: number, baseX: number) => { - // 设置 y 值,y 值为每层级相差 300 - node.y = level * 200; - // console.log(node); - // 获取子节点,并计算它们的总宽度 - let children = childMap.get(node.id) || []; - if (children.length === 0) { - return; - } - const nodeW = nodeWidthMapping[node.shape as keyof typeof nodeWidthMapping]; - const totalWidth = children.reduce((pre, cur, index) => { - const w = nodeWidthMapping[cur.shape as keyof typeof nodeWidthMapping]; - return pre + w + (index > 0 ? 100 : 0); - }, 0); // 子节点总宽度 - // 基于父节点的 x 值,将子节点居中排列 - const startX = baseX + nodeW / 2 - totalWidth / 2; // 从居中的位置开始 - if (level > maxLevel) { - maxLevel = level; - } - /** 排序子节点,如果字节点中存在父节点是审批节点的情况,考虑children最多存在两个,永远将审批节点右置 */ - children = children.sort((a) => { - return a.data.conditionId ? 1 : -1; + let levelList = buildLevel(nodes, childMap); + const getMaxLevelWidth = () => { + const treeWidth = levelList.map((items) => { + return items.reduce((pre: any, cur: any, index: number) => { + const nodeW = + nodeWidthMapping[cur.shape as keyof typeof nodeWidthMapping]; + return pre + nodeW + (index > 0 ? 100 : 0); + }, 0); }); + return Math.max(...treeWidth); + }; - children.forEach((child, index) => { - // 每个子节点的 x 值基于父节点的 x,并均匀排布 - const w = nodeWidthMapping[child.shape as keyof typeof nodeWidthMapping]; + const maxWidth = getMaxLevelWidth(); - child.x = startX + index * (w + 100); - updatePosition(child, level + 1, child.x); // 递归更新子节点的位置 - }); - }; + /** 按照层级更新每一个点位的位置 */ + levelList = levelList.map((list, level) => { + const spliceX = maxWidth / list.length; - // 初始化根节点(没有 pid 的节点) - nodes.forEach((node) => { - if (!node.data.pid) { - node.x = 0; // 根节点 x 从 0 开始,居中 - updatePosition(node, 0, node.x); // 从根节点开始递归更新位置 - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return list.map((node: any, index: number) => { + const nodeW = + nodeWidthMapping[node.shape as keyof typeof nodeWidthMapping]; + + const newD = { + ...node, + y: level * 200, + x: spliceX * index + spliceX / 2 - nodeW / 2, + }; + return newD; + }); }); - console.log(nodes); - // debugger; - const findEndNodes = () => { - return nodes.find((v) => v.id === "endEvent"); - }; - /** 重置结束事件的位置,一定位于层级最底层 */ - const resetEndNode = () => { - const node = findEndNodes(); - node!.y = maxLevel * 200 + 200; - node!.x = 0; - }; - resetEndNode(); + nodes = levelList.flat(); const createEdge = (node: Node.Metadata, child: Node.Metadata) => { if (node.data?.type === "addConditionBtn") { @@ -276,7 +389,7 @@ export function buildLineX6data(nodes: Node.Metadata[]): { source: node.id, target: child.id, router: { - name: "manhattan", + name: "metro", args: { startDirections: ["left", "right"], endDirections: ["top"], @@ -296,7 +409,7 @@ export function buildLineX6data(nodes: Node.Metadata[]): { source: node.id, target: child.id, router: { - name: "manhattan", + name: "metro", args: { startDirections: ["right"], endDirections: ["right"], @@ -356,7 +469,7 @@ export function buildLineX6data(nodes: Node.Metadata[]): { source: node.id, target: child.id, router: { - name: "manhattan", + name: "metro", args: { startDirections: ["bottom"], endDirections: ["top"], @@ -427,7 +540,7 @@ export function replacePids(pid: string, oldP: string, newP: string) { } /** 新增节点 */ -export function getDefaultNodeByType(type?: 1 | 2 | 3 | 4 | 5) { +export function getDefaultNodeByType(type?: 1 | 2 | 3 | 4 | 5 | 6 | 7) { switch (type) { case 1: return { @@ -482,7 +595,7 @@ export function getDefaultNodeByType(type?: 1 | 2 | 3 | 4 | 5) { nodeType: 4, elementType: 2, }; - case 5: { + case 5: return { elementId: generateUUID(), elementName: "可选审批节点", @@ -498,7 +611,15 @@ export function getDefaultNodeByType(type?: 1 | 2 | 3 | 4 | 5) { forwardAndReverseName: ["领导审批", "同意", "驳回"], examineKeys: "all", }; - } + + case 6: + return { + elementId: generateUUID(), + nodeType: 7, + elementAlias: "条件节点", + elementName: "条件节点", + elementType: 2, + }; default: return { diff --git a/src/pages/User/SubmitApplication/index.tsx b/src/pages/User/SubmitApplication/index.tsx index b517d95..9c78d48 100644 --- a/src/pages/User/SubmitApplication/index.tsx +++ b/src/pages/User/SubmitApplication/index.tsx @@ -140,6 +140,10 @@ const SubmitApplication = () => { params2.append("approveComment", "已发起"); /** 添加街道id */ params2.append("streetId", mainBody?.streetCode); + for (const key in data) { + params2.append(key, data[key]); + } + const { code: code3 } = await service.applyService.completeExamine( params2 ); diff --git a/vite.config.ts b/vite.config.ts index 1e441e9..d2d46c3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -16,22 +16,24 @@ export default defineConfig({ proxy: { "/login": { // target: "http://192.168.2.21:21601", // 旧测试服务 - target: "https://api.xinducg.cn", // 新测试服务 + // target: "https://api.xinducg.cn", // 正式环境 + //测试环境 + target: "http://171.217.92.33:21511", changeOrigin: true, rewrite: (path) => path.replace(/^\/login/, ""), }, "/apis": { - // target: "http://172.23.0.46:21610", - // target: "http://192.168.31.172:21610", - // target: "http://192.168.31.172:33011", - // target: "http://171.217.92.33:33011", - target: "https://api.xinducg.cn/approval", // 测试服务 - // target: "http://172.23.0.140:33011", // 唐 + // target: "https://api.xinducg.cn/approval", // 正式环境 + //测试环境 + target: "http://171.217.92.33:21511/approval", changeOrigin: true, rewrite: (path) => path.replace(/^\/apis/, ""), }, "/mainBody": { - target: "https://api.xinducg.cn", + //正式环境 + // target: "https://api.xinducg.cn", + //测试环境 + target: "http://171.217.92.33:21511", changeOrigin: true, rewrite: (path) => path.replace(/^\/mainBody/, ""), }, -- GitLab