Эх сурвалжийг харах

修复我的任务时间类型的表单 和作业详情

pm 4 сар өмнө
parent
commit
75831f6bfc

+ 1 - 0
src/api/bpm/form.ts

@@ -14,6 +14,7 @@ export interface FormPageParams {
   pageNo: number;
   pageSize: number;
   name?: string;
+  status?: number;
 }
 
 export interface FormPageResponse {

+ 87 - 40
src/components/IsolationWork.tsx

@@ -1028,7 +1028,10 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
       
       // 流程设计数据在 content 字段中(JSON字符串)
       let jsonData: any = null;
-      if (workflow.content) {
+      // 检查 content 是否为 null 或空字符串
+      const hasContent = workflow.content && workflow.content !== null && workflow.content !== '';
+      
+      if (hasContent) {
         try {
           jsonData = typeof workflow.content === 'string' 
             ? JSON.parse(workflow.content) 
@@ -1039,30 +1042,25 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
           console.error('解析JSON失败:', parseError);
           console.error('原始content:', workflow.content);
           message.error('流程设计JSON格式错误');
+          // 解析失败时清空流程
+          setWorkflowJson(null);
+          setWorkflowNodes([]);
+          setWorkflowEdges([]);
+          setWorkflowNodeConfigCache(new Map());
+          setCompletedNodeIds(new Set());
+          setSelectedWorkflowNode(null);
           return;
         }
       } else {
-        console.warn('流程设计content为空,使用备用JSON');
-        // 使用备用JSON
-        jsonData = {
-          nodes: [
-            { id: '1', type: 'createJob', label: '提交/开始', position: { x: 100, y: 100 } },
-            { id: '2', type: 'review', label: '审核/确认', position: { x: 300, y: 100 } },
-            { id: '3', type: 'inputInfo', label: '录入/表单', position: { x: 500, y: 100 } },
-            { id: '4', type: 'isolation', label: '隔离/方案', position: { x: 700, y: 100 } },
-            { id: '5', type: 'releaseIsolation', label: '取锁/共锁', position: { x: 700, y: 300 } },
-            { id: '6', type: 'complete', label: '完成/结束', position: { x: 900, y: 300 } },
-          ],
-          edges: [
-            { id: 'e1-2', source: '1', target: '2' },
-            { id: 'e2-3', source: '2', target: '3' },
-            { id: 'e3-4', source: '3', target: '4' },
-            { id: 'e4-5', source: '4', target: '5' },
-            { id: 'e5-6', source: '5', target: '6' },
-            { id: 'e2-5', source: '2', target: '5' },
-          ],
-        };
-        setWorkflowJson(jsonData);
+        // content 为 null 或空时,清空流程,不使用备用数据
+        console.warn('流程设计content为空或null,清空流程');
+        setWorkflowJson(null);
+        setWorkflowNodes([]);
+        setWorkflowEdges([]);
+        setWorkflowNodeConfigCache(new Map());
+        setCompletedNodeIds(new Set());
+        setSelectedWorkflowNode(null);
+        return;
       }
       
       // 解析并渲染到ReactFlow
@@ -1265,7 +1263,8 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
       
       const loadFormList = async () => {
         try {
-          const res = await getFormPage({ pageNo: 1, pageSize: -1 });
+          // 只获取开启状态(status=1)的表单
+          const res = await getFormPage({ pageNo: 1, pageSize: -1, status: 1 });
           setWorkflowFormList(res.list || []);
         } catch (error) {
           console.error('加载表单列表失败:', error);
@@ -1586,7 +1585,11 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
       
       // 如果有流程设计内容,加载流程设计JSON
       // 优先使用 designContent(如果存在),否则使用 designId 调用接口
-      if (detail.designContent) {
+      // 但如果 designContent 和 workflowWorkNodeDOList 都为 null,应该清空流程
+      const hasDesignContent = detail.designContent && detail.designContent !== null;
+      const hasWorkflowNodes = detail.workflowWorkNodeDOList && Array.isArray(detail.workflowWorkNodeDOList) && detail.workflowWorkNodeDOList.length > 0;
+      
+      if (hasDesignContent) {
         try {
           // 如果详情中直接包含流程设计内容,直接解析并设置
           const jsonData = typeof detail.designContent === 'string' 
@@ -1802,21 +1805,35 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
           }
         } catch (error: any) {
           console.error('解析流程设计内容失败:', error);
-          // 如果解析失败,尝试使用 designId 调用接口
-          if (detail.designId) {
-            try {
-              await loadWorkflowJson(detail.designId);
-            } catch (loadError: any) {
-              console.error('加载流程设计失败:', loadError);
-            }
-          }
+          // 如果解析失败,清空流程
+          setWorkflowJson(null);
+          setWorkflowNodes([]);
+          setWorkflowEdges([]);
+          setWorkflowNodeConfigCache(new Map());
+          setCompletedNodeIds(new Set());
         }
-      } else if (detail.designId) {
+      } else if (!hasDesignContent && !hasWorkflowNodes) {
+        // 如果 designContent 和 workflowWorkNodeDOList 都为 null,清空流程
+        console.log('designContent 和 workflowWorkNodeDOList 都为 null,清空流程');
+        setWorkflowJson(null);
+        setWorkflowNodes([]);
+        setWorkflowEdges([]);
+        setWorkflowNodeConfigCache(new Map());
+        setCompletedNodeIds(new Set());
+        setSelectedWorkflowNode(null);
+      } else if (detail.designId && hasWorkflowNodes) {
+        // 只有在有 workflowWorkNodeDOList 数据时,才尝试通过 designId 加载模板
+        // 这样可以避免在编辑时,即使没有流程数据也加载模板
         try {
           await loadWorkflowJson(detail.designId);
         } catch (error: any) {
           console.error('加载流程设计失败:', error);
-          // 不阻止弹框打开,只是不加载流程设计
+          // 加载失败时也清空流程
+          setWorkflowJson(null);
+          setWorkflowNodes([]);
+          setWorkflowEdges([]);
+          setWorkflowNodeConfigCache(new Map());
+          setCompletedNodeIds(new Set());
         }
       }
       
@@ -1845,14 +1862,29 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
     try {
       const response = await workflowDesignApi.selectWorkflowDesignById(workflowId);
       const workflow = response as any;
-      if (workflow.content) {
-        const jsonData = typeof workflow.content === 'string' 
-          ? JSON.parse(workflow.content) 
-          : workflow.content;
-        setViewWorkflowJson(jsonData);
+      // 检查 content 是否为 null 或空字符串
+      const hasContent = workflow.content && workflow.content !== null && workflow.content !== '';
+      
+      if (hasContent) {
+        try {
+          const jsonData = typeof workflow.content === 'string' 
+            ? JSON.parse(workflow.content) 
+            : workflow.content;
+          setViewWorkflowJson(jsonData);
+        } catch (parseError) {
+          console.error('解析流程设计JSON失败:', parseError);
+          // 解析失败时清空
+          setViewWorkflowJson(null);
+        }
+      } else {
+        // content 为 null 或空时,清空流程
+        console.warn('流程设计content为空或null,清空查看模式的流程');
+        setViewWorkflowJson(null);
       }
     } catch (error: any) {
       console.error('加载流程设计失败:', error);
+      // 加载失败时也清空
+      setViewWorkflowJson(null);
     }
   };
 
@@ -2531,7 +2563,22 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
       dataIndex: 'description',
       width: '26%',
       ellipsis: true,
-      render: (description: string, record: WorkJobVO) => description || record.content || '-',
+      render: (description: string, record: WorkJobVO) => {
+        const content = description || record.content || '-';
+        if (content === '-') return content;
+        
+        // 如果内容超过10个字,截取前10个字并用 Tooltip 显示完整内容
+        if (content.length > 10) {
+          const truncated = content.substring(0, 10) + '...';
+          return (
+            <Tooltip title={content} placement="topLeft">
+              <span style={{ cursor: 'pointer' }}>{truncated}</span>
+            </Tooltip>
+          );
+        }
+        
+        return content;
+      },
     },
     {
       title: '当前节点',

+ 163 - 5
src/components/MyTask.tsx

@@ -32,9 +32,22 @@ const convertDateValues = (formValues: any, fields: any[]): any => {
     
     if (fieldType === 'date' || fieldType === 'datetime') {
       // 日期或日期时间类型
-      if (typeof value === 'string' && value.trim() !== '') {
+      // 优先处理毫秒级时间戳
+      if (typeof value === 'number') {
         const dayjsValue = dayjs(value);
         convertedValues[fieldName] = dayjsValue.isValid() ? dayjsValue : undefined;
+      } else if (typeof value === 'string' && value.trim() !== '') {
+        // 尝试判断是否为时间戳字符串
+        const numValue = Number(value);
+        if (!isNaN(numValue) && numValue > 0) {
+          // 可能是时间戳字符串,转换为数字再处理
+          const dayjsValue = dayjs(numValue);
+          convertedValues[fieldName] = dayjsValue.isValid() ? dayjsValue : undefined;
+        } else {
+          // 普通日期字符串
+          const dayjsValue = dayjs(value);
+          convertedValues[fieldName] = dayjsValue.isValid() ? dayjsValue : undefined;
+        }
       } else if (value && typeof value === 'object' && 'isValid' in value) {
         // 已经是 dayjs 对象
         convertedValues[fieldName] = value;
@@ -45,10 +58,41 @@ const convertDateValues = (formValues: any, fields: any[]): any => {
       // 日期范围类型
       if (Array.isArray(value) && value.length === 2) {
         const [start, end] = value;
-        const startDayjs = typeof start === 'string' && start ? dayjs(start) : (start && typeof start === 'object' && 'isValid' in start ? start : null);
-        const endDayjs = typeof end === 'string' && end ? dayjs(end) : (end && typeof end === 'object' && 'isValid' in end ? end : null);
-        if (startDayjs && (typeof startDayjs === 'object' && 'isValid' in startDayjs ? startDayjs.isValid() : true) && 
-            endDayjs && (typeof endDayjs === 'object' && 'isValid' in endDayjs ? endDayjs.isValid() : true)) {
+        // 处理开始时间
+        let startDayjs: any = null;
+        if (typeof start === 'number') {
+          startDayjs = dayjs(start);
+          startDayjs = startDayjs.isValid() ? startDayjs : null;
+        } else if (typeof start === 'string' && start) {
+          const numValue = Number(start);
+          if (!isNaN(numValue) && numValue > 0) {
+            startDayjs = dayjs(numValue);
+            startDayjs = startDayjs.isValid() ? startDayjs : null;
+          } else {
+            startDayjs = dayjs(start);
+            startDayjs = startDayjs.isValid() ? startDayjs : null;
+          }
+        } else if (start && typeof start === 'object' && 'isValid' in start) {
+          startDayjs = start;
+        }
+        // 处理结束时间
+        let endDayjs: any = null;
+        if (typeof end === 'number') {
+          endDayjs = dayjs(end);
+          endDayjs = endDayjs.isValid() ? endDayjs : null;
+        } else if (typeof end === 'string' && end) {
+          const numValue = Number(end);
+          if (!isNaN(numValue) && numValue > 0) {
+            endDayjs = dayjs(numValue);
+            endDayjs = endDayjs.isValid() ? endDayjs : null;
+          } else {
+            endDayjs = dayjs(end);
+            endDayjs = endDayjs.isValid() ? endDayjs : null;
+          }
+        } else if (end && typeof end === 'object' && 'isValid' in end) {
+          endDayjs = end;
+        }
+        if (startDayjs && endDayjs) {
           convertedValues[fieldName] = [startDayjs, endDayjs];
         } else {
           convertedValues[fieldName] = undefined;
@@ -361,6 +405,35 @@ export default function MyTask() {
               required={field.required && !formConfig.hideRequiredMark}
               rules={field.required ? [{ required: true, message: field.requiredMessage || '请选择日期' }] : []}
               help={field.hint}
+              getValueFromEvent={(value) => {
+                // 选择日期时,将 dayjs 对象转换为毫秒级时间戳
+                if (!value) return undefined;
+                if (value && typeof value === 'object' && 'valueOf' in value) {
+                  return value.valueOf(); // dayjs 的 valueOf() 返回毫秒级时间戳
+                }
+                return value;
+              }}
+              normalize={(value) => {
+                // 回显时,将毫秒级时间戳转换为 dayjs 对象
+                if (!value) return undefined;
+                if (typeof value === 'number') {
+                  const dayjsValue = dayjs(value);
+                  return dayjsValue.isValid() ? dayjsValue : undefined;
+                }
+                if (typeof value === 'string') {
+                  const numValue = Number(value);
+                  if (!isNaN(numValue) && numValue > 0) {
+                    const dayjsValue = dayjs(numValue);
+                    return dayjsValue.isValid() ? dayjsValue : undefined;
+                  }
+                  const dayjsValue = dayjs(value);
+                  return dayjsValue.isValid() ? dayjsValue : undefined;
+                }
+                if (value && typeof value === 'object' && 'isValid' in value) {
+                  return value.isValid() ? value : undefined;
+                }
+                return undefined;
+              }}
             >
               <DatePicker 
                 style={{ width: '100%' }} 
@@ -381,6 +454,62 @@ export default function MyTask() {
               required={field.required && !formConfig.hideRequiredMark}
               rules={field.required ? [{ required: true, message: field.requiredMessage || '请选择日期范围' }] : []}
               help={field.hint}
+              getValueFromEvent={(value) => {
+                // 选择日期范围时,将 dayjs 对象数组转换为毫秒级时间戳数组
+                if (!value || !Array.isArray(value) || value.length !== 2) return undefined;
+                const [start, end] = value;
+                if (start && typeof start === 'object' && 'valueOf' in start &&
+                    end && typeof end === 'object' && 'valueOf' in end) {
+                  return [start.valueOf(), end.valueOf()]; // 返回毫秒级时间戳数组
+                }
+                return value;
+              }}
+              normalize={(value) => {
+                // 回显时,将毫秒级时间戳数组转换为 dayjs 对象数组
+                if (!value) return undefined;
+                if (Array.isArray(value) && value.length === 2) {
+                  const [start, end] = value;
+                  let startDayjs: any = null;
+                  let endDayjs: any = null;
+                  
+                  if (typeof start === 'number') {
+                    startDayjs = dayjs(start);
+                    startDayjs = startDayjs.isValid() ? startDayjs : null;
+                  } else if (typeof start === 'string') {
+                    const numValue = Number(start);
+                    if (!isNaN(numValue) && numValue > 0) {
+                      startDayjs = dayjs(numValue);
+                      startDayjs = startDayjs.isValid() ? startDayjs : null;
+                    } else {
+                      startDayjs = dayjs(start);
+                      startDayjs = startDayjs.isValid() ? startDayjs : null;
+                    }
+                  } else if (start && typeof start === 'object' && 'isValid' in start) {
+                    startDayjs = start.isValid() ? start : null;
+                  }
+                  
+                  if (typeof end === 'number') {
+                    endDayjs = dayjs(end);
+                    endDayjs = endDayjs.isValid() ? endDayjs : null;
+                  } else if (typeof end === 'string') {
+                    const numValue = Number(end);
+                    if (!isNaN(numValue) && numValue > 0) {
+                      endDayjs = dayjs(numValue);
+                      endDayjs = endDayjs.isValid() ? endDayjs : null;
+                    } else {
+                      endDayjs = dayjs(end);
+                      endDayjs = endDayjs.isValid() ? endDayjs : null;
+                    }
+                  } else if (end && typeof end === 'object' && 'isValid' in end) {
+                    endDayjs = end.isValid() ? end : null;
+                  }
+                  
+                  if (startDayjs && endDayjs) {
+                    return [startDayjs, endDayjs];
+                  }
+                }
+                return undefined;
+              }}
             >
               <DatePicker.RangePicker 
                 style={{ width: '100%' }} 
@@ -401,6 +530,35 @@ export default function MyTask() {
               required={field.required && !formConfig.hideRequiredMark}
               rules={field.required ? [{ required: true, message: field.requiredMessage || '请选择日期时间' }] : []}
               help={field.hint}
+              getValueFromEvent={(value) => {
+                // 选择日期时间时,将 dayjs 对象转换为毫秒级时间戳
+                if (!value) return undefined;
+                if (value && typeof value === 'object' && 'valueOf' in value) {
+                  return value.valueOf(); // dayjs 的 valueOf() 返回毫秒级时间戳
+                }
+                return value;
+              }}
+              normalize={(value) => {
+                // 回显时,将毫秒级时间戳转换为 dayjs 对象
+                if (!value) return undefined;
+                if (typeof value === 'number') {
+                  const dayjsValue = dayjs(value);
+                  return dayjsValue.isValid() ? dayjsValue : undefined;
+                }
+                if (typeof value === 'string') {
+                  const numValue = Number(value);
+                  if (!isNaN(numValue) && numValue > 0) {
+                    const dayjsValue = dayjs(numValue);
+                    return dayjsValue.isValid() ? dayjsValue : undefined;
+                  }
+                  const dayjsValue = dayjs(value);
+                  return dayjsValue.isValid() ? dayjsValue : undefined;
+                }
+                if (value && typeof value === 'object' && 'isValid' in value) {
+                  return value.isValid() ? value : undefined;
+                }
+                return undefined;
+              }}
             >
               <DatePicker 
                 style={{ width: '100%' }} 

+ 212 - 312
src/components/WorkJobDetail.tsx

@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useState, useEffect } from 'react';
 import { useNavigate, useSearchParams } from 'react-router-dom';
 import { 
   User, 
@@ -11,23 +11,12 @@ import {
   Star,
   ArrowLeft,
   Clock,
-  List
+  List,
+  Plus,
+  Minus,
+  Loader2
 } from 'lucide-react';
-import ReactFlow, {
-  Node,
-  Edge,
-  useNodesState,
-  useEdgesState,
-  NodeTypes,
-  ConnectionMode,
-  Handle,
-  Position,
-  BaseEdge,
-  EdgeTypes,
-  getStraightPath,
-} from 'reactflow';
-import 'reactflow/dist/style.css';
-import { workJobApi, WorkJobVO } from '../api/WorkJob';
+import { workJobApi, WorkJobVO, WorkflowWorkNodeDO } from '../api/WorkJob';
 import { formatDateWithFormat } from '../utils/formatTime';
 
 interface WorkflowStep {
@@ -54,134 +43,124 @@ interface FlowRecord {
   duration?: string; // 耗时
 }
 
-// 使用 ReactFlow 默认的连线效果,不需要自定义边组件
-
-// 节点类型映射(简化版,只读模式)
-const nodeTypes: NodeTypes = {
-  createJob: ({ data, selected }: any) => <CustomNode data={data} selected={selected} />,
-  confirm: ({ data, selected }: any) => <CustomNode data={data} selected={selected} />,
-  review: ({ data, selected }: any) => <CustomNode data={data} selected={selected} />,
-  inputInfo: ({ data, selected }: any) => <CustomNode data={data} selected={selected} />,
-  isolation: ({ data, selected }: any) => <CustomNode data={data} selected={selected} />,
-  releaseIsolation: ({ data, selected }: any) => <CustomNode data={data} selected={selected} />,
-  returnLock: ({ data, selected }: any) => <CustomNode data={data} selected={selected} />,
-  complete: ({ data, selected }: any) => <CustomNode data={data} selected={selected} />,
+// 流程节点数据结构
+interface WorkflowNode {
+  uuid: string;
+  nodeName: string;
+  type: string;
+  status: 'completed' | 'in_progress' | 'pending';
+  workNode: WorkflowWorkNodeDO;
+  children: WorkflowNode[];
+  isBranch: boolean; // 是否为分支节点(有多个子节点)
+  branchNodes?: WorkflowNode[]; // 分支节点列表
+}
+
+// 获取节点图标
+const getNodeIcon = (type: string, status: 'completed' | 'in_progress' | 'pending') => {
+  const iconClass = status === 'completed' 
+    ? 'text-green-600' 
+    : status === 'in_progress' 
+    ? 'text-blue-600' 
+    : 'text-gray-400';
+  
+  switch (type) {
+    case 'createJob':
+      return <CheckCircle2 className={`w-5 h-5 ${iconClass}`} />;
+    case 'review':
+    case 'confirm':
+      return <Settings className={`w-5 h-5 ${iconClass}`} />;
+    case 'inputInfo':
+      return <FileText className={`w-5 h-5 ${iconClass}`} />;
+    case 'isolation':
+      return <Shield className={`w-5 h-5 ${iconClass}`} />;
+    case 'releaseIsolation':
+    case 'returnLock':
+      return <Lock className={`w-5 h-5 ${iconClass}`} />;
+    case 'complete':
+      return <Flag className={`w-5 h-5 ${iconClass}`} />;
+    default:
+      return <FileText className={`w-5 h-5 ${iconClass}`} />;
+  }
 };
 
-// 简化的自定义节点组件(只读模式)
-function CustomNode({ data, selected, id }: any) {
-  const getNodeIcon = () => {
-    switch (data?.type) {
-      case 'createJob':
-        return <CheckCircle2 className="w-6 h-6" />;
-      case 'review':
-      case 'confirm':
-        return <Settings className="w-6 h-6" />;
-      case 'inputInfo':
-        return <FileText className="w-6 h-6" />;
-      case 'isolation':
-        return <Shield className="w-6 h-6" />;
-      case 'releaseIsolation':
-      case 'returnLock':
-        return <Lock className="w-6 h-6" />;
-      case 'complete':
-        return <Flag className="w-6 h-6" />;
-      default:
-        return <FileText className="w-6 h-6" />;
-    }
-  };
+// 获取状态图标(圆形指示器
+const getStatusIcon = (status: 'completed' | 'in_progress' | 'pending') => {
+  if (status === 'completed') {
+    return (
+      <div className="w-6 h-6 rounded-full bg-green-500 flex items-center justify-center flex-shrink-0">
+        <CheckCircle2 className="w-4 h-4 text-white" />
+      </div>
+    );
+  } else if (status === 'in_progress') {
+    return (
+      <div className="w-6 h-6 rounded-full bg-blue-500 flex items-center justify-center flex-shrink-0">
+        <Loader2 className="w-4 h-4 text-white animate-spin" />
+      </div>
+    );
+  } else {
+    return (
+      <div className="w-6 h-6 rounded-full border-2 border-gray-300 flex items-center justify-center flex-shrink-0">
+        <div className="w-2 h-2 rounded-full bg-gray-300" />
+      </div>
+    );
+  }
+};
 
-  // 根据状态确定节点颜色
-  const getStatusColor = () => {
-    if (data?.status === 'completed') {
-      return { icon: 'text-green-600' };
-    } else if (data?.status === 'in_progress') {
-      return { icon: 'text-blue-600' };
-    } else {
-      return { icon: 'text-gray-400' };
+// 获取执行人信息
+const getExecutorInfo = (workNode: WorkflowWorkNodeDO, type: string): string => {
+  if (!workNode) return '待处理';
+  
+  let executorName = '';
+  if (workNode.nodeUserList && Array.isArray(workNode.nodeUserList)) {
+    const workerUser = workNode.nodeUserList.find((user: any) => user.type === 'worker' || !user.type);
+    if (workerUser && workerUser.userName) {
+      executorName = workerUser.userName;
     }
-  };
-
-  // 获取人员信息
-  const getExecutorInfo = () => {
-    const workNode = data?.workNode;
-    if (!workNode) return '待处理';
-    
-    // 尝试从多个字段获取执行人名称
-    // 优先从 nodeUserList 中查找 worker 类型的用户
-    let executorName = '';
-    if (workNode.nodeUserList && Array.isArray(workNode.nodeUserList)) {
-      const workerUser = workNode.nodeUserList.find((user: any) => user.type === 'worker' || !user.type);
-      if (workerUser && workerUser.userName) {
-        executorName = workerUser.userName;
-      }
+  }
+  
+  if (!executorName) {
+    if (workNode.workerUserName) {
+      executorName = workNode.workerUserName;
+    } else if (workNode.initiatorName) {
+      executorName = workNode.initiatorName;
+    } else if (workNode.workerUserId) {
+      executorName = String(workNode.workerUserId);
+    } else if (workNode.initiator) {
+      executorName = workNode.initiator;
     }
-    
-    // 如果没有从 nodeUserList 找到,尝试其他字段
-    if (!executorName) {
-      if (workNode.workerUserName) {
-        executorName = workNode.workerUserName;
-      } else if (workNode.initiatorName) {
-        executorName = workNode.initiatorName;
-      } else if (workNode.workerUserId) {
-        executorName = String(workNode.workerUserId);
-      } else if (workNode.initiator) {
-        executorName = workNode.initiator;
-      }
+  }
+  
+  if (!executorName) return '待处理';
+  
+  if (type === 'createJob') {
+    return `发起人: ${executorName}`;
+  } else if (type === 'review' || type === 'confirm') {
+    return `审核人: ${executorName}`;
+  } else if (type === 'isolation' || type === 'releaseIsolation' || type === 'returnLock') {
+    return `操作人: ${executorName}`;
+  } else {
+    return `执行人: ${executorName}`;
+  }
+};
+
+// 获取节点状态
+const getNodeStatus = (workNode: WorkflowWorkNodeDO, nodeMap: Map<string, WorkflowWorkNodeDO>): 'completed' | 'in_progress' | 'pending' => {
+  if (!workNode) return 'pending';
+  
+  if (workNode.approvalStatus === 'approved') {
+    return 'completed';
+  } else if (workNode.approvalStatus === 'unaudited' || workNode.approvalStatus === 'pending') {
+    if (!workNode.parentUuid || workNode.parentUuid === '') {
+      return 'completed';
     }
-    
-    if (!executorName) return '待处理';
-    
-    if (data?.type === 'createJob') {
-      return `发起人: ${executorName}`;
-    } else if (data?.type === 'review' || data?.type === 'confirm') {
-      return `审核人: ${executorName}`;
-    } else if (data?.type === 'isolation' || data?.type === 'releaseIsolation' || data?.type === 'returnLock') {
-      return `操作人: ${executorName}`;
-    } else {
-      return `执行人: ${executorName}`;
+    const parentNode = nodeMap.get(workNode.parentUuid);
+    if (parentNode && parentNode.approvalStatus === 'approved') {
+      return 'in_progress';
     }
-  };
-
-  const statusColor = getStatusColor();
-
-  return (
-    <div className="flex items-center gap-3 py-2 relative">
-      {/* 连接点 - 隐藏但保留用于连接 */}
-      <Handle
-        id="top-target"
-        type="target"
-        position={Position.Top}
-        className="!w-0 !h-0 !border-0 !bg-transparent !opacity-0 !pointer-events-none"
-        style={{ top: -6, left: '50%', transform: 'translateX(-50%)', visibility: 'hidden' }}
-        isConnectable={false}
-      />
-      <Handle
-        id="bottom-source"
-        type="source"
-        position={Position.Bottom}
-        className="!w-0 !h-0 !border-0 !bg-transparent !opacity-0 !pointer-events-none"
-        style={{ bottom: -6, left: '50%', transform: 'translateX(-50%)', visibility: 'hidden' }}
-        isConnectable={false}
-      />
-      
-      {/* 图标 */}
-      <div className={`${statusColor.icon} flex items-center justify-center flex-shrink-0`}>
-        {getNodeIcon()}
-      </div>
-      
-      {/* 节点名称和人员信息 */}
-      <div className="flex flex-col gap-1">
-        <div className="font-semibold text-base text-gray-900">
-          {data?.label || data?.nodeName || '节点'}
-        </div>
-        <div className="text-sm text-gray-600">
-          {getExecutorInfo()}
-        </div>
-      </div>
-    </div>
-  );
-}
+    return 'pending';
+  }
+  return 'pending';
+};
 
 export default function WorkJobDetail() {
   const navigate = useNavigate();
@@ -191,11 +170,9 @@ export default function WorkJobDetail() {
   const [jobDetail, setJobDetail] = useState<WorkJobVO | null>(null);
   const [loading, setLoading] = useState(true);
   
-  // ReactFlow 状态
-  const [nodes, setNodes, onNodesChange] = useNodesState([]);
-  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
-  const reactFlowWrapper = useRef<HTMLDivElement>(null);
-  const [reactFlowInstance, setReactFlowInstance] = useState<any>(null);
+  // 流程树状态
+  const [workflowTree, setWorkflowTree] = useState<WorkflowNode[]>([]);
+  const [expandedBranches, setExpandedBranches] = useState<Set<string>>(new Set());
 
   // 获取作业详情
   useEffect(() => {
@@ -214,9 +191,13 @@ export default function WorkJobDetail() {
       setJobDetail(data);
       console.log('作业详情数据:', data);
       
-      // 加载完成后,解析 designContent 并渲染流程
-      if (data?.designContent) {
-        loadWorkflowFromDesignContent(data);
+      // 构建流程树
+      if (data?.workflowWorkNodeDOList && Array.isArray(data.workflowWorkNodeDOList) && data.workflowWorkNodeDOList.length > 0) {
+        console.log('准备构建流程树,节点数量:', data.workflowWorkNodeDOList.length);
+        buildWorkflowTree(data.workflowWorkNodeDOList);
+      } else {
+        console.warn('没有流程节点数据,workflowWorkNodeDOList:', data?.workflowWorkNodeDOList);
+        setWorkflowTree([]);
       }
     } catch (error: any) {
       console.error('获取作业详情失败:', error);
@@ -225,150 +206,100 @@ export default function WorkJobDetail() {
     }
   };
 
-  // 从 designContent 加载流程并渲染到 ReactFlow
-  const loadWorkflowFromDesignContent = (jobData: WorkJobVO) => {
-    try {
-      // 解析 designContent
-      let designContentData: any = null;
-      if (jobData.designContent) {
-        try {
-          designContentData = typeof jobData.designContent === 'string' 
-            ? JSON.parse(jobData.designContent) 
-            : jobData.designContent;
-        } catch (e) {
-          console.warn('解析 designContent 失败:', e);
-          return;
+  // 构建流程树
+  const buildWorkflowTree = (nodeList: WorkflowWorkNodeDO[]) => {
+    console.log('开始构建流程树,节点列表:', nodeList);
+    
+    if (!nodeList || nodeList.length === 0) {
+      console.warn('节点列表为空');
+      setWorkflowTree([]);
+      return;
+    }
+
+    // 构建节点映射
+    const nodeMap = new Map<string, WorkflowWorkNodeDO>();
+    nodeList.forEach((node) => {
+      if (node.uuid) {
+        nodeMap.set(node.uuid, node);
+      }
+    });
+
+    // 构建父子关系映射
+    const childrenMap = new Map<string, WorkflowWorkNodeDO[]>();
+    nodeList.forEach((node) => {
+      if (node.parentUuid) {
+        if (!childrenMap.has(node.parentUuid)) {
+          childrenMap.set(node.parentUuid, []);
         }
+        childrenMap.get(node.parentUuid)!.push(node);
       }
+    });
+
+    console.log('子节点映射:', Array.from(childrenMap.entries()));
+
+    // 找到根节点(parentUuid 为空或不在 nodeList 中的节点)
+    const rootNodes = nodeList.filter((node) => !node.parentUuid || node.parentUuid === '');
+    console.log('根节点:', rootNodes);
 
-      if (!designContentData || !designContentData.nodes || !Array.isArray(designContentData.nodes)) {
-        console.warn('designContent 数据格式不正确');
-        return;
+    // 递归构建节点树
+    const buildNode = (workNode: WorkflowWorkNodeDO): WorkflowNode => {
+      const status = getNodeStatus(workNode, nodeMap);
+      const children = childrenMap.get(workNode.uuid || '') || [];
+      
+      // 判断是否有分支(多个子节点)
+      const isBranch = children.length > 1;
+      
+      // 构建子节点
+      const childNodes = children.map((child) => buildNode(child));
+      
+      // 如果有分支,第一个子节点作为主流程,其他作为分支
+      let mainChild: WorkflowNode | null = null;
+      let branchNodes: WorkflowNode[] = [];
+      
+      if (isBranch && childNodes.length > 0) {
+        mainChild = childNodes[0];
+        branchNodes = childNodes.slice(1);
+      } else if (childNodes.length === 1) {
+        mainChild = childNodes[0];
       }
 
-      // 构建节点映射(通过 uuid)
-      const nodeMap = new Map<string, any>();
-      if (jobData.workflowWorkNodeDOList && Array.isArray(jobData.workflowWorkNodeDOList)) {
-        jobData.workflowWorkNodeDOList.forEach((node: any) => {
-          if (node.uuid) {
-            nodeMap.set(node.uuid, node);
-          }
-        }  );
-}
-      const getNodeStatus = (nodeUuid: string): 'completed' | 'in_progress' | 'pending' => {
-        const workNode = nodeMap.get(nodeUuid);
-        if (!workNode) return 'pending';
-        
-        if (workNode.approvalStatus === 'approved') {
-          return 'completed';
-        } else if (workNode.approvalStatus === 'unaudited' || workNode.approvalStatus === 'pending') {
-          // 检查父节点是否已完成
-          if (!workNode.parentUuid || workNode.parentUuid === '') {
-            return 'completed'; // 根节点
-          }
-          const parentNode = nodeMap.get(workNode.parentUuid);
-          if (parentNode && parentNode.approvalStatus === 'approved') {
-            return 'in_progress';
-          }
-          return 'pending';
-        }
-        return 'pending';
+      return {
+        uuid: workNode.uuid || '',
+        nodeName: workNode.nodeName || '未知节点',
+        type: workNode.type || 'createJob',
+        status,
+        workNode,
+        children: mainChild ? [mainChild] : [],
+        isBranch,
+        branchNodes: branchNodes.length > 0 ? branchNodes : undefined,
       };
+    };
 
-      // 转换为 ReactFlow 的 nodes
-      // 将横向布局转换为纵向布局:交换 x 和 y 坐标
-      const importedNodes: Node[] = designContentData.nodes.map((node: any) => {
-        const nodeId = node.id || node.uuid;
-        const nodeData = node.data || {};
-        const workNode = nodeMap.get(nodeId);
-        const status = getNodeStatus(nodeId);
-
-        // 获取原始位置
-        const originalPosition = node.position || { x: 0, y: 0 };
-        
-        // 交换 x 和 y 坐标,实现横向到纵向的转换
-        // 同时应用缩放因子,使节点间距更紧凑(0.6 表示缩小到原来的 60%)
-        const spacingScale = 0.6;
-        const verticalPosition = {
-          x: originalPosition.y * spacingScale,
-          y: originalPosition.x * spacingScale,
-        };
-
-        return {
-          id: nodeId,
-          type: node.type || 'createJob',
-          position: verticalPosition,
-          data: {
-            ...nodeData,
-            label: nodeData.label || node.label || node.nodeName || workNode?.nodeName || '节点',
-            type: node.type || nodeData.type || 'createJob',
-            status: status,
-            workNode: workNode, // 传递 workNode 以便获取人员信息
-          },
-        };
-      });
-
-      // 转换为 ReactFlow 的 edges
-      // 统一使用垂直布局:所有连接线都从节点底部垂直向下
-      const importedEdges: Edge[] = (designContentData.edges || []).map((edge: any) => {
-        // 检查节点位置,确保 source 在上,target 在下
-        const sourceNode = importedNodes.find((n: Node) => n.id === edge.source);
-        const targetNode = importedNodes.find((n: Node) => n.id === edge.target);
-        
-        // 如果 target 在 source 上方,交换它们(确保连接线从上到下)
-        let finalSource = edge.source;
-        let finalTarget = edge.target;
-        if (sourceNode && targetNode && targetNode.position.y < sourceNode.position.y) {
-          finalSource = edge.target;
-          finalTarget = edge.source;
-        }
-        
-        // 使用最终确定的 source 节点状态来确定边的颜色
-        const sourceStatus = getNodeStatus(finalSource);
-        
-        // 根据状态确定边的颜色
-        let edgeColor = '#d1d5db'; // 默认灰色
-        let edgeStyle = 'dashed';
-        if (sourceStatus === 'completed') {
-          edgeColor = '#10b981'; // 绿色
-          edgeStyle = 'solid';
-        } else if (sourceStatus === 'in_progress') {
-          edgeColor = '#3b82f6'; // 蓝色
-          edgeStyle = 'solid';
-        }
-        
-        return {
-          id: edge.id || `${finalSource}-${finalTarget}`,
-          source: finalSource,
-          target: finalTarget,
-          sourceHandle: 'bottom-source', // 统一使用底部
-          targetHandle: 'top-target',   // 统一使用顶部
-          // 不指定 type,使用 ReactFlow 默认的连线效果
-          style: { 
-            strokeWidth: 4, 
-            stroke: edgeColor,
-            strokeDasharray: edgeStyle === 'dashed' ? '5,5' : undefined,
-          },
-        };
-      });
-
-      console.log('导入的节点:', importedNodes);
-      console.log('导入的连线:', importedEdges);
-
-      setNodes(importedNodes);
-      setEdges(importedEdges);
-
-      // 自动适应视图
-      if (reactFlowInstance && importedNodes.length > 0) {
-        setTimeout(() => {
-          reactFlowInstance.fitView({ padding: 0.2, duration: 300 });
-        }, 100);
-      }
-    } catch (error) {
-      console.error('加载流程失败:', error);
+    // 构建主流程树(从第一个根节点开始)
+    if (rootNodes.length > 0) {
+      const rootNode = rootNodes[0];
+      const tree = [buildNode(rootNode)];
+      console.log('构建完成的流程树:', tree);
+      setWorkflowTree(tree);
+    } else {
+      console.warn('未找到根节点');
+      setWorkflowTree([]);
     }
   };
 
+  // 切换分支展开/收起
+  const toggleBranch = (nodeUuid: string) => {
+    setExpandedBranches((prev) => {
+      const newSet = new Set(prev);
+      if (newSet.has(nodeUuid)) {
+        newSet.delete(nodeUuid);
+      } else {
+        newSet.add(nodeUuid);
+      }
+      return newSet;
+    });
+  };
+
   // 格式化状态文本
   const getStatusText = (status: string | number | undefined): string => {
     const statusMap: Record<string, string> = {
@@ -633,38 +564,7 @@ export default function WorkJobDetail() {
               </h2>
             </div>
             <div className="flex-1 overflow-y-auto p-6">
-              {/* 作业流程渲染 - 使用 ReactFlow */}
-              {(() => {
-                if (!jobDetail?.designContent || nodes.length === 0) {
-                  return (
-                    <div className="flex items-center justify-center h-full text-gray-400">
-                      {loading ? '加载中...' : '暂无流程数据'}
-                    </div>
-                  );
-                }
-
-                return (
-                  <div className="relative" style={{ minHeight: '100%', height: '600px' }} ref={reactFlowWrapper}>
-                    <ReactFlow
-                      nodes={nodes}
-                      edges={edges}
-                      onNodesChange={onNodesChange}
-                      onEdgesChange={onEdgesChange}
-                      nodeTypes={nodeTypes}
-                      fitView
-                      onInit={setReactFlowInstance}
-                      nodesDraggable={false}
-                      nodesConnectable={false}
-                      elementsSelectable={false}
-                      connectionMode={ConnectionMode.Loose}
-                      defaultEdgeOptions={{
-                        style: { strokeWidth: 4 },
-                      }}
-                    >
-                    </ReactFlow>
-                  </div>
-                );
-              })()}
+              {/* 作业流程渲染 - 待重新设计 */}
             </div>
           </div>