Bladeren bron

修复部分bug

pm 4 maanden geleden
bovenliggende
commit
bb97b5a667
3 gewijzigde bestanden met toevoegingen van 612 en 341 verwijderingen
  1. 14 5
      src/components/IsolationWork.tsx
  2. 97 1
      src/components/MyTask.tsx
  3. 501 335
      src/components/WorkJobDetail.tsx

+ 14 - 5
src/components/IsolationWork.tsx

@@ -1829,10 +1829,15 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
     }
   };
 
-  // 作业管理查看处理(打开查看详情弹框,复用编辑弹框但设为只读模式)
-  const handleWorkJobView = async (item: WorkJobVO) => {
-    // 调用编辑处理函数来打开弹框并加载数据,并传入 viewMode=true
-    await handleWorkJobEdit(item, true);
+  // 作业管理查看处理(跳转到作业详情页面)
+  const handleWorkJobView = (item: WorkJobVO) => {
+    // 跳转到作业详情页面
+    const workId = item.id;
+    if (workId) {
+      navigate(`/work-job/detail?id=${workId}`);
+    } else {
+      message.warning('作业ID不存在,无法查看详情');
+    }
   };
   
   // 为查看模式加载流程设计JSON
@@ -2564,7 +2569,11 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
           <UIButton
             variant="ghost"
             size="sm"
-            onClick={() => handleWorkJobView(record)}
+            onClick={(e) => {
+              e.stopPropagation();
+              e.preventDefault();
+              handleWorkJobView(record);
+            }}
             className="h-8 px-2"
           >
             <Eye className="w-4 h-4" />

+ 97 - 1
src/components/MyTask.tsx

@@ -515,7 +515,103 @@ export default function MyTask() {
             </AntdForm.Item>
           </div>
         );
+      case 'time':
+      case 'timepicker':
+        return (
+          <div key={field.id} style={{ ...spanStyle, backgroundColor: '#f5f5f5', padding: '12px', borderRadius: '12px', marginBottom: '12px', border: '1px solid #e0e0e0' }}>
+            <AntdForm.Item
+              label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
+              name={field.name || field.field}
+              required={field.required && !formConfig.hideRequiredMark}
+              rules={field.required ? [{ required: true, message: field.requiredMessage || '请选择时间' }] : []}
+              help={field.hint}
+            >
+              <DatePicker 
+                style={{ width: '100%' }} 
+                placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择时间'}
+                allowClear={field.showClear}
+                picker="time"
+                format="HH:mm:ss"
+                disabled={field.disabled}
+                size={field.size || 'middle'}
+              />
+            </AntdForm.Item>
+          </div>
+        );
       default:
+        // 检查 inputType 是否为时间类型
+        const inputType = field.inputType || field.type;
+        if (inputType === 'date' || inputType === 'datetime' || inputType === 'time' || inputType === 'timepicker') {
+          // 如果是时间类型,使用 DatePicker
+          if (inputType === 'datetime') {
+            return (
+              <div key={field.id} style={{ ...spanStyle, backgroundColor: '#f5f5f5', padding: '12px', borderRadius: '12px', marginBottom: '12px', border: '1px solid #e0e0e0' }}>
+                <AntdForm.Item
+                  label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
+                  name={field.name || field.field}
+                  required={field.required && !formConfig.hideRequiredMark}
+                  rules={field.required ? [{ required: true, message: field.requiredMessage || '请选择日期时间' }] : []}
+                  help={field.hint}
+                >
+                  <DatePicker 
+                    style={{ width: '100%' }} 
+                    placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择日期时间'}
+                    allowClear={field.showClear}
+                    showTime={{ format: 'HH:mm:ss' }}
+                    format="YYYY-MM-DD HH:mm:ss"
+                    disabled={field.disabled}
+                    size={field.size || 'middle'}
+                  />
+                </AntdForm.Item>
+              </div>
+            );
+          } else if (inputType === 'time' || inputType === 'timepicker') {
+            return (
+              <div key={field.id} style={{ ...spanStyle, backgroundColor: '#f5f5f5', padding: '12px', borderRadius: '12px', marginBottom: '12px', border: '1px solid #e0e0e0' }}>
+                <AntdForm.Item
+                  label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
+                  name={field.name || field.field}
+                  required={field.required && !formConfig.hideRequiredMark}
+                  rules={field.required ? [{ required: true, message: field.requiredMessage || '请选择时间' }] : []}
+                  help={field.hint}
+                >
+                  <DatePicker 
+                    style={{ width: '100%' }} 
+                    placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择时间'}
+                    allowClear={field.showClear}
+                    picker="time"
+                    format="HH:mm:ss"
+                    disabled={field.disabled}
+                    size={field.size || 'middle'}
+                  />
+                </AntdForm.Item>
+              </div>
+            );
+          } else {
+            // date 类型
+            return (
+              <div key={field.id} style={{ ...spanStyle, backgroundColor: '#f5f5f5', padding: '12px', borderRadius: '12px', marginBottom: '12px', border: '1px solid #e0e0e0' }}>
+                <AntdForm.Item
+                  label={(field.label || field.title || '') + (formConfig.labelSuffix || '')}
+                  name={field.name || field.field}
+                  required={field.required && !formConfig.hideRequiredMark}
+                  rules={field.required ? [{ required: true, message: field.requiredMessage || '请选择日期' }] : []}
+                  help={field.hint}
+                >
+                  <DatePicker 
+                    style={{ width: '100%' }} 
+                    placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择日期'}
+                    allowClear={field.showClear}
+                    disabled={field.disabled}
+                    size={field.size || 'middle'}
+                  />
+                </AntdForm.Item>
+              </div>
+            );
+          }
+        }
+        
+        // 其他类型使用 Input
         return (
           <div key={field.id} style={{ ...spanStyle, backgroundColor: '#f5f5f5', padding: '12px', borderRadius: '12px', marginBottom: '12px', border: '1px solid #e0e0e0' }}>
             <AntdForm.Item
@@ -1054,7 +1150,7 @@ export default function MyTask() {
             icon={<Eye className="w-4 h-4" />}
             onClick={(e) => {
               e.stopPropagation();
-              console.log('MyTask: 查看详情按钮被点击', record);
+              e.preventDefault();
               handleViewDetail(record);
             }}
           >

+ 501 - 335
src/components/WorkJobDetail.tsx

@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useRef } from 'react';
 import { useNavigate, useSearchParams } from 'react-router-dom';
 import { 
   User, 
@@ -9,20 +9,25 @@ import {
   Lock, 
   Flag, 
   Star,
-  Play,
-  Check,
-  Pause,
-  Send,
-  ArrowLeftRight,
-  UserPlus,
-  Plus,
   ArrowLeft,
-  X
+  Clock,
+  List
 } from 'lucide-react';
-import { Button } from './ui/button';
-import { Tabs } from 'antd';
+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 { Table, TableHeader, TableBody, TableHead, TableRow, TableCell } from './ui/table';
 import { formatDateWithFormat } from '../utils/formatTime';
 
 interface WorkflowStep {
@@ -49,6 +54,135 @@ 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} />,
+};
+
+// 简化的自定义节点组件(只读模式)
+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 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 = () => {
+    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;
+      }
+    }
+    
+    // 如果没有从 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 (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 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>
+  );
+}
+
 export default function WorkJobDetail() {
   const navigate = useNavigate();
   const [searchParams] = useSearchParams();
@@ -56,7 +190,12 @@ export default function WorkJobDetail() {
   
   const [jobDetail, setJobDetail] = useState<WorkJobVO | null>(null);
   const [loading, setLoading] = useState(true);
-  const [activeTab, setActiveTab] = useState('details');
+  
+  // ReactFlow 状态
+  const [nodes, setNodes, onNodesChange] = useNodesState([]);
+  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
+  const reactFlowWrapper = useRef<HTMLDivElement>(null);
+  const [reactFlowInstance, setReactFlowInstance] = useState<any>(null);
 
   // 获取作业详情
   useEffect(() => {
@@ -70,7 +209,15 @@ export default function WorkJobDetail() {
     try {
       setLoading(true);
       const response = await workJobApi.selectWorkflowWorkById(Number(jobId));
-      setJobDetail(response as any);
+      // 处理响应数据,可能包含 data 字段
+      const data = (response as any)?.data || response;
+      setJobDetail(data);
+      console.log('作业详情数据:', data);
+      
+      // 加载完成后,解析 designContent 并渲染流程
+      if (data?.designContent) {
+        loadWorkflowFromDesignContent(data);
+      }
     } catch (error: any) {
       console.error('获取作业详情失败:', error);
     } finally {
@@ -78,6 +225,150 @@ 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;
+        }
+      }
+
+      if (!designContentData || !designContentData.nodes || !Array.isArray(designContentData.nodes)) {
+        console.warn('designContent 数据格式不正确');
+        return;
+      }
+
+      // 构建节点映射(通过 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';
+      };
+
+      // 转换为 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);
+    }
+  };
+
   // 格式化状态文本
   const getStatusText = (status: string | number | undefined): string => {
     const statusMap: Record<string, string> = {
@@ -120,7 +411,7 @@ export default function WorkJobDetail() {
         title: '发起人',
         assigneeName: jobDetail?.initiatorName || jobDetail?.initiator || '张三',
         time: jobDetail?.initiationTime || jobDetail?.initiateTime 
-          ? new Date(jobDetail.initiationTime || jobDetail.initiateTime).toLocaleString('zh-CN', {
+          ? new Date((jobDetail.initiationTime || jobDetail.initiateTime) as string | number | Date).toLocaleString('zh-CN', {
               year: 'numeric',
               month: '2-digit',
               day: '2-digit',
@@ -189,54 +480,59 @@ export default function WorkJobDetail() {
 
   // 构建流转记录数据
   const buildFlowRecords = (): FlowRecord[] => {
-    // 这里应该根据实际的作业数据构建流转记录
-    // 目前使用模拟数据,后续需要根据实际API返回的数据结构调整
-    const initTime = jobDetail?.initiationTime || jobDetail?.initiateTime;
-    const startTime = initTime ? formatDateWithFormat(initTime) : '2024-01-15 10:30:00';
-    
-    // 计算结束时间(如果开始时间是时间戳,加5秒)
-    let endTime: string | undefined;
-    if (initTime) {
-      const startTimestamp = typeof initTime === 'number' 
-        ? (initTime > 1000000000000 ? initTime : initTime * 1000)
-        : new Date(initTime).getTime();
-      endTime = formatDateWithFormat(new Date(startTimestamp + 5000));
-    } else {
-      endTime = '2024-01-15 10:30:05';
+    if (!jobDetail?.workflowWorkNodeDOList || !Array.isArray(jobDetail.workflowWorkNodeDOList)) {
+      return [];
     }
 
-    const records: FlowRecord[] = [
-      {
-        id: '1',
-        taskNode: '发起人',
-        executor: jobDetail?.initiatorName || jobDetail?.initiator || '张三',
-        startTime,
-        endTime,
-        taskStatus: 'completed',
-        executionDescription: '查看表单',
-        duration: '5秒',
-      },
-      {
-        id: '2',
-        taskNode: '审核/确认',
-        executor: '李四',
-        startTime: '2024-01-15 10:35:00',
-        endTime: undefined,
-        taskStatus: 'in_progress',
-        executionDescription: '查看表单',
-        duration: '进行中',
-      },
-      {
-        id: '3',
-        taskNode: '录入/表单',
-        executor: '王五',
-        startTime: undefined,
+    const records: FlowRecord[] = jobDetail.workflowWorkNodeDOList.map((node: any, index: number) => {
+      // 根据审批状态确定任务状态
+      let taskStatus: 'completed' | 'in_progress' | 'pending' = 'pending';
+      if (node.approvalStatus === 'approved') {
+        taskStatus = 'completed';
+      } else if (node.approvalStatus === 'unaudited' || node.approvalStatus === 'pending') {
+        // 检查是否是第一个节点或者前面的节点已完成
+        if (index === 0) {
+          taskStatus = 'completed';
+        } else {
+          const prevNode = jobDetail.workflowWorkNodeDOList[index - 1];
+          if (prevNode && prevNode.approvalStatus === 'approved') {
+            taskStatus = 'in_progress';
+          } else {
+            taskStatus = 'pending';
+          }
+        }
+      }
+
+      // 格式化时间
+      const startTime = node.createTime ? formatDateWithFormat(node.createTime) : undefined;
+      
+      // 获取执行人(如果有)
+      const executor = node.workerUserId || node.initiatorName || '';
+
+      // 获取描述信息
+      let description = node.approvalOpinion || '';
+      if (!description || description === 'pending') {
+        if (taskStatus === 'completed') {
+          description = '任务已完成';
+        } else if (taskStatus === 'in_progress') {
+          description = '任务正在进行中';
+    } else {
+          description = '等待开始';
+        }
+      }
+
+      return {
+        id: String(node.id || index),
+        taskNode: node.nodeName || '未知节点',
+        executor: executor,
+        startTime: startTime,
         endTime: undefined,
-        taskStatus: 'pending',
-        executionDescription: undefined,
+        taskStatus: taskStatus,
+        executionDescription: description,
         duration: undefined,
-      },
-    ];
+      };
+    });
+
     return records;
   };
 
@@ -260,12 +556,6 @@ export default function WorkJobDetail() {
     return statusMap[status] || 'bg-gray-100 text-gray-700';
   };
 
-  // 处理查看表单点击
-  const handleViewForm = (record: FlowRecord) => {
-    // TODO: 实现查看表单功能
-    console.log('查看表单:', record);
-  };
-
   const flowRecords = buildFlowRecords();
 
   if (loading) {
@@ -286,21 +576,23 @@ export default function WorkJobDetail() {
 
   return (
     <div className="h-screen bg-gray-50 flex flex-col">
-      <div className="max-w-7xl mx-auto px-4 py-6 flex-1 flex flex-col min-h-0 w-full">
+      <div className="w-full max-w-none mx-auto px-8 py-6 flex-1 flex flex-col min-h-0">
         {/* 头部区域 */}
-        <div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6 flex-shrink-0">
+        <div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6 flex-shrink-0" style={{ marginLeft: '20px', marginRight: '20px' }}>
+          <div className="flex items-start justify-between">
+            <div className="flex-1">
           {/* 编号 */}
           <div className="text-sm text-gray-600 mb-2">
-            编号:<span className="font-medium text-gray-900">{jobDetail.orderNo || jobDetail.code || 'Work1766368222538'}</span>
+                编号:<span className="font-medium text-gray-900">{jobDetail?.orderNo || jobDetail?.code || '-'}</span>
           </div>
           
           {/* 标题和状态按钮(同一行) */}
           <div className="flex items-center gap-3 mb-3">
             <h1 className="text-2xl font-semibold text-gray-900">
-              {jobDetail.name || 'test'}
+                  {jobDetail?.name || '-'}
             </h1>
-            <span className={`inline-flex px-4 py-1.5 rounded-full text-sm font-medium ${getStatusClassName(jobDetail.status)}`}>
-              {getStatusText(jobDetail.status)}
+                <span className={`inline-flex px-4 py-1.5 rounded-full text-sm font-medium ${getStatusClassName(jobDetail?.status)}`}>
+                  {getStatusText(jobDetail?.status)}
             </span>
           </div>
 
@@ -309,281 +601,155 @@ export default function WorkJobDetail() {
             <div className="w-5 h-5 rounded-full bg-blue-100 flex items-center justify-center flex-shrink-0">
               <User className="w-3.5 h-3.5 text-blue-600" />
             </div>
-            <span>{jobDetail.initiatorName || jobDetail.initiator || '博士安全'}</span>
+                <span>{jobDetail?.initiatorName || jobDetail?.initiator || '-'}</span>
             <span className="text-gray-300">|</span>
-            <span>
-              {jobDetail.initiationTime || jobDetail.initiateTime
-                ? new Date(jobDetail.initiationTime || jobDetail.initiateTime).toLocaleString('zh-CN', {
-                    year: 'numeric',
-                    month: '2-digit',
-                    day: '2-digit',
-                    hour: '2-digit',
-                    minute: '2-digit',
-                    second: '2-digit'
-                  }).replace(/\//g, '/')
-                : '2025/12/22 09:50:23'} 提交
-            </span>
+                       <span>
+                         {jobDetail?.initiationTime
+                           ? formatDateWithFormat(jobDetail.initiationTime as string | number | Date)
+                           : '-'} 提交
+                       </span>
+              </div>
+            </div>
+            
+            {/* 返回按钮 */}
+            <button
+              onClick={() => navigate(-1)}
+              className="flex items-center justify-center w-10 h-10 rounded-lg border border-gray-300 hover:bg-gray-50 hover:border-gray-400 transition-colors flex-shrink-0"
+              title="返回作业管理"
+            >
+              <ArrowLeft className="w-5 h-5 text-gray-600" />
+            </button>
           </div>
         </div>
 
-        {/* 导航标签和内容区域 - 撑满剩余空间 */}
-        <div className="bg-white rounded-xl shadow-sm border border-gray-200 flex-1 flex flex-col min-h-0 mb-6">
-          <style>{`
-            .work-job-tabs .ant-tabs-nav {
-              margin-bottom: 0;
-              padding: 0 24px;
-            }
-            .work-job-tabs .ant-tabs-tab {
-              padding: 16px 0;
-              font-size: 14px;
-              font-weight: 500;
-              color: #6b7280;
-            }
-            .work-job-tabs .ant-tabs-tab-active {
-              color: #2563eb !important;
-            }
-            .work-job-tabs .ant-tabs-ink-bar {
-              background: #2563eb !important;
-              height: 2px !important;
-            }
-            .work-job-tabs .ant-tabs-content-holder {
-              flex: 1;
-              overflow: hidden;
-              display: flex;
-              flex-direction: column;
-            }
-            .work-job-tabs .ant-tabs-tabpane {
-              height: 100%;
-              overflow-y: auto;
-            }
-          `}</style>
-          <Tabs
-            activeKey={activeTab}
-            onChange={setActiveTab}
-            type="line"
-            className="work-job-tabs flex-1 flex flex-col min-h-0"
-            items={[
-              {
-                key: 'details',
-                label: '任务详情',
-                children: (
-                  <div className="relative pl-2 p-6">
-                {/* 工作流步骤时间线 */}
-                <div className="space-y-0">
-                  {workflowSteps.map((step, index) => {
-                    const isActive = step.status === 'in_progress';
-                    const isCompleted = step.status === 'completed';
-                    const isPending = step.status === 'pending';
-
-                    return (
-                      <div key={step.id} className="relative flex items-start pb-8 last:pb-0">
-                        {/* 左侧连接线 */}
-                        {index < workflowSteps.length - 1 && (
-                          <div 
-                            className={`absolute left-[18px] top-[36px] w-0.5 ${
-                              isCompleted ? 'bg-blue-500' : 'bg-gray-300'
-                            }`}
-                            style={{ height: 'calc(100% - 8px)' }}
-                          />
-                        )}
-
-                        {/* 图标圆圈 */}
-                        <div 
-                          className={`relative z-10 flex items-center justify-center w-9 h-9 rounded-full border-2 flex-shrink-0 ${
-                            isActive 
-                              ? 'bg-white border-blue-500 shadow-md text-blue-600' 
-                              : isCompleted
-                              ? 'bg-green-500 border-green-500 text-white'
-                              : 'bg-white border-gray-300 text-gray-400'
-                          }`}
-                        >
-                          {step.icon}
-                        </div>
+        {/* 任务详情和执行记录区域 - 左右两栏布局 */}
+        <div className="flex-1 flex gap-8 mb-6 min-h-0">
+          {/* 左侧:任务详情 */}
+          <div className="bg-white rounded-xl shadow-sm border border-gray-200 flex-1 flex flex-col min-h-0" style={{ marginLeft: '20px',maxHeight:'750px' }}>
+            <div className="px-6 py-4 border-b border-gray-200">
+              <h2 className="text-lg font-semibold text-blue-600 flex items-center gap-2">
+                <FileText className="w-5 h-5" />
+                作业流程
+              </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>
+                  );
+                }
 
-                        {/* 右侧内容 */}
-                        <div className="flex-1 ml-4 pt-0.5">
-                          <div className="flex items-start justify-between">
-                            <div className="flex-1">
-                              <div className="flex items-center gap-2 mb-1.5">
-                                <h3 className="text-base font-medium text-gray-900">{step.title}</h3>
-                              </div>
-                              {step.assigneeName && (
-                                <div className="text-sm text-gray-600 mb-1">
-                                  {step.assigneeName}
-                                  {step.time && (
-                                    <>
-                                      <span className="mx-2 text-gray-300">·</span>
-                                      <span>{step.time}</span>
-                                    </>
-                                  )}
-                                </div>
-                              )}
-                              {step.hasDetail && (
-                                <button className="flex items-center gap-1 text-sm text-blue-600 hover:text-blue-700 mt-2">
-                                  <Star className="w-4 h-4" />
-                                  查看详情
-                                </button>
-                              )}
-                            </div>
-                            
-                            {/* 状态标签 */}
-                            <div className="ml-4 flex-shrink-0">
-                              {isActive ? (
-                                <span className="inline-flex px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-700 border border-blue-200">
-                                  执行中
-                                </span>
-                              ) : isCompleted ? (
-                                <span className="inline-flex px-3 py-1 rounded-lg text-xs font-medium bg-gray-100 text-gray-700">
-                                  已完成
-                                </span>
-                              ) : (
-                                <span className="inline-flex px-3 py-1 rounded-lg text-xs font-medium bg-gray-100 text-gray-700">
-                                  待处理
-                                </span>
-                              )}
-                            </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>
+
+          {/* 右侧:执行记录 */}
+          <div className="bg-white rounded-xl shadow-sm border border-gray-200 flex-1 flex flex-col min-h-0 flex-shrink-0" style={{ marginRight: '20px',maxHeight:'750px' }}>
+            <div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
+              <h2 className="text-lg font-semibold text-blue-600 flex items-center gap-2">
+                <Clock className="w-5 h-5" />
+                执行记录
+              </h2>
+            </div>
+            <div className="flex-1 overflow-y-auto p-4">
+              <div className="space-y-3">
+                {flowRecords.map((record, index) => {
+                  const isCompleted = record.taskStatus === 'completed';
+                  const isInProgress = record.taskStatus === 'in_progress';
+                  const isPending = record.taskStatus === 'pending';
+
+                  // 根据状态获取描述信息
+                  let description = record.executionDescription || '';
+                  if (!description) {
+                    if (isCompleted) {
+                      description = '任务已完成';
+                    } else if (isInProgress) {
+                      description = '任务正在进行中';
+                    } else {
+                      description = '等待开始';
+                    }
+                  }
+
+                  return (
+                    <div
+                      key={record.id}
+                      className="bg-white rounded-lg border border-gray-200 p-4 hover:shadow-md transition-shadow"
+                      style={{
+                        borderLeftWidth: '4px',
+                        borderLeftColor: isCompleted ? '#10b981' : isInProgress ? '#3b82f6' : '#9ca3af',
+                      }}
+                    >
+                      <div className="flex items-start justify-between">
+                        <div className="flex-1">
+                          {/* 任务节点名称 */}
+                          <div className="font-semibold text-base text-gray-900 mb-2">
+                            {record.taskNode}
                           </div>
+
+                          {/* 执行人信息 */}
+                          <div className="text-sm text-gray-600 mb-2">
+                            {record.executor ? (
+                              <>
+                                {record.taskNode === '作业申请' ? '发起人' : 
+                                 record.taskNode === '审核审批' ? '审核人' : 
+                                 record.taskNode === '上锁操作' || record.taskNode === '共锁操作' ? '操作人' : 
+                                 '执行人'}: {record.executor}
+                              </>
+                            ) : '待处理'}
+                          </div>
+
+                          {/* 描述信息 */}
+                          <div className="text-sm text-gray-500">
+                            {description}
+                          </div>
+
+                          {/* 时间信息 */}
+                          {record.startTime && (
+                            <div className="text-xs text-gray-400 mt-2">
+                              {record.startTime}
+                            </div>
+                          )}
+                        </div>
+
+                        {/* 状态标签 */}
+                        <div className="ml-4 flex-shrink-0">
+                          <span className={`inline-flex px-3 py-1 rounded-full text-xs font-medium ${getFlowRecordStatusClassName(record.taskStatus)}`}>
+                            {getFlowRecordStatusText(record.taskStatus)}
+                          </span>
                         </div>
                       </div>
-                    );
-                  })}
-                </div>
-                  </div>
-                ),
-              },
-              {
-                key: 'flowchart',
-                label: '流程图',
-                children: (
-                  <div className="text-center py-12 text-gray-500 p-6">
-                    流程图功能待开发
-                  </div>
-                ),
-              },
-              {
-                key: 'history',
-                label: '流转记录',
-                children: (
-                  <div className="p-6">
-                    <div className="border border-gray-200 rounded-lg overflow-hidden">
-                      <Table>
-                        <TableHeader>
-                          <TableRow className="bg-gray-50 hover:bg-gray-50">
-                            <TableHead className="h-10 px-4 font-medium text-gray-700 bg-gray-50">任务节点</TableHead>
-                            <TableHead className="h-10 px-4 font-medium text-gray-700 bg-gray-50">执行人</TableHead>
-                            <TableHead className="h-10 px-4 font-medium text-gray-700 bg-gray-50">开始时间</TableHead>
-                            <TableHead className="h-10 px-4 font-medium text-gray-700 bg-gray-50">结束时间</TableHead>
-                            <TableHead className="h-10 px-4 font-medium text-gray-700 bg-gray-50">任务状态</TableHead>
-                            <TableHead className="h-10 px-4 font-medium text-gray-700 bg-gray-50">执行说明</TableHead>
-                            <TableHead className="h-10 px-4 font-medium text-gray-700 bg-gray-50">耗时</TableHead>
-                          </TableRow>
-                        </TableHeader>
-                        <TableBody>
-                          {flowRecords.length === 0 ? (
-                            <TableRow>
-                              <TableCell colSpan={7} className="text-center py-8 text-gray-500">
-                                暂无流转记录
-                              </TableCell>
-                            </TableRow>
-                          ) : (
-                            flowRecords.map((record) => (
-                              <TableRow key={record.id} className="hover:bg-gray-50">
-                                <TableCell className="px-4 py-3 text-gray-900">{record.taskNode}</TableCell>
-                                <TableCell className="px-4 py-3 text-gray-600">{record.executor}</TableCell>
-                                <TableCell className="px-4 py-3 text-gray-600">
-                                  {record.startTime || '-'}
-                                </TableCell>
-                                <TableCell className="px-4 py-3 text-gray-600">
-                                  {record.endTime || '-'}
-                                </TableCell>
-                                <TableCell className="px-4 py-3">
-                                  <span className={`inline-flex px-3 py-1 rounded-full text-xs font-medium ${getFlowRecordStatusClassName(record.taskStatus)}`}>
-                                    {getFlowRecordStatusText(record.taskStatus)}
-                                  </span>
-                                </TableCell>
-                                <TableCell className="px-4 py-3">
-                                  {record.executionDescription ? (
-                                    <button
-                                      onClick={() => handleViewForm(record)}
-                                      className="text-blue-600 hover:text-blue-700 hover:underline text-sm"
-                                    >
-                                      {record.executionDescription}
-                                    </button>
-                                  ) : (
-                                    <span className="text-gray-400">-</span>
-                                  )}
-                                </TableCell>
-                                <TableCell className="px-4 py-3 text-gray-600">
-                                  {record.duration || '-'}
-                                </TableCell>
-                              </TableRow>
-                            ))
-                          )}
-                        </TableBody>
-                      </Table>
                     </div>
-                  </div>
-                ),
-              },
-            ]}
-          />
-        </div>
-
-        {/* 底部操作按钮 */}
-        <div className="bg-white rounded-xl shadow-sm border border-gray-200 p-4">
-          <div className="flex items-center gap-3 flex-wrap">
-            <Button 
-              variant="default" 
-              className="bg-green-600 hover:bg-green-700 text-white"
-            >
-              <Play className="w-4 h-4" />
-              执行
-            </Button>
-            <Button 
-              variant="default" 
-              className="bg-blue-600 hover:bg-blue-700 text-white"
-            >
-              <Check className="w-4 h-4" />
-              完成
-            </Button>
-            <Button 
-              variant="outline" 
-              className="border-orange-300 text-orange-600 hover:bg-orange-50"
-            >
-              <Pause className="w-4 h-4" />
-              暂停
-            </Button>
-            <Button variant="outline" className="border-gray-300 text-gray-600 hover:bg-gray-50">
-              <Send className="w-4 h-4" />
-              抄送
-            </Button>
-            <Button variant="outline" className="border-gray-300 text-gray-600 hover:bg-gray-50">
-              <ArrowLeftRight className="w-4 h-4" />
-              转办
-            </Button>
-            <Button variant="outline" className="border-gray-300 text-gray-600 hover:bg-gray-50">
-              <UserPlus className="w-4 h-4" />
-              委派
-            </Button>
-            <Button variant="outline" className="border-gray-300 text-gray-600 hover:bg-gray-50">
-              <Plus className="w-4 h-4" />
-              加签
-            </Button>
-            <Button variant="outline" className="border-gray-300 text-gray-600 hover:bg-gray-50">
-              <ArrowLeft className="w-4 h-4" />
-              退回
-            </Button>
-            <Button variant="outline" className="border-gray-300 text-gray-600 hover:bg-gray-50">
-              <X className="w-4 h-4" />
-              取消
-            </Button>
+                  );
+                })}
+              </div>
+            </div>
           </div>
         </div>
-
-        {/* 版权信息 */}
-        <div className="mt-6 text-center text-sm text-gray-500">
-          Copyright ©2025 锁控管理系统
-        </div>
       </div>
     </div>
   );