diff --git a/src/components/Common/FlowDesign/Config/Nodes/EndNode/index.tsx b/src/components/Common/FlowDesign/Config/Nodes/EndNode/index.tsx index 6a681a9397f48becb037d77e44954a861535395b..e6bc5ee27c48a1d30bc578477231d0f196ae99a9 100644 --- a/src/components/Common/FlowDesign/Config/Nodes/EndNode/index.tsx +++ b/src/components/Common/FlowDesign/Config/Nodes/EndNode/index.tsx @@ -7,88 +7,112 @@ import { model } from '@/ts/base'; import { getUuid } from '@/utils/tools'; import ExecutorShowComp from '@/components/Common/ExecutorShowComp'; import ExecutorConfigModal from '../ApprovalNode/configModal'; + +// 组件的 Props(属性)接口定义 interface IProps { - work: IWork; - belong: IBelong; - current: WorkNodeDisplayModel; - refresh: () => void; + work: IWork; // 当前工作对象 + belong: IBelong; // 所属的组织或身份信息 + current: WorkNodeDisplayModel; // 当前工作流节点模型 + refresh: () => void; // 刷新函数 } + /** - * @description: 数据归档节点配置 + * @description: 数据归档节点配置组件(用于配置流程最后的执行器) * @return {*} */ - const EndNode: React.FC = (props) => { + // 如果当前节点的 primaryForms 或 detailForms 为空,则初始化为空数组 props.current.primaryForms = props.current.primaryForms || []; props.current.detailForms = props.current.detailForms || []; + + // 定义执行器状态,初始值来自 props.current.executors(若无则为空数组) const [executors, setExecutors] = useState( props.current.executors ?? [], ); + + // 控制执行器配置弹窗显示与隐藏的状态 const [executorModal, setExecutorModal] = useState(false); + + // 组件返回的渲染结构 return (
+ {/* 卡片:显示执行器列表 */} + {/* 分割线与标题文字 */} 执行器配置
} className={cls[`card-info`]} + // 如果存在执行器,则卡片内留出间距;否则去掉内边距 bodyStyle={{ padding: executors && executors.length ? '24px' : '0' }} extra={ <> + {/* 卡片右上角的“添加”按钮 */} { - setExecutorModal(true); + setExecutorModal(true); // 点击后打开执行器配置弹窗 }}> + 添加 }> + {/* 如果存在执行器,则渲染执行器展示组件 */} {executors && executors.length > 0 && ( { + // 过滤掉被删除的执行器 var exes = executors.filter((a) => a.id != id); - setExecutors(exes); - props.current.executors = exes; + setExecutors(exes); // 更新执行器状态 + props.current.executors = exes; // 同步更新节点模型中的执行器信息 }} /> )} + + {/* 如果执行器配置弹窗状态为 true,则显示弹窗组件 */} {executorModal && ( { if (param) { + // 如果传入参数(表示新建了一个执行器) executors.push({ - id: getUuid(), - trigger: param.trigger, - funcName: param.funcName, - changes: [], - hookUrl: '', - dataType: true, - autoType: true, - tokenType: false, - belongId: props.belong.id, - acquires: [], - copyForm: [] + id: getUuid(), // 生成唯一 ID + trigger: param.trigger, // 执行触发条件 + funcName: param.funcName, // 绑定的执行函数名 + changes: [], // 变化记录(此处默认空) + hookUrl: '', // 钩子 URL(暂时为空) + dataType: true, // 数据类型标记 + autoType: true, // 是否自动执行 + tokenType: false, // 是否使用 Token(false 表示不使用) + belongId: props.belong.id, // 所属 ID(组织或身份) + acquires: [], // 获取参数配置(默认空) + copyForm: [] // 复制表单配置(默认空) }); + // 更新执行器状态 setExecutors([...executors]); + // 同步更新到当前节点模型中 props.current.executors = executors; } + // 关闭弹窗 setExecutorModal(false); }} - current={props.current} + current={props.current} // 传入当前节点模型 /> )}
); }; -export default EndNode; + +export default EndNode; // 导出组件 diff --git a/src/components/Common/FlowDesign/Config/Nodes/GatewayNode/index.tsx b/src/components/Common/FlowDesign/Config/Nodes/GatewayNode/index.tsx index 8287d225c5680873c6ada73e559de60c35795cd0..25ef6369ba0bb59b697d1a3501389d9c8a031611 100644 --- a/src/components/Common/FlowDesign/Config/Nodes/GatewayNode/index.tsx +++ b/src/components/Common/FlowDesign/Config/Nodes/GatewayNode/index.tsx @@ -12,54 +12,69 @@ import useObjectUpdate from '@/hooks/useObjectUpdate'; import { Card, Divider } from 'antd'; import { SelectBox } from 'devextreme-react'; +// 组件的属性接口定义 interface IProps { - current: WorkNodeDisplayModel; - define: IWork; - belong: IBelong; - refresh: () => void; + current: WorkNodeDisplayModel; // 当前节点对象 + define: IWork; // 当前工作流定义对象 + belong: IBelong; // 当前归属组织或用户 + refresh: () => void; // 刷新回调函数 } /** - * @description: 成员节点配置信息 - * @return {*} + * @description: 成员节点(网关节点)配置组件 + * 主要用于配置流程中“分流网关”节点的参数与绑定信息 */ - const GatewayNode: React.FC = (props) => { + // 使用自定义 Hook,返回唯一 key 和刷新函数,用于强制更新组件 const [tkey, tforceUpdate] = useObjectUpdate(props.current); + + // 定义当前网关节点的默认目标 ID 状态,初始值来自当前节点对象 const [destId, setDestId] = useState(props.current.destId); + + // 使用异步加载 Hook 加载网关节点的详细信息 const [loaded, nodeInfo] = useAsyncLoad(async () => { + // 先加载工作流定义的网关信息 await props.define.loadGatewayInfo(); + // 过滤出与当前节点匹配的网关数据 return props.define.gatewayInfo.filter((a) => a.nodeId == props.current.primaryId); }); + + // 再次使用异步加载 Hook,加载当前节点可用的字段信息(用于成员选择) const [loadField, fields] = useAsyncLoad(async () => { - const fields: { text: string; value: string }[] = []; + const fields: { text: string; value: string }[] = []; // 字段下拉选项数组 + + // 遍历当前工作定义的所有主表单 for (const xform of props.define.primaryForms) { - const xfields = await xform.loadFields(); + const xfields = await xform.loadFields(); // 异步加载每个表单的字段 fields.push( ...xfields + // 筛选出“用户型”字段,且控件类型为“成员选择框”,并且所属团队一致 .filter( (a) => a.valueType == '用户型' && a.widget == '成员选择框' && a.options?.teamId == props.define.directory.target.id, ) + // 转换为 SelectBox 可用格式 .map((a) => { return { - text: `[${xform.name}] ${a.name}`, - value: a.id, + text: `[${xform.name}] ${a.name}`, // 显示文字(包含表单名和字段名) + value: a.id, // 字段 ID 作为选项值 }; }), ); } - return fields; + return fields; // 返回字段列表 }); - /** 分流网关信息列 */ + + /** 定义表格列结构(显示网关绑定详情) */ const GatewayColumns: ProColumns[] = [ - { title: '序号', valueType: 'index', width: 50 }, + { title: '序号', valueType: 'index', width: 50 }, // 序号列 { title: '组织', dataIndex: 'target', render: (_: any, record: schema.XWorkGateway) => { + // 使用 EntityIcon 组件显示组织图标与名称 return ; }, }, @@ -67,63 +82,77 @@ const GatewayNode: React.FC = (props) => { title: '办事名称', dataIndex: 'name', render: (_: any, record: schema.XWorkGateway) => { + // 显示绑定的办事定义名称 return record.define?.name; }, }, { title: '绑定时间', - dataIndex: 'createTime', + dataIndex: 'createTime', // 绑定时间字段 }, ]; - // 操作内容渲染函数 + + /** + * 定义每一行的操作项渲染函数 + * 用于“解绑”当前网关绑定的关系 + */ const renderOperate = (item: schema.XWorkGateway) => { return [ { - key: item.id, - label: `解绑`, + key: item.id, // 唯一标识 + label: `解绑`, // 按钮文字 onClick: async () => { + // 调用 define.deleteGateway 方法解绑当前项 const success = await props.define.deleteGateway(item.id); if (success) { - message.info('解绑成功!'); - tforceUpdate(); + message.info('解绑成功!'); // 提示信息 + tforceUpdate(); // 强制刷新组件显示最新状态 } }, }, ]; }; + + // 组件的渲染结构 return (
+ {/* 如果字段已加载完成,则显示“网关设置”卡片 */} {loadField && ( + {/* 标题左侧分割线 + 标题文字 */} 网关设置
}> + {/* DevExtreme 的选择框,用于选择默认分流字段 */} { + // 当用户选择变化时,更新当前节点的目标 ID props.current.destId = e.value; - setDestId(e.value); + setDestId(e.value); // 更新状态 }} - dataSource={fields} + dataSource={fields} // 下拉数据源 /> )} + + {/* 如果网关信息已加载完成,则显示绑定详情卡片 */} {loaded && ( = (props) => { 网关绑定详情
}> + {/* 使用通用表格组件展示网关绑定信息 */} - key={tkey} - rowKey={'id'} - dataSource={nodeInfo ?? []} - scroll={{ y: 'calc(60vh - 150px)' }} - operation={renderOperate} - columns={GatewayColumns} + key={tkey} // 用于触发刷新 + rowKey={'id'} // 表格行的唯一键 + dataSource={nodeInfo ?? []} // 数据源(加载到的网关信息) + scroll={{ y: 'calc(60vh - 150px)' }} // 表格高度设置 + operation={renderOperate} // 操作按钮配置 + columns={GatewayColumns} // 表格列配置 /> )} @@ -154,4 +184,6 @@ const GatewayNode: React.FC = (props) => { ); }; + +// 导出组件 export default GatewayNode; diff --git a/src/components/Common/FlowDesign/Config/Nodes/RootNode/index.tsx b/src/components/Common/FlowDesign/Config/Nodes/RootNode/index.tsx index 5120dfda5fef7732323881b18b24c38341d7263e..01b7e71019ddada78ac092bd529d257495e3bdb6 100644 --- a/src/components/Common/FlowDesign/Config/Nodes/RootNode/index.tsx +++ b/src/components/Common/FlowDesign/Config/Nodes/RootNode/index.tsx @@ -18,53 +18,82 @@ import orgCtrl from '@/ts/controller'; import FormBinding from '../../Components/FormBinding'; import DocumentConfig from '../../Components/Document'; + +// 组件属性接口定义 interface IProps { - work: IWork; - belong: IBelong; - type?: string; - current: WorkNodeDisplayModel; - refresh: () => void; + work: IWork; // 当前工作流对象 + belong: IBelong; // 所属组织或身份信息 + type?: string; // 可选类型(例如审批类型) + current: WorkNodeDisplayModel; // 当前节点信息 + refresh: () => void; // 刷新回调 } + /** - * @description: 角色 - * @return {*} + * @description: RootNode 根节点配置组件 + * 用于配置整个流程起点节点的信息(如表单、按钮、规则、打印模板、执行器等) */ - const RootNode: React.FC = (props) => { - props.current.primaryForms = props.current.primaryForms || []; - props.current.detailForms = props.current.detailForms || []; - props.current.buttons = props.current.buttons || []; + // 初始化节点的基础属性,确保不为 undefined + props.current.primaryForms = props.current.primaryForms || []; // 主表单配置 + props.current.detailForms = props.current.detailForms || []; // 子表单配置 + props.current.buttons = props.current.buttons || []; // 节点上的按钮配置 props.current.documentConfig = props.current.documentConfig || { - propMapping: {}, - nodeMapping: {}, - templates: [], + propMapping: {}, // 属性映射 + nodeMapping: {}, // 节点映射 + templates: [], // 文档模板 }; + + // 当前节点已绑定的打印模板数组 const [primaryPrints, setPrimaryPrints] = useState(props.current.print); + + // 当前节点的执行器配置(自动化任务/触发器) const [executors, setExecutors] = useState( props.current.executors ?? [], ); + + // 控制执行器配置弹窗显示/隐藏 const [executorModal, setExecutorModal] = useState(false); + + // 打印模板创建弹窗(选择新模板) const [printModalCreate, setPrintModalCreate] = useState(false); + + // 打印模板配置弹窗 const [printModal, setPrintModal] = useState(false); + + // 当前选中的打印模板类型 const [printType, setPrintType] = useState( props.current.printData.type ?? '默认无', ); + + // 异步加载打印模板列表 const [printLoaded, prints] = useAsyncLoad(async () => { return await props.work.application.loadAllPrint(); }); + + // 是否正在加载状态 const [loading, setLoading] = useState(true); + + /** + * useEffect — 初始化加载打印模板数据 + * 逻辑:根据组织路径筛选出当前工作流可用的打印模板,并合并进 current.print + */ useEffect(() => { const fetchData = async () => { setLoading(true); - const newData: any = []; + const newData: any = []; // 存储符合条件的新模板 try { + // 获取系统中默认的打印模板组织路径 const IDsIprint = await orgCtrl.loadAllDefaultIPrint(props.work.belongId); + // 加载当前归属下的所有打印模板 const IPrints = await props.belong.directory.loadAllPrints(); + + // 过滤符合当前工作流归属、未被删除的模板 IPrints.forEach((item) => { if ( - props.work.belongId == item.belongId && - !item.groupTags.includes('已删除') + props.work.belongId == item.belongId && // 属于当前工作流 + !item.groupTags.includes('已删除') // 未被删除 ) { + // 路径匹配,确保模板属于当前组织路径层级 if ( item.path.length > 0 && item.path[1] == IDsIprint[0] && @@ -74,9 +103,13 @@ const RootNode: React.FC = (props) => { } } }); + + // 移除重复模板,保留当前已有但未在新模板中出现的项 const newPrimaryPrint = primaryPrints.filter( (obj1) => !newData.some((obj2: any) => obj1.id === obj2.id), ); + + // 确保存在一个“默认无”项(避免为空) let newPrintData = primaryPrints.filter((item) => item.id == '默认无'); if (newPrintData.length == 0) { setPrimaryPrints([ @@ -95,24 +128,32 @@ const RootNode: React.FC = (props) => { }; fetchData(); }, []); + + // 当打印模板更新时,同步写回到当前节点模型中 useEffect(() => { props.current.print = primaryPrints; }, [primaryPrints]); + + // 组件渲染结构 return (
+ {/* 主表配置 */} + {/* 子表配置 */} + + {/* 执行器配置卡片 */} = (props) => { <> { - setExecutorModal(true); + setExecutorModal(true); // 打开执行器配置弹窗 }}> 添加 @@ -136,9 +177,10 @@ const RootNode: React.FC = (props) => { {executors && executors.length > 0 && ( { + // 删除执行器 var exes = executors.filter((a) => a.id != id); setExecutors(exes); props.current.executors = exes; @@ -147,14 +189,22 @@ const RootNode: React.FC = (props) => { )} + + {/* 按钮配置 */} + + {/* 规则配置 */} + + {/* 文档配置 */} + + {/* 打印模板设置 */} = (props) => { <> { - setPrintModalCreate(true); + setPrintModalCreate(true); // 打开打印模板选择弹窗 }}> 添加 @@ -179,6 +229,7 @@ const RootNode: React.FC = (props) => { style={{ width: '100%', }}> + {/* 打印权限开关 */}
= (props) => { }} defaultValue={props.current.allowAllNodesPrint ?? true} onValueChange={(value) => { + // 设置当前节点是否允许所有节点打印 props.current.allowAllNodesPrint = value; }} switchedOnText="允许所有节点打印" @@ -198,6 +250,7 @@ const RootNode: React.FC = (props) => { />
+ {/* 打印模板下拉选择框 */} = (props) => { displayExpr={'name'} valueExpr={'id'} onFocusIn={() => { - setPrintType(''); + setPrintType(''); // 聚焦时清空 }} onValueChange={(e) => { + // 选择模板时同步更新节点的打印数据 props.current.printData && props.current.printData.attributes ? (props.current.printData.type = e) : (props.current.printData = { attributes: [], type: e }); setPrintType(e); + // 根据不同情况决定是否显示打印配置弹窗 if (e == '默认无') { setPrintModal(false); } else if (e == null) { @@ -221,6 +276,7 @@ const RootNode: React.FC = (props) => { setPrintModal(true); } }} + // 自定义选项渲染(带删除按钮) itemRender={(data) => (
{data.name} @@ -230,6 +286,7 @@ const RootNode: React.FC = (props) => { if (data.id == '默认无') { return false; } + // 删除选中的模板 const newPrintData = primaryPrints.filter( (option) => option.id !== data.id, ); @@ -246,10 +303,13 @@ const RootNode: React.FC = (props) => { /> + + {/* 执行器配置弹窗 */} {executorModal ? ( { if (param) { + // 添加新执行器 executors.push({ id: getUuid(), trigger: param.trigger, @@ -271,6 +331,8 @@ const RootNode: React.FC = (props) => { current={props.current} /> ) : null} + + {/* 打印模板配置弹窗 */} {printModal && ( { @@ -285,6 +347,8 @@ const RootNode: React.FC = (props) => { type={props.type} /> )} + + {/* 打印模板选择弹窗(从系统中选择模板) */} {printModalCreate && printLoaded && ( = (props) => { onCancel={() => setPrintModalCreate(false)} onOk={(files) => { if (files.length > 0) { + // 将选择的打印模板加入当前节点 const prints = (files as unknown[] as IPrint[]).map((i) => ({ id: i.metadata.id, name: i.metadata.name, @@ -318,4 +383,6 @@ const RootNode: React.FC = (props) => {
); }; + +// 导出组件 export default RootNode;