Pārlūkot izejas kodu

修复作业管理节点切换同步与表单项回显问题

pm 2 mēneši atpakaļ
vecāks
revīzija
089f7a9881
1 mainītis faili ar 34 papildinājumiem un 18 dzēšanām
  1. 34 18
      src/components/IsolationWork.tsx

+ 34 - 18
src/components/IsolationWork.tsx

@@ -1,4 +1,5 @@
 import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
+import { flushSync } from 'react-dom';
 import { useNavigate } from 'react-router-dom';
 import { useTranslation } from 'react-i18next';
 import { Plus, Search, Edit2, Trash2, MoreVertical, FileText, Eye, Play, CheckCircle, RefreshCw, Workflow, Hand } from 'lucide-react';
@@ -432,6 +433,8 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
   const [workflowActiveTabKey, setWorkflowActiveTabKey] = useState<string>('info');
   const workflowReactFlowWrapper = useRef<HTMLDivElement>(null);
   const [workflowReactFlowInstance, setWorkflowReactFlowInstance] = useState<any>(null);
+  /** 当前 workflowNodeConfig 对应的节点 id,用于切换节点时避免把上一节点的 config 应用到画布 */
+  const workflowNodeConfigForIdRef = useRef<string | null>(null);
   
   // 节点配置状态
   const [workflowNodeConfig, setWorkflowNodeConfig] = useState({
@@ -1953,6 +1956,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
   // 节点点击事件
   const onWorkflowNodeClick = useCallback((event: React.MouseEvent, node: Node) => {
     console.log('点击节点:', node.id);
+    workflowNodeConfigForIdRef.current = node.id;
     
     // 更新节点选中状态
     setWorkflowNodes((nds) =>
@@ -1963,10 +1967,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
       )
     );
     
-    // 先设置选中的节点,确保右侧面板显示
-    setSelectedWorkflowNode(node);
-    
-    // 优先从 workflowWorkNodeDOList 中查找对应节点数据(通过uuid匹配)
+    // 先计算并设置右侧面板的 config,再设置选中的节点,避免切换时出现「选中已变但面板仍显示上一节点」的中间状态
     const nodeDO = workflowWorkNodeDOList.find(item => item.uuid === node.id);
     
     console.log('点击节点:', node.id, '找到的 nodeDO:', nodeDO);
@@ -2030,29 +2031,39 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
       }
       
       const fromIsolationOnly = isReleaseIsolation && isolationNodeDO;
+      // 解除隔离且有关联隔离方案时:表单项仅从隔离方案(dataSource)取数,不兜底 source/node.data,保证清空或更换隔离方案内容后解除隔离表单始终与之一致
+      const isolationOnlyIsolationPoints = fromIsolationOnly && dataSource.isolationPoints
+        ? (() => {
+            try {
+              return typeof dataSource.isolationPoints === 'string'
+                ? JSON.parse(dataSource.isolationPoints)
+                : (Array.isArray(dataSource.isolationPoints) ? dataSource.isolationPoints : []);
+            } catch (_) { return []; }
+          })()
+        : (fromIsolationOnly ? [] : isolationPoints);
       const nodeConfig = {
         nodeName: fromIsolationOnly
           ? (nodeDO.nodeName || releaseNodeData.label || node.data?.label || config?.label || '')
           : (dataSource.nodeName || source.label || config?.label || ''),
         nodeIcon: (isolationNodeDO ? nodeDO.nodeIcon : dataSource.nodeIcon) || source.icon || '',
-        responsible: (dataSource.workerUserId !== null && dataSource.workerUserId !== undefined && dataSource.workerUserId !== 0)
-          ? (typeof dataSource.workerUserId === 'number' ? dataSource.workerUserId : Number(dataSource.workerUserId))
-          : (source.workerUserId && source.workerUserId !== '' && source.workerUserId !== '0')
-            ? (typeof source.workerUserId === 'number' ? source.workerUserId : Number(source.workerUserId))
-            : undefined,
+        responsible: fromIsolationOnly
+          ? (dataSource.workerUserId !== null && dataSource.workerUserId !== undefined && dataSource.workerUserId !== 0 ? (typeof dataSource.workerUserId === 'number' ? dataSource.workerUserId : Number(dataSource.workerUserId)) : undefined)
+          : (dataSource.workerUserId !== null && dataSource.workerUserId !== undefined && dataSource.workerUserId !== 0)
+            ? (typeof dataSource.workerUserId === 'number' ? dataSource.workerUserId : Number(dataSource.workerUserId))
+            : (source.workerUserId && source.workerUserId !== '' && source.workerUserId !== '0' ? (typeof source.workerUserId === 'number' ? source.workerUserId : Number(source.workerUserId)) : undefined),
         remark: source.remark || '',
         submitForm: fromIsolationOnly
           ? (nodeDO.formId != null && nodeDO.formId !== '' ? (typeof nodeDO.formId === 'number' ? nodeDO.formId : Number(nodeDO.formId)) : (releaseNodeData.submitForm != null ? (typeof releaseNodeData.submitForm === 'number' ? releaseNodeData.submitForm : Number(releaseNodeData.submitForm)) : undefined))
           : (dataSource.formId ? (typeof dataSource.formId === 'number' ? dataSource.formId : Number(dataSource.formId)) : (source.submitForm ? (typeof source.submitForm === 'number' ? source.submitForm : Number(source.submitForm)) : undefined)),
-        isolationType: (dataSource.isolationType !== null && dataSource.isolationType !== undefined) ? String(dataSource.isolationType) : (source.isolationType || ''),
-        isolationPoints,
+        isolationType: fromIsolationOnly ? (dataSource.isolationType != null && dataSource.isolationType !== undefined ? String(dataSource.isolationType) : '') : (dataSource.isolationType !== null && dataSource.isolationType !== undefined ? String(dataSource.isolationType) : (source.isolationType || '')),
+        isolationPoints: fromIsolationOnly ? isolationOnlyIsolationPoints : isolationPoints,
         isolationNode: source.isolationNode || [],
         isolationNodeUuid: nodeDO.isolationNodeUuid || source.isolationNodeUuid || '',
-        lockPerson: lockPersonId || (source.lockPerson ? (typeof source.lockPerson === 'number' ? source.lockPerson : Number(source.lockPerson)) : undefined),
-        coLockPersons: coLockPersonIds.length > 0 ? coLockPersonIds : (source.coLockPersons && Array.isArray(source.coLockPersons) ? source.coLockPersons.map((id: any) => typeof id === 'number' ? id : Number(id)) : []),
+        lockPerson: fromIsolationOnly ? (lockPersonId || undefined) : (lockPersonId || (source.lockPerson ? (typeof source.lockPerson === 'number' ? source.lockPerson : Number(source.lockPerson)) : undefined)),
+        coLockPersons: fromIsolationOnly ? coLockPersonIds : (coLockPersonIds.length > 0 ? coLockPersonIds : (source.coLockPersons && Array.isArray(source.coLockPersons) ? source.coLockPersons.map((id: any) => typeof id === 'number' ? id : Number(id)) : [])),
         notificationMethods: source.notificationMethods || { sms: false, message: false, email: false, app: false },
         notificationPerson: source.notificationPerson || '',
-        notificationTime: dataSource.notifyTime || source.notificationTime || '',
+        notificationTime: fromIsolationOnly ? (dataSource.notifyTime ?? '') : (dataSource.notifyTime || source.notificationTime || ''),
         smsTemplateCode: source.smsTemplateCode || 'false',
         messageTemplateCode: source.messageTemplateCode || 'false',
         emailTemplateCode: source.emailTemplateCode || 'false',
@@ -2109,6 +2120,10 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
         });
       }
     }
+    // 使用 flushSync 立即提交选中节点,确保右侧面板在本轮事件中完成切换,避免「有时切换未生效」
+    flushSync(() => {
+      setSelectedWorkflowNode(node);
+    });
     setWorkflowActiveTabKey('info');
   }, [workflowNodeConfigCache, workflowWorkNodeDOList, nodeConfigs]);
   
@@ -2117,9 +2132,10 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
     setSelectedWorkflowNode(null);
   }, []);
   
-  // 实时更新节点显示(只在 workflowNodeConfig 变化时更新,避免在切换节点时覆盖
+  // 实时更新节点显示(仅当当前 config 属于当前选中节点时才写回画布,避免切换节点时把上一节点的 config 误写到新节点
   useEffect(() => {
-    if (selectedWorkflowNode && workflowNodeConfig.nodeName) {
+    if (!selectedWorkflowNode || selectedWorkflowNode.id !== workflowNodeConfigForIdRef.current) return;
+    if (workflowNodeConfig.nodeName) {
       const { isolationMethod, ...restData } = selectedWorkflowNode.data || {};
       // 从缓存中读取 completed 状态,保持保存状态不变
       const isSaved = nodeSavedStatusCache.get(selectedWorkflowNode.id) || false;
@@ -4535,10 +4551,10 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                       )}
                     </div>
 
-                    {/* 右侧:配置面板 */}
+                    {/* 右侧:配置面板;key 随选中节点变化以强制重挂载,避免切换节点后面板内容未刷新 */}
                     <div className="w-80 border border-gray-200 rounded-lg bg-white overflow-y-auto flex-shrink-0">
                       {selectedWorkflowNode ? (
-                        <div className="h-full flex flex-col">
+                        <div key={selectedWorkflowNode.id} className="h-full flex flex-col">
                           {/* 头部 */}
                           <div className="px-4 py-2.5 bg-white border-b border-gray-200 flex items-center justify-between sticky top-0 z-10 shadow-sm">
                             <h3 className="text-sm font-medium text-gray-900">