فهرست منبع

修复流程设计器里表单预览和作业管理编辑中上下步切换逻辑和渲染

pm 4 ماه پیش
والد
کامیت
53b04ffe4e
5فایلهای تغییر یافته به همراه735 افزوده شده و 587 حذف شده
  1. 494 512
      src/components/IsolationWork.tsx
  2. 52 18
      src/components/MyTask.tsx
  3. 59 27
      src/components/ProcessDesigner.tsx
  4. 52 18
      src/components/TaskManagement.tsx
  5. 78 12
      src/components/WorkJobDetail.tsx

+ 494 - 512
src/components/IsolationWork.tsx

@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useCallback, useRef } from 'react';
+import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
 import { useNavigate } from 'react-router-dom';
 import { Plus, Search, Edit2, Trash2, MoreVertical, FileText, Eye, Play, CheckCircle, RefreshCw, Workflow } from 'lucide-react';
 import { Button, Input, Space, Select, Table as AntdTable, message, Modal, Form, Row, Col, Tabs, Radio, DatePicker, Checkbox, Tooltip } from 'antd';
@@ -14,7 +14,6 @@ import ReactFlow, {
   useEdgesState,
   Controls,
   Background,
-  MiniMap,
   NodeTypes,
   BackgroundVariant,
   Handle,
@@ -459,8 +458,20 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
   // 节点保存状态缓存(Map<nodeId, boolean>)- 用于记录当前作业中哪些节点已保存
   const [nodeSavedStatusCache, setNodeSavedStatusCache] = useState<Map<string, boolean>>(new Map());
   
-  // 节点类型映射(在组件内部定义以访问 nodeSavedStatusCache)
-  const nodeTypes: NodeTypes = {
+  // 计算是否有未保存的节点(用于控制流程管理tab的下一步按钮)
+  const hasUnsavedNodes = useMemo(() => {
+    if (workflowNodes.length === 0) {
+      return false; // 如果没有节点,认为没有未保存的节点
+    }
+    // 检查所有节点是否都已保存
+    return workflowNodes.some(node => {
+      const isSaved = nodeSavedStatusCache.get(node.id) || false;
+      return !isSaved;
+    });
+  }, [workflowNodes, nodeSavedStatusCache]);
+  
+  // 节点类型映射(使用 useMemo 避免每次渲染都创建新对象)
+  const nodeTypes: NodeTypes = useMemo(() => ({
     createJob: (props: any) => <CustomNode {...props} nodeSavedStatusCache={nodeSavedStatusCache} />,
     confirm: (props: any) => <CustomNode {...props} nodeSavedStatusCache={nodeSavedStatusCache} />,
     review: (props: any) => <CustomNode {...props} nodeSavedStatusCache={nodeSavedStatusCache} />,
@@ -469,7 +480,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
     releaseIsolation: (props: any) => <CustomNode {...props} nodeSavedStatusCache={nodeSavedStatusCache} />,
     returnLock: (props: any) => <CustomNode {...props} nodeSavedStatusCache={nodeSavedStatusCache} />,
     complete: (props: any) => <CustomNode {...props} nodeSavedStatusCache={nodeSavedStatusCache} />,
-  };
+  }), [nodeSavedStatusCache]);
   
   // 角色用户列表和表单列表
   const [workflowDrawerUsers, setWorkflowDrawerUsers] = useState<UserVO[]>([]);
@@ -1332,11 +1343,357 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
         }
       };
       
+      // 初始化节点状态:调用 checkWorkById 和 selectWorkflowWorkById 接口
+      const initializeNodeStates = async () => {
+        if (!workflowWorkId) {
+          console.warn('workflowWorkId 不存在,无法初始化节点状态');
+          return;
+        }
+        
+        try {
+          // 1. 调用 checkWorkById 获取未保存的节点列表
+          const checkResponse = await workJobApi.checkWorkById(workflowWorkId);
+          const checkData = (checkResponse as any)?.data || checkResponse;
+          const unsavedNodeMessages = Array.isArray(checkData) ? checkData : (checkData ? [checkData] : []);
+          
+          // 2. 调用 selectWorkflowWorkById 获取所有节点数据
+          const detailResponse = await workJobApi.selectWorkflowWorkById(workflowWorkId);
+          const detail = detailResponse as any;
+          
+          if (detail.workflowWorkNodeDOList && Array.isArray(detail.workflowWorkNodeDOList)) {
+            // 更新 workflowWorkNodeDOList
+            setWorkflowWorkNodeDOList(detail.workflowWorkNodeDOList);
+            
+            // 3. 根据接口数据初始化缓存和节点状态
+            // 创建新的缓存和状态,不使用旧的(因为我们要用接口数据覆盖)
+            const newCache = new Map<string, any>();
+            const newCompleted = new Set<string>();
+            const newSavedStatusCache = new Map<string, boolean>();
+            
+            // 遍历所有节点,初始化缓存
+            detail.workflowWorkNodeDOList.forEach((nodeDO: WorkflowWorkNodeDO) => {
+              if (!nodeDO.uuid) return;
+              
+              // 解析节点数据
+              let nodeData: any = {};
+              if (nodeDO.data) {
+                try {
+                  nodeData = typeof nodeDO.data === 'string' ? JSON.parse(nodeDO.data) : nodeDO.data;
+                } catch (e) {
+                  console.error('解析节点data失败:', e);
+                }
+              }
+              
+              // 构建节点配置
+              const config = nodeConfigs.find(c => c.type === (nodeDO.type || nodeData.type));
+              const nodeConfig = {
+                nodeName: nodeDO.nodeName || nodeData.label || config?.label || '',
+                nodeIcon: nodeDO.nodeIcon || nodeData.icon || '',
+                responsible: (nodeDO.workerUserId !== null && nodeDO.workerUserId !== undefined && nodeDO.workerUserId !== 0) 
+                  ? (typeof nodeDO.workerUserId === 'number' ? nodeDO.workerUserId : Number(nodeDO.workerUserId)) 
+                  : (nodeData.workerUserId && nodeData.workerUserId !== '' && nodeData.workerUserId !== '0') 
+                    ? (typeof nodeData.workerUserId === 'number' ? nodeData.workerUserId : Number(nodeData.workerUserId)) 
+                    : undefined,
+                remark: nodeData.remark || '',
+                submitForm: nodeDO.formId ? (typeof nodeDO.formId === 'number' ? nodeDO.formId : Number(nodeDO.formId)) : (nodeData.submitForm ? (typeof nodeData.submitForm === 'number' ? nodeData.submitForm : Number(nodeData.submitForm)) : undefined),
+                isolationType: (nodeDO.isolationType !== null && nodeDO.isolationType !== undefined) ? String(nodeDO.isolationType) : (nodeData.isolationType || ''),
+                isolationPoints: nodeDO.isolationPoints ? (typeof nodeDO.isolationPoints === 'string' ? JSON.parse(nodeDO.isolationPoints) : nodeDO.isolationPoints) : (nodeData.isolationPoints || []),
+                isolationNode: nodeData.isolationNode || [],
+                isolationNodeUuid: nodeDO.isolationNodeUuid || nodeData.isolationNodeUuid || '',
+                lockPerson: (() => {
+                  // 优先从 nodeUserList 中提取
+                  let lockPersonId: number | undefined = undefined;
+                  if (nodeDO.nodeUserList && Array.isArray(nodeDO.nodeUserList)) {
+                    const lockerUser = nodeDO.nodeUserList.find((user: any) => user.type === 'jtlocker');
+                    if (lockerUser?.userId) {
+                      lockPersonId = typeof lockerUser.userId === 'number' ? lockerUser.userId : Number(lockerUser.userId);
+                    }
+                  }
+                  return lockPersonId || (nodeData.lockPerson ? (typeof nodeData.lockPerson === 'number' ? nodeData.lockPerson : Number(nodeData.lockPerson)) : undefined);
+                })(),
+                coLockPersons: (() => {
+                  // 优先从 nodeUserList 中提取
+                  const coLockPersonIds: number[] = [];
+                  if (nodeDO.nodeUserList && Array.isArray(nodeDO.nodeUserList)) {
+                    nodeDO.nodeUserList.forEach((user: any) => {
+                      if (user.type === 'jtcolocker' && user.userId) {
+                        coLockPersonIds.push(typeof user.userId === 'number' ? user.userId : Number(user.userId));
+                      }
+                    });
+                  }
+                  return coLockPersonIds.length > 0 ? coLockPersonIds : (nodeData.coLockPersons && Array.isArray(nodeData.coLockPersons) ? nodeData.coLockPersons.map((id: any) => typeof id === 'number' ? id : Number(id)) : []);
+                })(),
+                notificationMethods: nodeData.notificationMethods || {
+                  sms: false,
+                  message: false,
+                  email: false,
+                  app: false,
+                },
+                notificationPerson: nodeData.notificationPerson || '',
+                notificationTime: nodeDO.notifyTime || nodeData.notificationTime || '',
+              };
+              
+              // 将节点配置存入缓存
+              newCache.set(nodeDO.uuid, nodeConfig);
+              
+              // 检查节点是否已保存(如果不在未保存列表中,说明已保存)
+              // 通过检查未保存消息中是否包含该节点的信息来判断
+              const isNodeUnsaved = unsavedNodeMessages.some((msg: string) => {
+                // 如果消息中包含节点名称或UUID,说明该节点未保存
+                return msg.includes(nodeDO.nodeName || '') || msg.includes(nodeDO.uuid || '');
+              });
+              
+              if (!isNodeUnsaved) {
+                // 节点已保存,标记为已完成
+                newCompleted.add(nodeDO.uuid);
+                newSavedStatusCache.set(nodeDO.uuid, true);
+              } else {
+                // 节点未保存
+                newSavedStatusCache.set(nodeDO.uuid, false);
+              }
+            });
+            
+            // 更新缓存和状态
+            setWorkflowNodeConfigCache(newCache);
+            setCompletedNodeIds(newCompleted);
+            setNodeSavedStatusCache(newSavedStatusCache);
+            
+            console.log('节点状态初始化完成:', {
+              totalNodes: detail.workflowWorkNodeDOList.length,
+              completedNodes: newCompleted.size,
+              unsavedNodes: unsavedNodeMessages.length,
+            });
+            
+            // 找到第一个未保存的节点并自动选中(按照流程顺序)
+            // 等待 workflowNodes 渲染完成后再选中
+            // 使用 setState 的回调形式确保获取最新的 workflowNodes
+            setTimeout(() => {
+              setWorkflowNodes((currentNodes) => {
+                // 使用 getNextIncompleteNode 的逻辑,但基于 nodeSavedStatusCache 来判断
+                // 找到第一个未保存的节点(按照流程顺序)
+                let firstUnsavedNode: Node | null = null;
+                
+                if (workflowJson && workflowJson.adjacency && currentNodes.length > 0) {
+                  const adjacency = workflowJson.adjacency;
+                  const nodeMap = new Map<string, Node>();
+                  
+                  // 初始化节点映射
+                  currentNodes.forEach(node => {
+                    nodeMap.set(node.id, node);
+                  });
+                  
+                  // 辅助函数:检查节点的所有父节点是否都已保存
+                  const areAllParentsSaved = (nodeId: string): boolean => {
+                    const nodeInfo = adjacency[nodeId];
+                    if (!nodeInfo || !nodeInfo.parentUuid || nodeInfo.parentUuid.length === 0) {
+                      return true; // 没有父节点,可以执行
+                    }
+                    return nodeInfo.parentUuid.every((parentId: string) => {
+                      const isParentSaved = newSavedStatusCache.get(parentId);
+                      return isParentSaved === true; // 父节点已保存
+                    });
+                  };
+                  
+                  // 辅助函数:获取节点的第一个未保存的子节点
+                  const getFirstUnsavedChild = (nodeId: string): string | null => {
+                    const nodeInfo = adjacency[nodeId];
+                    if (!nodeInfo || !nodeInfo.childrenUuid || nodeInfo.childrenUuid.length === 0) {
+                      return null; // 没有子节点
+                    }
+                    
+                    // 按顺序查找第一个未保存的子节点
+                    for (const childId of nodeInfo.childrenUuid) {
+                      const isChildSaved = newSavedStatusCache.get(childId);
+                      if (isChildSaved === false && areAllParentsSaved(childId)) {
+                        return childId;
+                      }
+                    }
+                    
+                    return null;
+                  };
+                  
+                  // BFS遍历查找第一个未保存的节点
+                  const queue: string[] = [];
+                  const visited = new Set<string>();
+                  
+                  // 找到所有开始节点(parentUuid 为空的节点)
+                  for (const nodeId in adjacency) {
+                    const nodeInfo = adjacency[nodeId];
+                    if (!nodeInfo.parentUuid || nodeInfo.parentUuid.length === 0) {
+                      const isSaved = newSavedStatusCache.get(nodeId);
+                      if (isSaved === false) {
+                        // 如果开始节点未保存,直接返回
+                        firstUnsavedNode = nodeMap.get(nodeId) || null;
+                        if (firstUnsavedNode) {
+                          break;
+                        }
+                      } else {
+                        // 如果开始节点已保存,将其子节点加入队列
+                        if (nodeInfo.childrenUuid && nodeInfo.childrenUuid.length > 0) {
+                          nodeInfo.childrenUuid.forEach((childId: string) => {
+                            if (!visited.has(childId)) {
+                              queue.push(childId);
+                              visited.add(childId);
+                            }
+                          });
+                        }
+                      }
+                    }
+                  }
+                  
+                  // 如果开始节点都已保存,BFS遍历查找下一个未保存的节点
+                  if (!firstUnsavedNode) {
+                    while (queue.length > 0) {
+                      const currentNodeId = queue.shift()!;
+                      
+                      const isSaved = newSavedStatusCache.get(currentNodeId);
+                      if (isSaved === false && areAllParentsSaved(currentNodeId)) {
+                        firstUnsavedNode = nodeMap.get(currentNodeId) || null;
+                        if (firstUnsavedNode) {
+                          break;
+                        }
+                      }
+                      
+                      // 如果当前节点已保存,将其子节点加入队列
+                      const nodeInfo = adjacency[currentNodeId];
+                      if (nodeInfo && nodeInfo.childrenUuid && nodeInfo.childrenUuid.length > 0) {
+                        nodeInfo.childrenUuid.forEach((childId: string) => {
+                          if (!visited.has(childId)) {
+                            queue.push(childId);
+                            visited.add(childId);
+                          }
+                        });
+                      }
+                    }
+                  }
+                } else {
+                  // 如果没有 adjacency 信息,回退到简单查找
+                  firstUnsavedNode = currentNodes.find(node => {
+                    const isSaved = newSavedStatusCache.get(node.id);
+                    return isSaved === false; // 明确为 false 表示未保存
+                  }) || null;
+                }
+                
+                if (!firstUnsavedNode) {
+                  console.log('没有找到未保存的节点,所有节点都已保存');
+                  return currentNodes;
+                }
+                
+                // 选中第一个未保存的节点
+                setSelectedWorkflowNode(firstUnsavedNode);
+                
+                // 从缓存中获取节点配置
+                const cachedConfig = newCache.get(firstUnsavedNode.id);
+                if (cachedConfig) {
+                  setWorkflowNodeConfig(cachedConfig);
+                } else {
+                  // 如果缓存中没有,从 nodeDO 中获取
+                  const nodeDO = detail.workflowWorkNodeDOList.find(item => item.uuid === firstUnsavedNode.id);
+                  if (nodeDO) {
+                    let nodeData: any = {};
+                    if (nodeDO.data) {
+                      try {
+                        nodeData = typeof nodeDO.data === 'string' ? JSON.parse(nodeDO.data) : nodeDO.data;
+                      } catch (e) {
+                        console.error('解析节点data失败:', e);
+                      }
+                    }
+                    const config = nodeConfigs.find(c => c.type === (nodeDO.type || nodeData.type));
+                    setWorkflowNodeConfig({
+                      nodeName: nodeDO.nodeName || nodeData.label || config?.label || '',
+                      nodeIcon: nodeDO.nodeIcon || nodeData.icon || '',
+                      responsible: (nodeDO.workerUserId !== null && nodeDO.workerUserId !== undefined && nodeDO.workerUserId !== 0) 
+                        ? (typeof nodeDO.workerUserId === 'number' ? nodeDO.workerUserId : Number(nodeDO.workerUserId)) 
+                        : (nodeData.workerUserId && nodeData.workerUserId !== '' && nodeData.workerUserId !== '0') 
+                          ? (typeof nodeData.workerUserId === 'number' ? nodeData.workerUserId : Number(nodeData.workerUserId)) 
+                          : undefined,
+                      remark: nodeData.remark || '',
+                      submitForm: nodeDO.formId ? (typeof nodeDO.formId === 'number' ? nodeDO.formId : Number(nodeDO.formId)) : (nodeData.submitForm ? (typeof nodeData.submitForm === 'number' ? nodeData.submitForm : Number(nodeData.submitForm)) : undefined),
+                      isolationType: (nodeDO.isolationType !== null && nodeDO.isolationType !== undefined) ? String(nodeDO.isolationType) : (nodeData.isolationType || ''),
+                      isolationPoints: nodeDO.isolationPoints ? (typeof nodeDO.isolationPoints === 'string' ? JSON.parse(nodeDO.isolationPoints) : nodeDO.isolationPoints) : (nodeData.isolationPoints || []),
+                      isolationNode: nodeData.isolationNode || [],
+                      isolationNodeUuid: nodeDO.isolationNodeUuid || nodeData.isolationNodeUuid || '',
+                      lockPerson: (() => {
+                        let lockPersonId: number | undefined = undefined;
+                        if (nodeDO.nodeUserList && Array.isArray(nodeDO.nodeUserList)) {
+                          const lockerUser = nodeDO.nodeUserList.find((user: any) => user.type === 'jtlocker');
+                          if (lockerUser?.userId) {
+                            lockPersonId = typeof lockerUser.userId === 'number' ? lockerUser.userId : Number(lockerUser.userId);
+                          }
+                        }
+                        return lockPersonId || (nodeData.lockPerson ? (typeof nodeData.lockPerson === 'number' ? nodeData.lockPerson : Number(nodeData.lockPerson)) : undefined);
+                      })(),
+                      coLockPersons: (() => {
+                        const coLockPersonIds: number[] = [];
+                        if (nodeDO.nodeUserList && Array.isArray(nodeDO.nodeUserList)) {
+                          nodeDO.nodeUserList.forEach((user: any) => {
+                            if (user.type === 'jtcolocker' && user.userId) {
+                              coLockPersonIds.push(typeof user.userId === 'number' ? user.userId : Number(user.userId));
+                            }
+                          });
+                        }
+                        return coLockPersonIds.length > 0 ? coLockPersonIds : (nodeData.coLockPersons && Array.isArray(nodeData.coLockPersons) ? nodeData.coLockPersons.map((id: any) => typeof id === 'number' ? id : Number(id)) : []);
+                      })(),
+                      notificationMethods: nodeData.notificationMethods || {
+                        sms: false,
+                        message: false,
+                        email: false,
+                        app: false,
+                      },
+                      notificationPerson: nodeData.notificationPerson || '',
+                      notificationTime: nodeDO.notifyTime || nodeData.notificationTime || '',
+                    });
+                  } else {
+                    // 如果既没有缓存也没有 nodeDO,使用节点原始数据
+                    const source = firstUnsavedNode.data || {};
+                    const config = nodeConfigs.find(c => c.type === source.type);
+                    setWorkflowNodeConfig({
+                      nodeName: source.label || config?.label || '',
+                      nodeIcon: source.icon || '',
+                      responsible: source.workerUserId ? (typeof source.workerUserId === 'number' ? source.workerUserId : Number(source.workerUserId)) : undefined,
+                      remark: source.remark || '',
+                      submitForm: source.submitForm ? (typeof source.submitForm === 'number' ? source.submitForm : Number(source.submitForm)) : undefined,
+                      isolationType: source.isolationType || '',
+                      isolationPoints: source.isolationPoints || [],
+                      isolationNode: source.isolationNode || [],
+                      isolationNodeUuid: source.isolationNodeUuid || '',
+                      lockPerson: source.lockPerson ? (typeof source.lockPerson === 'number' ? source.lockPerson : Number(source.lockPerson)) : undefined,
+                      coLockPersons: 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: source.notificationTime || '',
+                    });
+                  }
+                }
+                
+                console.log('自动选中第一个未保存的节点:', firstUnsavedNode.id);
+                
+                // 高亮选中的节点
+                return currentNodes.map((node) =>
+                  node.id === firstUnsavedNode.id
+                    ? { ...node, selected: true }
+                    : { ...node, selected: false }
+                );
+              });
+            }, 300); // 延迟300ms,确保 workflowNodes 已经渲染完成
+          }
+        } catch (error: any) {
+          console.error('初始化节点状态失败:', error);
+          message.error('加载节点状态失败: ' + (error?.message || '未知错误'));
+        }
+      };
+      
       loadRoleUsers();
       loadIsolationPoints();
       loadFormList();
+      initializeNodeStates();
     }
-  }, [workJobStep]);
+  }, [workJobStep, workflowWorkId]);
   
   // 验证节点配置是否完整
   const validateNodeConfig = useCallback((config: any, nodeType: string): boolean => {
@@ -1515,6 +1872,8 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
   
   // 节点点击事件
   const onWorkflowNodeClick = useCallback((event: React.MouseEvent, node: Node) => {
+    console.log('点击节点:', node.id);
+    
     // 更新节点选中状态
     setWorkflowNodes((nds) =>
       nds.map((n) =>
@@ -1524,6 +1883,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
       )
     );
     
+    // 先设置选中的节点,确保右侧面板显示
     setSelectedWorkflowNode(node);
     
     // 优先从 workflowWorkNodeDOList 中查找对应节点数据(通过uuid匹配)
@@ -1604,39 +1964,48 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
         notificationTime: nodeDO.notifyTime || source.notificationTime || '',
       };
       
-      // 如果 nodeDO 存在且有 id,说明是已保存的节点,直接使用 nodeDO 的数据,不使用缓存
-      console.log('设置节点配置 - nodeConfig.responsible:', nodeConfig.responsible);
-      if (nodeDO.id) {
-        setWorkflowNodeConfig(nodeConfig);
+      // 优先使用缓存中的配置(如果有),这样可以保持用户未保存的修改
+      // 如果缓存中没有,再使用 nodeDO 的数据
+      console.log('设置节点配置 - nodeConfig.responsible:', nodeConfig.responsible, 'node.id:', node.id);
+      const cachedConfig = workflowNodeConfigCache.get(node.id);
+      console.log('缓存中的配置:', cachedConfig);
+      if (cachedConfig) {
+        // 如果缓存中有配置,使用缓存(可能包含用户未保存的修改)
+        console.log('使用缓存配置');
+        // 使用函数式更新确保状态正确更新
+        setWorkflowNodeConfig(() => ({ ...cachedConfig }));
+      } else if (nodeDO.id) {
+        // 如果缓存中没有,但节点已保存,使用 nodeDO 的数据
+        console.log('使用 nodeDO 配置');
+        setWorkflowNodeConfig(() => ({ ...nodeConfig }));
       } else {
-        // 如果没有保存过,优先使用缓存,否则使用节点数据
-        const cachedConfig = workflowNodeConfigCache.get(node.id);
-        if (cachedConfig) {
-          setWorkflowNodeConfig(cachedConfig);
-        } else {
-          setWorkflowNodeConfig(nodeConfig);
-        }
+        // 如果既没有缓存也没有保存,使用节点数据
+        console.log('使用节点数据配置');
+        setWorkflowNodeConfig(() => ({ ...nodeConfig }));
       }
     } else {
       // 如果没有找到节点数据,使用原有逻辑
+      console.log('没有找到 nodeDO,使用节点数据');
       const cachedConfig = workflowNodeConfigCache.get(node.id);
       if (cachedConfig) {
-        setWorkflowNodeConfig(cachedConfig);
+        console.log('使用缓存配置(无 nodeDO)');
+        setWorkflowNodeConfig(() => cachedConfig);
       } else {
+        console.log('使用节点原始数据');
         const source = node.data || {};
         const config = nodeConfigs.find(c => c.type === source.type);
         setWorkflowNodeConfig({
           nodeName: source.label || config?.label || '',
           nodeIcon: source.icon || '',
-          responsible: source.workerUserId || '',
+          responsible: source.workerUserId ? (typeof source.workerUserId === 'number' ? source.workerUserId : Number(source.workerUserId)) : undefined,
           remark: source.remark || '',
-          submitForm: source.submitForm || '',
+          submitForm: source.submitForm ? (typeof source.submitForm === 'number' ? source.submitForm : Number(source.submitForm)) : undefined,
           isolationType: source.isolationType || '',
           isolationPoints: source.isolationPoints || [],
           isolationNode: source.isolationNode || [],
           isolationNodeUuid: source.isolationNodeUuid || '',
-          lockPerson: source.lockPerson || '',
-          coLockPersons: source.coLockPersons || [],
+          lockPerson: source.lockPerson ? (typeof source.lockPerson === 'number' ? source.lockPerson : Number(source.lockPerson)) : undefined,
+          coLockPersons: source.coLockPersons && Array.isArray(source.coLockPersons) ? source.coLockPersons.map((id: any) => typeof id === 'number' ? id : Number(id)) : [],
           notificationMethods: source.notificationMethods || {
             sms: false,
             message: false,
@@ -1649,17 +2018,19 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
       }
     }
     setWorkflowActiveTabKey('info');
-  }, [workflowNodeConfigCache, workflowWorkNodeDOList]);
+  }, [workflowNodeConfigCache, workflowWorkNodeDOList, nodeConfigs]);
   
   // 画布点击事件(取消选择)
   const onWorkflowPaneClick = useCallback(() => {
     setSelectedWorkflowNode(null);
   }, []);
   
-  // 实时更新节点显示
+  // 实时更新节点显示(只在 workflowNodeConfig 变化时更新,避免在切换节点时覆盖)
   useEffect(() => {
-    if (selectedWorkflowNode) {
+    if (selectedWorkflowNode && workflowNodeConfig.nodeName) {
       const { isolationMethod, ...restData } = selectedWorkflowNode.data || {};
+      // 从缓存中读取 completed 状态,保持保存状态不变
+      const isSaved = nodeSavedStatusCache.get(selectedWorkflowNode.id) || false;
       const updatedData = {
         ...restData,
         label: workflowNodeConfig.nodeName,
@@ -1676,6 +2047,8 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
         notificationMethods: workflowNodeConfig.notificationMethods,
         notificationPerson: workflowNodeConfig.notificationPerson,
         notificationTime: workflowNodeConfig.notificationTime,
+        // 保持 completed 状态从缓存中读取,不覆盖
+        completed: isSaved,
       };
       setWorkflowNodes((nds) =>
         nds.map((node) =>
@@ -1685,7 +2058,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
         )
       );
     }
-  }, [workflowNodeConfig, selectedWorkflowNode, setWorkflowNodes]);
+  }, [workflowNodeConfig, selectedWorkflowNode?.id, setWorkflowNodes, nodeSavedStatusCache]);
 
   // 作业管理删除处理
   const handleWorkJobDelete = async (id: number) => {
@@ -2707,6 +3080,53 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
     },
   ];
 
+  // 获取作业状态样式
+  const getWorkJobStatusStyle = (status: string | number | undefined): React.CSSProperties => {
+    if (!status) {
+      return {
+        backgroundColor: '#e5e5e5',
+        color: '#333333',
+      };
+    }
+    
+    // 从字典中查找状态文本
+    const statusItem = jobStatusDictList.find(item => String(item.value) === String(status));
+    const statusText = statusItem ? (statusItem.label || '') : String(status || '');
+    const statusTextLower = statusText.toLowerCase();
+    
+    // 根据状态文本判断颜色
+    // 待执行、待发布:灰色 #e5e5e5
+    if (statusTextLower.includes('待执行') || statusTextLower.includes('待发布') || 
+        statusTextLower.includes('pending') || statusTextLower.includes('unreleased')) {
+      return {
+        backgroundColor: '#e5e5e5',
+        color: '#333333',
+      };
+    }
+    // 进行中:蓝色 #1677ff
+    if (statusTextLower.includes('进行中') || statusTextLower.includes('执行中') || 
+        statusTextLower.includes('running') || statusTextLower.includes('in_progress')) {
+      return {
+        backgroundColor: '#1677ff',
+        color: '#ffffff',
+      };
+    }
+    // 已完成:绿色 #0acb57
+    if (statusTextLower.includes('已完成') || statusTextLower.includes('执行完成') || 
+        statusTextLower.includes('completed') || statusTextLower.includes('完成')) {
+      return {
+        backgroundColor: '#0acb57',
+        color: '#ffffff',
+      };
+    }
+    
+    // 如果没有匹配到,默认灰色
+    return {
+      backgroundColor: '#e5e5e5',
+      color: '#333333',
+    };
+  };
+
   // 作业管理表格列配置
   const workJobColumns: ColumnsType<WorkJobVO> = [
     {
@@ -2782,12 +3202,13 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
       width: '10%',
       render: (status: string | number) => {
         const statusItem = jobStatusDictList.find(item => String(item.value) === String(status));
-        const statusInfo = statusItem
-          ? { text: statusItem.label, className: 'bg-gray-100 text-gray-700' }
-          : { text: String(status || '-'), className: 'bg-gray-100 text-gray-700' };
+        const statusText = statusItem ? (statusItem.label || '') : String(status || '-');
         return (
-          <span className={`inline-flex px-3 py-1 rounded-lg text-xs ${statusInfo.className}`}>
-            {statusInfo.text}
+          <span 
+            className="inline-flex px-3 py-1 rounded-lg text-xs"
+            style={getWorkJobStatusStyle(status)}
+          >
+            {statusText}
           </span>
         );
       },
@@ -3768,15 +4189,6 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                           >
                             <Controls className="!bg-white !border !border-gray-200 !rounded-lg !shadow-md" />
                             <Background variant={BackgroundVariant.Dots} gap={16} size={1} color="#e5e7eb" />
-                            <MiniMap
-                              nodeColor={(node) => {
-                                const config = nodeConfigs.find(c => c.type === node.type);
-                                if (config?.iconColorCustom) return config.iconColorCustom;
-                                return '#6b7280';
-                              }}
-                              maskColor="rgba(0, 0, 0, 0.05)"
-                              className="!bg-white !border !border-gray-200 !rounded-lg"
-                            />
                           </ReactFlow>
                         </div>
                       ) : (
@@ -4727,10 +5139,10 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                               : { ...node, selected: false }
                                           )
                                         );
-                                        // 滚动到节点位置
-                                        if (workflowReactFlowInstance) {
-                                          workflowReactFlowInstance.fitView({ padding: 0.2, duration: 300, nodes: [nextNode] });
-                                        }
+                                        // 不自动聚焦,保持当前视图,让用户能看到其他节点
+                                        // if (workflowReactFlowInstance) {
+                                        //   workflowReactFlowInstance.fitView({ padding: 0.2, duration: 300, nodes: [nextNode] });
+                                        // }
                                       }, 100);
                                       message.success('节点配置已保存,请继续配置下一个节点');
                                     } else {
@@ -4743,11 +5155,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                           
                                           // 如果返回的错误信息数组为空或null,表示所有节点配置完毕
                                           if (!errorMessages || errorMessages.length === 0) {
-                                            message.success('所有节点配置已完成');
-                                            // 延迟一下再跳转,让用户看到成功提示
-                                            setTimeout(() => {
-                                              setWorkJobStep(2);
-                                            }, 500);
+                                            message.success('所有节点配置已完成,可以点击"下一步"继续');
                                           } else {
                                             // 有错误信息,显示弹框
                                             Modal.warning({
@@ -4773,11 +5181,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                           message.error(error?.message || '检查作业失败');
                                         }
                                       } else {
-                                        message.success('所有节点配置已完成');
-                                        // 延迟一下再跳转,让用户看到成功提示
-                                        setTimeout(() => {
-                                          setWorkJobStep(2);
-                                        }, 500);
+                                        message.success('所有节点配置已完成,可以点击"下一步"继续');
                                       }
                                     }
                                   } catch (error: any) {
@@ -4998,19 +5402,8 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                             // 成功后进入下一步
                             setWorkJobStep(1);
                             
-                            // 如果有 workflowWorkId,获取详情并加载 workflowWorkNodeDOList
-                            if (currentWorkId) {
-                              try {
-                                const detailResponse = await workJobApi.selectWorkflowWorkById(currentWorkId);
-                                const detail = detailResponse as any;
-                                if (detail.workflowWorkNodeDOList && Array.isArray(detail.workflowWorkNodeDOList)) {
-                                  setWorkflowWorkNodeDOList(detail.workflowWorkNodeDOList);
-                                }
-                              } catch (error: any) {
-                                console.error('获取作业节点列表失败:', error);
-                                // 不阻止进入下一步,只是不加载节点列表
-                              }
-                            }
+                            // 注意:节点状态初始化会在 useEffect 中自动执行(当 workJobStep === 1 且 workflowWorkId 存在时)
+                            // 这里不需要手动加载,让 useEffect 统一处理
                           } catch (error: any) {
                             if (error.errorFields) {
                               // 表单验证失败
@@ -5038,7 +5431,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                   <div className="flex items-center justify-between w-full">
                     <div className="flex items-center text-red-600 text-sm">
                       <span className="mr-1">⚠️</span>
-                      <span>提示:流程中的每一个节点都需要单独配置并保存,未保存的节点将不会生效。</span>
+                      <span>提示:流程中的每一个节点都需要单独配置并保存,方可点击 "下一步"。</span>
                     </div>
                     <div className="flex items-center gap-2">
                       <Button onClick={() => setWorkJobStep(0)}>
@@ -5048,462 +5441,51 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                         <>
                       <Button
                         type="primary"
+                        disabled={hasUnsavedNodes}
                         onClick={async () => {
-                        if (!selectedWorkflowNode) {
-                          message.warning('请先选择一个节点进行配置');
-                          return;
-                        }
-                        
-                        // 验证当前节点配置
-                        const isValid = validateNodeConfig(workflowNodeConfig, selectedWorkflowNode.data?.type || '');
-                        if (!isValid) {
-                          message.warning('请完成当前节点的必填项配置');
+                        // 调用检查接口,检查是否有未保存的节点
+                        if (!workflowWorkId) {
+                          message.warning('作业ID不存在,无法检查');
                           return;
                         }
                         
                         try {
-                          // 查找对应的 workflowWorkNodeDO 节点(通过uuid匹配)
-                          const nodeDO = workflowWorkNodeDOList.find(item => item.uuid === selectedWorkflowNode.id);
-                          
-                          // 在外部作用域定义,确保在自动切换节点时可以使用
-                          let mergedWorkflowWorkNodeDOList: WorkflowWorkNodeDO[] = [];
-                          
-                          if (nodeDO && nodeDO.id && workflowWorkId) {
-                            // 构建节点用户信息列表
-                            const nodeUserDOList: WorkflowWorkNodeUserDO[] = [];
-                            
-                            // 如果是解除隔离节点,需要复制隔离节点的数据
-                            if (selectedWorkflowNode.data?.type === 'releaseIsolation' && workflowNodeConfig.isolationNodeUuid) {
-                              // 从 workflowWorkNodeDOList 中查找对应的隔离节点数据
-                              const isolationNodeDO = workflowWorkNodeDOList.find(item => 
-                                item.uuid === workflowNodeConfig.isolationNodeUuid && item.type === 'isolation'
-                              );
-                              
-                              if (isolationNodeDO) {
-                                // 复制隔离节点的 nodeUserList(上锁人、共锁人等)
-                                if (isolationNodeDO.nodeUserList && Array.isArray(isolationNodeDO.nodeUserList)) {
-                                  isolationNodeDO.nodeUserList.forEach((user: any) => {
-                                    if (user.type === 'jtlocker' || user.type === 'jtcolocker') {
-                                      nodeUserDOList.push({
-                                        userId: user.userId,
-                                        type: user.type,
-                                      });
-                                    }
-                                  });
-                                }
-                              }
-                            } else {
-                              // 如果不是解除隔离节点,使用原有逻辑
-                              // 如果隔离方式是"上锁挂牌"(字典值:1),添加上锁人员和共锁人员
-                              if (workflowNodeConfig.isolationType === '1') {
-                                // 添加上锁人员
-                                if (workflowNodeConfig.lockPerson) {
-                                  nodeUserDOList.push({
-                                    userId: Number(workflowNodeConfig.lockPerson),
-                                    type: 'jtlocker',
-                                  });
-                                }
-                                
-                                // 添加共锁人员(可以多选)
-                                if (workflowNodeConfig.coLockPersons && workflowNodeConfig.coLockPersons.length > 0) {
-                                  workflowNodeConfig.coLockPersons.forEach((userId: string | number) => {
-                                    nodeUserDOList.push({
-                                      userId: Number(userId),
-                                      type: 'jtcolocker',
-                                    });
-                                  });
-                                }
-                              }
-                            }
-                            
-                            // 调用更新节点接口
-                            const formId = workflowNodeConfig.submitForm ? Number(workflowNodeConfig.submitForm) : undefined;
-                            
-                            // 如果 formId 存在,先调用接口获取表单数据
-                            let formData: string | undefined = undefined;
-                            if (formId) {
-                              try {
-                                const formResponse = await getForm(formId);
-                                // 获取 data 字段的内容(如果接口返回的是 { code, data, message } 格式)
-                                let formDataObj: any = formResponse;
-                                if (formResponse && typeof formResponse === 'object' && 'data' in formResponse) {
-                                  formDataObj = (formResponse as any).data || formResponse;
-                                }
-                                // 将整个对象内容转换成 JSON 字符串传递给 formData 字段
-                                formData = JSON.stringify(formDataObj);
-                                console.log('表单数据已转换为字符串:', formData);
-                              } catch (error) {
-                                console.error('获取表单数据失败:', error);
-                                // 即使获取失败,也继续保存节点,只是不传递 formData
-                              }
-                            }
-                            
-                            const updateParam: UpdateWorkflowWorkNodeParam = {
-                              nodeId: nodeDO.id,
-                              nodeName: workflowNodeConfig.nodeName,
-                              formId: formId,
-                              workerUserId: workflowNodeConfig.responsible ? Number(workflowNodeConfig.responsible) : undefined,
-                              isolationType: workflowNodeConfig.isolationType || undefined,
-                              isolationPoints: workflowNodeConfig.isolationPoints && workflowNodeConfig.isolationPoints.length > 0 
-                                ? JSON.stringify(workflowNodeConfig.isolationPoints) 
-                                : undefined,
-                              // 如果是解除隔离节点,传递选中的隔离节点UUID
-                              isolationNodeUuid: (selectedWorkflowNode.data?.type === 'releaseIsolation' && workflowNodeConfig.isolationNodeUuid) 
-                                ? workflowNodeConfig.isolationNodeUuid 
-                                : undefined,
-                              nodeUserDOList: nodeUserDOList.length > 0 ? nodeUserDOList : undefined,
-                              // 如果获取到了表单数据,将整个对象内容转换成字符串传递给 formData 字段
-                              formData: formData,
-                            };
-                            
-                            await workJobApi.updateWorkflowWorkNode(updateParam);
-                            
-                            // 更新成功后,立即调用详情接口获取最新数据
-                            let mergedWorkflowWorkNodeDOList: WorkflowWorkNodeDO[] = [];
-                            if (workflowWorkId) {
-                              try {
-                                const detailResponse = await workJobApi.selectWorkflowWorkById(workflowWorkId);
-                                const detail = detailResponse as any;
-                                if (detail.workflowWorkNodeDOList && Array.isArray(detail.workflowWorkNodeDOList)) {
-                                  // 使用接口返回的最新数据更新 workflowWorkNodeDOList
-                                  // 合并更新:保留现有数据,只更新接口返回的数据
-                                  // 先计算合并后的数据,然后再更新状态
-                                  const prev = workflowWorkNodeDOList;
-                                  console.log('合并更新前的 workflowWorkNodeDOList:', prev);
-                                  console.log('接口返回的 workflowWorkNodeDOList:', detail.workflowWorkNodeDOList);
-                                  
-                                  // 创建一个映射,方便查找
-                                  const prevMap = new Map<string | number, WorkflowWorkNodeDO>();
-                                  prev.forEach(item => {
-                                    if (item.id) prevMap.set(item.id, item);
-                                    if (item.uuid) prevMap.set(item.uuid, item);
-                                  });
-                                  
-                                  const updatedList = detail.workflowWorkNodeDOList.map((newItem: WorkflowWorkNodeDO) => {
-                                    // 查找现有列表中对应的节点(通过 id 或 uuid 匹配)
-                                    const existingItem = (newItem.id && prevMap.get(newItem.id)) || 
-                                                        (newItem.uuid && prevMap.get(newItem.uuid)) || 
-                                                        null;
-                                    
-                                    // 如果找到现有节点,合并数据(优先使用接口返回的数据,但保留现有数据中接口没有返回的字段)
-                                    if (existingItem) {
-                                      // 合并数据,确保关键字段不被覆盖为空
-                                      // 只有当接口返回的值是有效的(不是 null、undefined、0 或空字符串)时,才使用接口的值
-                                      const merged: WorkflowWorkNodeDO = {
-                                        ...existingItem,
-                                        ...newItem,
-                                        // 如果接口返回的 workerUserId 是 null、undefined 或 0,保留现有的值
-                                        workerUserId: (newItem.workerUserId !== null && newItem.workerUserId !== undefined && newItem.workerUserId !== 0) 
-                                          ? newItem.workerUserId 
-                                          : existingItem.workerUserId,
-                                        // 如果接口返回的 formId 是 null、undefined 或 0,保留现有的值
-                                        formId: (newItem.formId !== null && newItem.formId !== undefined && newItem.formId !== 0) 
-                                          ? newItem.formId 
-                                          : existingItem.formId,
-                                        // 如果接口返回的 isolationType 是 null 或 undefined,保留现有的值
-                                        isolationType: (newItem.isolationType !== null && newItem.isolationType !== undefined && newItem.isolationType !== '') 
-                                          ? newItem.isolationType 
-                                          : existingItem.isolationType,
-                                        // 如果接口返回的 isolationPoints 是 null 或 undefined,保留现有的值
-                                        isolationPoints: (newItem.isolationPoints !== null && newItem.isolationPoints !== undefined && newItem.isolationPoints !== '') 
-                                          ? newItem.isolationPoints 
-                                          : existingItem.isolationPoints,
-                                        // 如果接口返回的 nodeUserList 是空数组或不存在,保留现有的值
-                                        nodeUserList: (newItem.nodeUserList && Array.isArray(newItem.nodeUserList) && newItem.nodeUserList.length > 0)
-                                          ? newItem.nodeUserList
-                                          : (existingItem.nodeUserList || []),
-                                      };
-                                      
-                                      console.log(`合并节点 ${newItem.uuid || newItem.id}:`, {
-                                        existing: { workerUserId: existingItem.workerUserId, formId: existingItem.formId },
-                                        new: { workerUserId: newItem.workerUserId, formId: newItem.formId },
-                                        merged: { workerUserId: merged.workerUserId, formId: merged.formId }
-                                      });
-                                      
-                                      return merged;
-                                    }
-                                    
-                                    // 如果没找到,直接使用接口返回的数据
-                                    return newItem;
-                                  });
-                                  
-                                  // 如果接口返回的列表中有新节点(不在现有列表中的),也要添加
-                                  const existingUuids = new Set<string | number>();
-                                  prev.forEach(item => {
-                                    if (item.id) existingUuids.add(item.id);
-                                    if (item.uuid) existingUuids.add(item.uuid);
-                                  });
-                                  
-                                  const newItems = detail.workflowWorkNodeDOList.filter((item: WorkflowWorkNodeDO) => {
-                                    const itemKey = item.id || item.uuid;
-                                    return itemKey && !existingUuids.has(itemKey);
-                                  });
-                                  
-                                  mergedWorkflowWorkNodeDOList = [...updatedList, ...newItems];
-                                  console.log('合并更新后的 workflowWorkNodeDOList:', mergedWorkflowWorkNodeDOList);
-                                  
-                                  // 更新状态
-                                  setWorkflowWorkNodeDOList(mergedWorkflowWorkNodeDOList);
-                                }
-                              } catch (error: any) {
-                                console.error('获取最新节点数据失败:', error);
-                                // 如果接口调用失败,仍然使用本地更新逻辑作为后备
-                                setWorkflowWorkNodeDOList(prev => prev.map(item => {
-                                  if (item.id === nodeDO.id) {
-                                    return {
-                                      ...item,
-                                      nodeName: workflowNodeConfig.nodeName,
-                                      formId: workflowNodeConfig.submitForm ? Number(workflowNodeConfig.submitForm) : undefined,
-                                      workerUserId: workflowNodeConfig.responsible ? Number(workflowNodeConfig.responsible) : undefined,
-                                      isolationType: workflowNodeConfig.isolationType || undefined,
-                                      isolationPoints: workflowNodeConfig.isolationPoints && workflowNodeConfig.isolationPoints.length > 0 
-                                        ? JSON.stringify(workflowNodeConfig.isolationPoints) 
-                                        : undefined,
-                                      lockPerson: workflowNodeConfig.lockPerson ? String(workflowNodeConfig.lockPerson) : undefined,
-                                      notifyTime: workflowNodeConfig.notificationTime || undefined,
-                                      data: JSON.stringify((() => {
-                                        const existingData = item.data ? (typeof item.data === 'string' ? JSON.parse(item.data) : item.data) : {};
-                                        const { isolationMethod, ...restExistingData } = existingData;
-                                        return {
-                                          ...restExistingData,
-                                          label: workflowNodeConfig.nodeName,
-                                          icon: workflowNodeConfig.nodeIcon,
-                                          responsible: workflowNodeConfig.responsible ? String(workflowNodeConfig.responsible) : '',
-                                          remark: workflowNodeConfig.remark,
-                                          submitForm: workflowNodeConfig.submitForm ? String(workflowNodeConfig.submitForm) : '',
-                                          isolationType: workflowNodeConfig.isolationType,
-                                          isolationPoints: workflowNodeConfig.isolationPoints,
-                                          isolationNode: workflowNodeConfig.isolationNode,
-                                          isolationNodeUuid: workflowNodeConfig.isolationNodeUuid,
-                                          lockPerson: workflowNodeConfig.lockPerson ? String(workflowNodeConfig.lockPerson) : '',
-                                          coLockPersons: workflowNodeConfig.coLockPersons.map((id: any) => String(id)),
-                                          notificationMethods: workflowNodeConfig.notificationMethods,
-                                          notificationPerson: workflowNodeConfig.notificationPerson,
-                                          notificationTime: workflowNodeConfig.notificationTime,
-                                        };
-                                      })()),
-                                    };
-                                  }
-                                  return item;
-                                }));
-                              }
-                            }
-                          }
-                          
-                          // 缓存当前节点配置
-                          const newCache = new Map(workflowNodeConfigCache);
-                          newCache.set(selectedWorkflowNode.id, { ...workflowNodeConfig });
-                          setWorkflowNodeConfigCache(newCache);
-                          
-                          // 标记当前节点为已完成
-                          const newCompleted = new Set(completedNodeIds);
-                          newCompleted.add(selectedWorkflowNode.id);
-                          setCompletedNodeIds(newCompleted);
-                          
-                          // 更新节点保存状态缓存
-                          const newSavedStatusCache = new Map(nodeSavedStatusCache);
-                          newSavedStatusCache.set(selectedWorkflowNode.id, true);
-                          setNodeSavedStatusCache(newSavedStatusCache);
-                          
-                          // 更新节点显示
-                          const { isolationMethod, ...restData } = selectedWorkflowNode.data || {};
-                          const updatedData = {
-                            ...restData,
-                            label: workflowNodeConfig.nodeName,
-                            icon: workflowNodeConfig.nodeIcon,
-                            responsible: workflowNodeConfig.responsible,
-                            remark: workflowNodeConfig.remark,
-                            submitForm: workflowNodeConfig.submitForm,
-                            isolationType: workflowNodeConfig.isolationType,
-                            isolationPoints: workflowNodeConfig.isolationPoints,
-                            isolationNode: workflowNodeConfig.isolationNode,
-                            isolationNodeUuid: workflowNodeConfig.isolationNodeUuid,
-                            lockPerson: workflowNodeConfig.lockPerson,
-                            coLockPersons: workflowNodeConfig.coLockPersons,
-                            notificationMethods: workflowNodeConfig.notificationMethods,
-                            notificationPerson: workflowNodeConfig.notificationPerson,
-                            notificationTime: workflowNodeConfig.notificationTime,
-                            completed: true, // 标记为已完成
-                          };
-                          setWorkflowNodes((nds) =>
-                            nds.map((node) =>
-                              node.id === selectedWorkflowNode.id
-                                ? { ...node, data: updatedData, selected: false }
-                                : node
-                            )
-                          );
+                          const checkResponse = await workJobApi.checkWorkById(workflowWorkId);
+                          const checkData = (checkResponse as any)?.data || checkResponse;
+                          const errorMessages = Array.isArray(checkData) ? checkData : (checkData ? [checkData] : []);
                           
-                          // 查找下一个未完成的节点(使用新的completedSet,而不是状态中的)
-                          // 优先查找当前节点的子节点
-                          const nextNode = getNextIncompleteNode(newCompleted, selectedWorkflowNode.id);
-                          if (nextNode) {
-                            // 自动切换到下一个节点
-                            // 使用合并后的最新数据(如果有的话),否则使用状态中的数据
-                            const latestWorkflowWorkNodeDOList = mergedWorkflowWorkNodeDOList.length > 0 
-                              ? mergedWorkflowWorkNodeDOList 
-                              : workflowWorkNodeDOList;
-                            
-                            setTimeout(() => {
-                              setSelectedWorkflowNode(nextNode);
-                              const cachedConfig = newCache.get(nextNode.id);
-                              if (cachedConfig) {
-                                setWorkflowNodeConfig(cachedConfig);
-                              } else {
-                                // 从最新的 workflowWorkNodeDOList 中查找节点数据
-                                const nextNodeDO = latestWorkflowWorkNodeDOList.find(item => item.uuid === nextNode.id);
-                                if (nextNodeDO) {
-                                  let nextNodeData: any = {};
-                                  if (nextNodeDO.data) {
-                                    try {
-                                      nextNodeData = typeof nextNodeDO.data === 'string' ? JSON.parse(nextNodeDO.data) : nextNodeDO.data;
-                                    } catch (e) {
-                                      console.error('解析节点data失败:', e);
-                                    }
-                                  }
-                                  const source = nextNodeData || nextNode.data || {};
-                                  const config = nodeConfigs.find(c => c.type === (nextNodeDO.type || source.type));
-                                  setWorkflowNodeConfig({
-                                    nodeName: nextNodeDO.nodeName || source.label || config?.label || '',
-                                    nodeIcon: nextNodeDO.nodeIcon || source.icon || '',
-                                    responsible: (nextNodeDO.workerUserId !== null && nextNodeDO.workerUserId !== undefined && nextNodeDO.workerUserId !== 0) 
-                                      ? (typeof nextNodeDO.workerUserId === 'number' ? nextNodeDO.workerUserId : Number(nextNodeDO.workerUserId)) 
-                                      : (source.workerUserId && source.workerUserId !== '' && source.workerUserId !== '0') 
-                                        ? (typeof source.workerUserId === 'number' ? source.workerUserId : Number(source.workerUserId)) 
-                                        : undefined,
-                                    remark: source.remark || '',
-                                    submitForm: nextNodeDO.formId ? (typeof nextNodeDO.formId === 'number' ? nextNodeDO.formId : Number(nextNodeDO.formId)) : (source.submitForm ? (typeof source.submitForm === 'number' ? source.submitForm : Number(source.submitForm)) : undefined),
-                                    isolationType: (nextNodeDO.isolationType !== null && nextNodeDO.isolationType !== undefined) ? String(nextNodeDO.isolationType) : (source.isolationType || ''),
-                                    isolationPoints: nextNodeDO.isolationPoints ? (typeof nextNodeDO.isolationPoints === 'string' ? JSON.parse(nextNodeDO.isolationPoints) : nextNodeDO.isolationPoints) : (source.isolationPoints || []),
-                                    isolationNode: source.isolationNode || [],
-                                    isolationNodeUuid: nextNodeDO.isolationNodeUuid || source.isolationNodeUuid || '',
-                                    lockPerson: (() => {
-                                      // 优先从 nodeUserList 中提取
-                                      let lockPersonId: number | undefined = undefined;
-                                      if (nextNodeDO.nodeUserList && Array.isArray(nextNodeDO.nodeUserList)) {
-                                        const lockerUser = nextNodeDO.nodeUserList.find((user: any) => user.type === 'jtlocker');
-                                        if (lockerUser?.userId) {
-                                          lockPersonId = typeof lockerUser.userId === 'number' ? lockerUser.userId : Number(lockerUser.userId);
-                                        }
-                                      }
-                                      return lockPersonId || (source.lockPerson ? (typeof source.lockPerson === 'number' ? source.lockPerson : Number(source.lockPerson)) : undefined);
-                                    })(),
-                                    coLockPersons: (() => {
-                                      // 优先从 nodeUserList 中提取
-                                      const coLockPersonIds: number[] = [];
-                                      if (nextNodeDO.nodeUserList && Array.isArray(nextNodeDO.nodeUserList)) {
-                                        nextNodeDO.nodeUserList.forEach((user: any) => {
-                                          if (user.type === 'jtcolocker' && user.userId) {
-                                            coLockPersonIds.push(typeof user.userId === 'number' ? user.userId : Number(user.userId));
-                                          }
-                                        });
-                                      }
-                                      return 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: nextNodeDO.notifyTime || source.notificationTime || '',
-                                  });
-                                } else {
-                                  const source = nextNode.data || {};
-                                  const config = nodeConfigs.find(c => c.type === source.type);
-                                  setWorkflowNodeConfig({
-                                    nodeName: source.label || config?.label || '',
-                                    nodeIcon: source.icon || '',
-                                    responsible: source.responsible ? (typeof source.responsible === 'number' ? source.responsible : Number(source.responsible)) : undefined,
-                                    remark: source.remark || '',
-                                    submitForm: source.submitForm ? (typeof source.submitForm === 'number' ? source.submitForm : Number(source.submitForm)) : undefined,
-                                    isolationType: source.isolationType || '',
-                                    isolationPoints: source.isolationPoints || [],
-                                    isolationNode: source.isolationNode || [],
-                                    isolationNodeUuid: source.isolationNodeUuid || '',
-                                    lockPerson: source.lockPerson || '',
-                                    coLockPersons: source.coLockPersons || [],
-                                    notificationMethods: source.notificationMethods || {
-                                      sms: false,
-                                      message: false,
-                                      email: false,
-                                      app: false,
-                                    },
-                                    notificationPerson: source.notificationPerson || '',
-                                    notificationTime: source.notificationTime || '',
-                                  });
-                                }
-                              }
-                              // 高亮下一个节点
-                              setWorkflowNodes((nds) =>
-                                nds.map((node) =>
-                                  node.id === nextNode.id
-                                    ? { ...node, selected: true }
-                                    : { ...node, selected: false }
-                                )
-                              );
-                              // 滚动到节点位置
-                              if (workflowReactFlowInstance) {
-                                workflowReactFlowInstance.fitView({ padding: 0.2, duration: 300, nodes: [nextNode] });
-                              }
-                            }, 100);
-                            message.success('节点配置已保存,请继续配置下一个节点');
+                          // 如果返回的错误信息数组为空或null,表示所有节点配置完毕,可以进入下一个tab
+                          if (!errorMessages || errorMessages.length === 0) {
+                            message.success('所有节点配置已完成');
+                            // 进入下一个tab
+                            setWorkJobStep(2);
                           } else {
-                            // 所有节点都已完成,调用检查接口
-                            if (workflowWorkId) {
-                              try {
-                                const checkResponse = await workJobApi.checkWorkById(workflowWorkId);
-                                const checkData = (checkResponse as any)?.data || checkResponse;
-                                const errorMessages = Array.isArray(checkData) ? checkData : (checkData ? [checkData] : []);
-                                
-                                // 如果返回的错误信息数组为空或null,表示所有节点配置完毕
-                                if (!errorMessages || errorMessages.length === 0) {
-                                  message.success('所有节点配置已完成');
-                                  // 延迟一下再跳转,让用户看到成功提示
-                                  setTimeout(() => {
-                                    setWorkJobStep(2);
-                                  }, 500);
-                                } else {
-                                  // 有错误信息,显示弹框
-                                  Modal.warning({
-                                    title: '节点配置未完成',
-                                    width: 500,
-                                    content: (
-                                      <div style={{ marginTop: 16 }}>
-                                        <p style={{ marginBottom: 12, color: '#666' }}>以下节点配置不完整,请检查:</p>
-                                        <ul style={{ margin: 0, paddingLeft: 20 }}>
-                                          {errorMessages.map((msg: string, index: number) => (
-                                            <li key={index} style={{ marginBottom: 8, color: '#ff4d4f' }}>
-                                              {msg}
-                                            </li>
-                                          ))}
-                                        </ul>
-                                      </div>
-                                    ),
-                                    okText: '我知道了',
-                                  });
-                                }
-                              } catch (error: any) {
-                                console.error('检查作业失败:', error);
-                                message.error(error?.message || '检查作业失败');
-                              }
-                            } else {
-                              message.success('所有节点配置已完成');
-                              // 延迟一下再跳转,让用户看到成功提示
-                              setTimeout(() => {
-                                setWorkJobStep(2);
-                              }, 500);
-                            }
+                            // 有错误信息,显示弹框,不能进入下一个tab
+                            Modal.warning({
+                              title: '节点配置未完成',
+                              width: 500,
+                              content: (
+                                <div style={{ marginTop: 16 }}>
+                                  <p style={{ marginBottom: 12, color: '#666' }}>以下节点配置不完整,请检查:</p>
+                                  <ul style={{ margin: 0, paddingLeft: 20 }}>
+                                    {errorMessages.map((msg: string, index: number) => (
+                                      <li key={index} style={{ marginBottom: 8, color: '#ff4d4f' }}>
+                                        {msg}
+                                      </li>
+                                    ))}
+                                  </ul>
+                                </div>
+                              ),
+                              okText: '我知道了',
+                            });
                           }
                         } catch (error: any) {
-                          console.error('保存节点配置失败:', error);
-                          message.error(error?.message || '保存节点配置失败');
+                          console.error('检查作业失败:', error);
+                          message.error(error?.message || '检查作业失败');
                         }
                       }}
                     >
-                      {completedNodeIds.size === workflowNodes.length && workflowNodes.length > 0
-                        ? '完成流程设置'
-                        : '保存'}
+                      下一步
                     </Button>
                     {/* <Button
                       type="default"

+ 52 - 18
src/components/MyTask.tsx

@@ -1386,8 +1386,13 @@ export default function MyTask() {
   };
 
   // 获取任务状态样式(审批状态)
-  const getTaskStatusClassName = (status: string | number | undefined): string => {
-    if (!status) return 'bg-gray-100 text-gray-600';
+  const getTaskStatusStyle = (status: string | number | undefined): React.CSSProperties => {
+    if (!status) {
+      return {
+        backgroundColor: '#e5e5e5',
+        color: '#333333',
+      };
+    }
     const statusStr = String(status).toLowerCase();
     
     // 先获取状态文本,根据文本判断颜色
@@ -1395,24 +1400,38 @@ export default function MyTask() {
     const statusTextLower = statusText.toLowerCase();
     
     // 根据状态文本判断颜色
-    if (statusTextLower.includes('未开始') || statusTextLower.includes('未审核') || statusTextLower.includes('待开始')) {
-      return 'bg-gray-100 text-gray-600';
+    // 待执行、待发布:灰色 #e5e5e5
+    if (statusTextLower.includes('待执行') || statusTextLower.includes('待发布') || 
+        statusTextLower.includes('未开始') || statusTextLower.includes('未审核') || 
+        statusTextLower.includes('待开始') || statusTextLower.includes('unaudited')) {
+      return {
+        backgroundColor: '#e5e5e5',
+        color: '#333333',
+      };
     }
-    if (statusTextLower.includes('进行中') || statusTextLower.includes('待审核') || statusTextLower.includes('pending')) {
-      return 'bg-blue-100 text-blue-700';
+    // 进行中:蓝色 #1677ff
+    if (statusTextLower.includes('进行中') || statusTextLower.includes('待审核') || 
+        statusTextLower.includes('pending') || statusTextLower.includes('执行中')) {
+      return {
+        backgroundColor: '#1677ff',
+        color: '#ffffff',
+      };
     }
-    if (statusTextLower.includes('已完成') || statusTextLower.includes('已通过') || statusTextLower.includes('approved') || statusTextLower.includes('完成')) {
-      return 'bg-green-100 text-green-700';
+    // 已完成:绿色 #0acb57
+    if (statusTextLower.includes('已完成') || statusTextLower.includes('已通过') || 
+        statusTextLower.includes('approved') || statusTextLower.includes('完成') ||
+        statusTextLower.includes('执行完成')) {
+      return {
+        backgroundColor: '#0acb57',
+        color: '#ffffff',
+      };
     }
     
-    // 如果没有匹配到,使用原来的逻辑
-    const statusMap: Record<string, string> = {
-      'pending': 'bg-blue-100 text-blue-700',
-      'approved': 'bg-green-100 text-green-700',
-      'rejected': 'bg-red-100 text-red-700',
-      'unaudited': 'bg-gray-100 text-gray-600',
+    // 如果没有匹配到,默认灰色
+    return {
+      backgroundColor: '#e5e5e5',
+      color: '#333333',
     };
-    return statusMap[statusStr] || 'bg-gray-100 text-gray-600';
   };
 
   // 表格列配置
@@ -1426,7 +1445,14 @@ export default function MyTask() {
         if (!text) return '-';
         return (
           <span
-            className="text-blue-600 hover:text-blue-800 cursor-pointer hover:underline"
+            className="cursor-pointer hover:underline"
+            style={{ color: '#1677ff' }}
+            onMouseEnter={(e) => {
+              e.currentTarget.style.textDecoration = 'underline';
+            }}
+            onMouseLeave={(e) => {
+              e.currentTarget.style.textDecoration = 'none';
+            }}
             onClick={(e) => {
               e.stopPropagation();
               e.preventDefault();
@@ -1448,7 +1474,14 @@ export default function MyTask() {
         if (!text) return '-';
         return (
           <span
-            className="text-blue-600 hover:text-blue-800 cursor-pointer hover:underline"
+            className="cursor-pointer hover:underline"
+            style={{ color: '#1677ff' }}
+            onMouseEnter={(e) => {
+              e.currentTarget.style.textDecoration = 'underline';
+            }}
+            onMouseLeave={(e) => {
+              e.currentTarget.style.textDecoration = 'none';
+            }}
             onClick={(e) => {
               e.stopPropagation();
               e.preventDefault();
@@ -1502,7 +1535,8 @@ export default function MyTask() {
         const taskStatus = status || record.taskStatus || record.status;
         return (
           <span
-            className={`inline-flex px-3 py-1 rounded-lg text-xs ${getTaskStatusClassName(taskStatus)}`}
+            className="inline-flex px-3 py-1 rounded-lg text-xs"
+            style={getTaskStatusStyle(taskStatus)}
           >
             {getTaskStatusText(taskStatus)}
           </span>

+ 59 - 27
src/components/ProcessDesigner.tsx

@@ -3946,7 +3946,7 @@ export default function ProcessDesigner() {
               switch (field.type) {
                 case 'textarea':
                   return (
-                    <div key={field.id} style={spanStyle}>
+                    <div key={field.id} style={spanStyle} className="form-item-wrapper">
                       <AntdForm.Item
                         label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
                         name={field.name || field.field}
@@ -3956,34 +3956,33 @@ export default function ProcessDesigner() {
                         <Input.TextArea 
                           placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请输入'} 
                           rows={4}
-                          disabled
                         />
                       </AntdForm.Item>
                     </div>
                   );
                 case 'number':
                   return (
-                    <div key={field.id} style={spanStyle}>
+                    <div key={field.id} style={spanStyle} className="form-item-wrapper">
                       <AntdForm.Item
                         label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
                         name={field.name || field.field}
                         required={field.required && !formConfig.hideRequiredMark}
                         help={field.hint}
                       >
-                        <InputNumber style={{ width: '100%' }} placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请输入'} disabled />
+                        <InputNumber style={{ width: '100%' }} placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请输入'} />
                       </AntdForm.Item>
                     </div>
                   );
                 case 'select':
                   return (
-                    <div key={field.id} style={spanStyle}>
+                    <div key={field.id} style={spanStyle} className="form-item-wrapper">
                       <AntdForm.Item
                         label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
                         name={field.name || field.field}
                         required={field.required && !formConfig.hideRequiredMark}
                         help={field.hint}
                       >
-                        <Select placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择'} disabled>
+                        <Select placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择'}>
                           {(field.options || []).map((opt: any, idx: number) => (
                             <Select.Option key={idx} value={opt.value}>{opt.label}</Select.Option>
                           ))}
@@ -3993,39 +3992,39 @@ export default function ProcessDesigner() {
                   );
                 case 'date':
                   return (
-                    <div key={field.id} style={spanStyle}>
+                    <div key={field.id} style={spanStyle} className="form-item-wrapper">
                       <AntdForm.Item
                         label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
                         name={field.name || field.field}
                         required={field.required && !formConfig.hideRequiredMark}
                         help={field.hint}
                       >
-                        <DatePicker style={{ width: '100%' }} placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择日期'} disabled />
+                        <DatePicker style={{ width: '100%' }} placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择日期'} />
                       </AntdForm.Item>
                     </div>
                   );
                 case 'switch':
                   return (
-                    <div key={field.id} style={spanStyle}>
+                    <div key={field.id} style={spanStyle} className="form-item-wrapper">
                       <AntdForm.Item
                         label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
                         name={field.name || field.field}
                         valuePropName="checked"
                       >
-                        <Switch disabled />
+                        <Switch />
                       </AntdForm.Item>
                     </div>
                   );
                 case 'radio':
                   return (
-                    <div key={field.id} style={spanStyle}>
+                    <div key={field.id} style={spanStyle} className="form-item-wrapper">
                       <AntdForm.Item
                         label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
                         name={field.name || field.field}
                         required={field.required && !formConfig.hideRequiredMark}
                         help={field.hint}
                       >
-                        <Radio.Group disabled>
+                        <Radio.Group>
                           {(field.options || []).map((opt: any, idx: number) => (
                             <Radio key={idx} value={opt.value}>{opt.label}</Radio>
                           ))}
@@ -4035,14 +4034,14 @@ export default function ProcessDesigner() {
                   );
                 case 'checkbox':
                   return (
-                    <div key={field.id} style={spanStyle}>
+                    <div key={field.id} style={spanStyle} className="form-item-wrapper">
                       <AntdForm.Item
                         label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
                         name={field.name || field.field}
                         required={field.required && !formConfig.hideRequiredMark}
                         help={field.hint}
                       >
-                        <Checkbox.Group disabled>
+                        <Checkbox.Group>
                           {(field.options || []).map((opt: any, idx: number) => (
                             <Checkbox key={idx} value={opt.value}>{opt.label}</Checkbox>
                           ))}
@@ -4052,7 +4051,7 @@ export default function ProcessDesigner() {
                   );
                 default:
                   return (
-                    <div key={field.id} style={spanStyle}>
+                    <div key={field.id} style={spanStyle} className="form-item-wrapper">
                       <AntdForm.Item
                         label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
                         name={field.name || field.field}
@@ -4062,7 +4061,6 @@ export default function ProcessDesigner() {
                         <Input 
                           type={field.inputType || 'text'}
                           placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请输入'}
-                          disabled
                         />
                       </AntdForm.Item>
                     </div>
@@ -4070,18 +4068,52 @@ export default function ProcessDesigner() {
               }
             };
 
+            // 预览模式下,强制使用更小的 label 宽度,让输入区域更宽
+            const previewLabelSpan = formConfig.labelPosition !== 'top' 
+              ? (formConfig.labelWidth ? Math.min(Math.floor(formConfig.labelWidth / 8), 4) : 4)
+              : undefined;
+            const previewWrapperSpan = previewLabelSpan ? 24 - previewLabelSpan : undefined;
+
             return (
-              <AntdForm
-                form={formPreviewForm}
-                layout={formConfig.labelPosition === 'top' ? 'vertical' : 'horizontal'}
-                size={formConfig.formSize || 'middle'}
-                labelCol={formConfig.labelPosition !== 'top' ? { span: formConfig.labelWidth ? Math.floor(formConfig.labelWidth / 8) : 6 } : undefined}
-                wrapperCol={formConfig.labelPosition !== 'top' ? { span: 24 - (formConfig.labelWidth ? Math.floor(formConfig.labelWidth / 8) : 6) } : undefined}
-              >
-                <div style={layoutColumns > 1 ? { display: 'grid', gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`, gap: '16px' } : {}}>
-                  {(formPreviewDetailData.rule || []).map((field: any) => renderFieldPreview(field))}
-                </div>
-              </AntdForm>
+              <>
+                <style>{`
+                  .form-preview-modal .ant-form-item-label {
+                    flex: 0 0 auto !important;
+                    max-width: none !important;
+                  }
+                  .form-preview-modal .ant-form-item-label > label {
+                    font-size: 14px !important;
+                    white-space: nowrap !important;
+                    overflow: hidden !important;
+                    text-overflow: ellipsis !important;
+                  }
+                  .form-preview-modal .ant-form-item-control {
+                    flex: 1 1 auto !important;
+                    min-width: 0 !important;
+                  }
+                  .form-preview-modal .form-item-wrapper {
+                    background-color: #f5f5f5;
+                    border-radius: 8px;
+                    padding: 12px 16px;
+                    margin-bottom: 16px;
+                  }
+                  .form-preview-modal .form-item-wrapper .ant-form-item {
+                    margin-bottom: 0;
+                  }
+                `}</style>
+                <AntdForm
+                  form={formPreviewForm}
+                  layout={formConfig.labelPosition === 'top' ? 'vertical' : 'horizontal'}
+                  size={formConfig.formSize || 'middle'}
+                  labelCol={previewLabelSpan ? { span: previewLabelSpan } : undefined}
+                  wrapperCol={previewWrapperSpan ? { span: previewWrapperSpan } : undefined}
+                  className="form-preview-modal"
+                >
+                  <div style={layoutColumns > 1 ? { display: 'grid', gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`, gap: '16px' } : {}}>
+                    {(formPreviewDetailData.rule || []).map((field: any) => renderFieldPreview(field))}
+                  </div>
+                </AntdForm>
+              </>
             );
           })()}
         </div>

+ 52 - 18
src/components/TaskManagement.tsx

@@ -1386,8 +1386,13 @@ export default function TaskManagement() {
   };
 
   // 获取任务状态样式(审批状态)
-  const getTaskStatusClassName = (status: string | number | undefined): string => {
-    if (!status) return 'bg-gray-100 text-gray-600';
+  const getTaskStatusStyle = (status: string | number | undefined): React.CSSProperties => {
+    if (!status) {
+      return {
+        backgroundColor: '#e5e5e5',
+        color: '#333333',
+      };
+    }
     const statusStr = String(status).toLowerCase();
     
     // 先获取状态文本,根据文本判断颜色
@@ -1395,24 +1400,38 @@ export default function TaskManagement() {
     const statusTextLower = statusText.toLowerCase();
     
     // 根据状态文本判断颜色
-    if (statusTextLower.includes('未开始') || statusTextLower.includes('未审核') || statusTextLower.includes('待开始')) {
-      return 'bg-gray-100 text-gray-600';
+    // 待执行、待发布:灰色 #e5e5e5
+    if (statusTextLower.includes('待执行') || statusTextLower.includes('待发布') || 
+        statusTextLower.includes('未开始') || statusTextLower.includes('未审核') || 
+        statusTextLower.includes('待开始') || statusTextLower.includes('unaudited')) {
+      return {
+        backgroundColor: '#e5e5e5',
+        color: '#333333',
+      };
     }
-    if (statusTextLower.includes('进行中') || statusTextLower.includes('待审核') || statusTextLower.includes('pending')) {
-      return 'bg-blue-100 text-blue-700';
+    // 进行中:蓝色 #1677ff
+    if (statusTextLower.includes('进行中') || statusTextLower.includes('待审核') || 
+        statusTextLower.includes('pending') || statusTextLower.includes('执行中')) {
+      return {
+        backgroundColor: '#1677ff',
+        color: '#ffffff',
+      };
     }
-    if (statusTextLower.includes('已完成') || statusTextLower.includes('已通过') || statusTextLower.includes('approved') || statusTextLower.includes('完成')) {
-      return 'bg-green-100 text-green-700';
+    // 已完成:绿色 #0acb57
+    if (statusTextLower.includes('已完成') || statusTextLower.includes('已通过') || 
+        statusTextLower.includes('approved') || statusTextLower.includes('完成') ||
+        statusTextLower.includes('执行完成')) {
+      return {
+        backgroundColor: '#0acb57',
+        color: '#ffffff',
+      };
     }
     
-    // 如果没有匹配到,使用原来的逻辑
-    const statusMap: Record<string, string> = {
-      'pending': 'bg-blue-100 text-blue-700',
-      'approved': 'bg-green-100 text-green-700',
-      'rejected': 'bg-red-100 text-red-700',
-      'unaudited': 'bg-gray-100 text-gray-600',
+    // 如果没有匹配到,默认灰色
+    return {
+      backgroundColor: '#e5e5e5',
+      color: '#333333',
     };
-    return statusMap[statusStr] || 'bg-gray-100 text-gray-600';
   };
 
   // 表格列配置
@@ -1426,7 +1445,14 @@ export default function TaskManagement() {
         if (!text) return '-';
         return (
           <span
-            className="text-blue-600 hover:text-blue-800 cursor-pointer hover:underline"
+            className="cursor-pointer hover:underline"
+            style={{ color: '#1677ff' }}
+            onMouseEnter={(e) => {
+              e.currentTarget.style.textDecoration = 'underline';
+            }}
+            onMouseLeave={(e) => {
+              e.currentTarget.style.textDecoration = 'none';
+            }}
             onClick={(e) => {
               e.stopPropagation();
               e.preventDefault();
@@ -1448,7 +1474,14 @@ export default function TaskManagement() {
         if (!text) return '-';
         return (
           <span
-            className="text-blue-600 hover:text-blue-800 cursor-pointer hover:underline"
+            className="cursor-pointer hover:underline"
+            style={{ color: '#1677ff' }}
+            onMouseEnter={(e) => {
+              e.currentTarget.style.textDecoration = 'underline';
+            }}
+            onMouseLeave={(e) => {
+              e.currentTarget.style.textDecoration = 'none';
+            }}
             onClick={(e) => {
               e.stopPropagation();
               e.preventDefault();
@@ -1502,7 +1535,8 @@ export default function TaskManagement() {
         const taskStatus = status || record.taskStatus || record.status;
         return (
           <span
-            className={`inline-flex px-3 py-1 rounded-lg text-xs ${getTaskStatusClassName(taskStatus)}`}
+            className="inline-flex px-3 py-1 rounded-lg text-xs"
+            style={getTaskStatusStyle(taskStatus)}
           >
             {getTaskStatusText(taskStatus)}
           </span>

+ 78 - 12
src/components/WorkJobDetail.tsx

@@ -20,6 +20,7 @@ import { Timeline, Badge, Card, Collapse, Tag } from 'antd';
 import { CheckCircleOutlined, ClockCircleOutlined, SyncOutlined } from '@ant-design/icons';
 import { workJobApi, WorkJobVO, WorkflowWorkNodeDO } from '../api/WorkJob';
 import { formatDateWithFormat } from '../utils/formatTime';
+import { dictDataApi, DictDataVO } from '../api/DictData';
 
 interface WorkflowStep {
   id: string;
@@ -1120,6 +1121,9 @@ export default function WorkJobDetail() {
   const [jobDetail, setJobDetail] = useState<WorkJobVO | null>(null);
   const [loading, setLoading] = useState(true);
   
+  // 作业状态字典数据
+  const [jobStatusDictList, setJobStatusDictList] = useState<DictDataVO[]>([]);
+  
   // 流程树状态
   const [workflowTree, setWorkflowTree] = useState<WorkflowNode[]>([]);
   const [expandedBranches, setExpandedBranches] = useState<Set<string>>(new Set());
@@ -1127,8 +1131,26 @@ export default function WorkJobDetail() {
   // 展开的分支组(按 parentUuid)
   const [expandedBranchGroups, setExpandedBranchGroups] = useState<Set<string>>(new Set());
 
+  // 获取作业状态字典数据
+  const getJobStatusDictList = async () => {
+    try {
+      const response = await dictDataApi.getDictDataPage({
+        pageNo: 1,
+        pageSize: -1,
+        dictType: 'job_status',
+      });
+      const data = (response as any)?.data || response;
+      const list = data?.list || [];
+      setJobStatusDictList(list);
+    } catch (error: any) {
+      console.error('获取作业状态字典失败:', error);
+      setJobStatusDictList([]);
+    }
+  };
+
   // 获取作业详情
   useEffect(() => {
+    getJobStatusDictList();
     if (jobId) {
       loadJobDetail();
     }
@@ -1253,8 +1275,15 @@ export default function WorkJobDetail() {
     });
   };
 
-  // 格式化状态文本
+  // 格式化状态文本(使用字典)
   const getStatusText = (status: string | number | undefined): string => {
+    if (!status) return '未知';
+    const statusStr = String(status).toLowerCase();
+    const statusItem = jobStatusDictList.find(item => String(item.value).toLowerCase() === statusStr);
+    if (statusItem) {
+      return statusItem.label || '未知';
+    }
+    // 如果没有找到字典值,使用默认映射
     const statusMap: Record<string, string> = {
       'pending': '待执行',
       'running': '执行中',
@@ -1266,22 +1295,56 @@ export default function WorkJobDetail() {
       '待开始': '待执行',
       '已取消': '已取消',
     };
-    return statusMap[String(status).toLowerCase()] || String(status) || '未知';
+    return statusMap[statusStr] || String(status) || '未知';
   };
 
-  // 格式化状态样式
-  const getStatusClassName = (status: string | number | undefined): string => {
+  // 获取作业状态样式(与列表颜色一致)
+  const getStatusStyle = (status: string | number | undefined): React.CSSProperties => {
+    if (!status) {
+      return {
+        backgroundColor: '#e5e5e5',
+        color: '#333333',
+      };
+    }
+    
+    // 从字典中查找状态文本
     const statusStr = String(status).toLowerCase();
-    if (statusStr === 'running' || statusStr === '执行中') {
-      return 'bg-blue-100 text-blue-700';
+    const statusItem = jobStatusDictList.find(item => String(item.value).toLowerCase() === statusStr);
+    const statusText = statusItem ? (statusItem.label || '') : String(status || '');
+    const statusTextLower = statusText.toLowerCase();
+    
+    // 根据状态文本判断颜色
+    // 待执行、待发布:灰色 #e5e5e5
+    if (statusTextLower.includes('待执行') || statusTextLower.includes('待发布') || 
+        statusTextLower.includes('pending') || statusTextLower.includes('unreleased') ||
+        statusTextLower.includes('待开始')) {
+      return {
+        backgroundColor: '#e5e5e5',
+        color: '#333333',
+      };
     }
-    if (statusStr === 'completed' || statusStr === '执行完成' || statusStr === '已完成') {
-      return 'bg-green-100 text-green-700';
+    // 进行中:蓝色 #1677ff
+    if (statusTextLower.includes('进行中') || statusTextLower.includes('执行中') || 
+        statusTextLower.includes('running') || statusTextLower.includes('in_progress')) {
+      return {
+        backgroundColor: '#1677ff',
+        color: '#ffffff',
+      };
     }
-    if (statusStr === 'pending' || statusStr === '待执行' || statusStr === '待开始') {
-      return 'bg-gray-100 text-gray-700';
+    // 已完成:绿色 #0acb57
+    if (statusTextLower.includes('已完成') || statusTextLower.includes('执行完成') || 
+        statusTextLower.includes('completed') || statusTextLower.includes('完成')) {
+      return {
+        backgroundColor: '#0acb57',
+        color: '#ffffff',
+      };
     }
-    return 'bg-gray-100 text-gray-700';
+    
+    // 如果没有匹配到,默认灰色
+    return {
+      backgroundColor: '#e5e5e5',
+      color: '#333333',
+    };
   };
 
   // 构建工作流步骤
@@ -1475,7 +1538,10 @@ export default function WorkJobDetail() {
             <h1 className="text-2xl font-semibold text-gray-900">
                   {jobDetail?.name || '-'}
             </h1>
-                <span className={`inline-flex px-4 py-1.5 rounded-full text-sm font-medium ${getStatusClassName(jobDetail?.status)}`}>
+                <span 
+                  className="inline-flex px-4 py-1.5 rounded-full text-sm font-medium"
+                  style={getStatusStyle(jobDetail?.status)}
+                >
                   {getStatusText(jobDetail?.status)}
             </span>
           </div>