Przeglądaj źródła

新增任务管理 修复部分bug12/31日

pm 4 miesięcy temu
rodzic
commit
de324b84ff

+ 34 - 3
src/Dashboard.tsx

@@ -13,6 +13,7 @@ import LockCabinetDetail from './components/lockCabinet/LockCabinetDetail';
 import NotificationManagement from './components/NotificationManagement';
 import FormManagement from './components/FormManagement';
 import MyTask from './components/MyTask';
+import TaskManagement from './components/TaskManagement';
 import { authApi } from './api';
 import { toast } from 'sonner';
 import { Toaster } from 'sonner';
@@ -77,6 +78,7 @@ export default function Dashboard() {
       if (path.includes('LocationManagement') || path.endsWith('/LocationManagement')) return 'locationManagement';
       if (path.includes('IsolationWork') || path.endsWith('/IsolationWork')) return 'isolationWork';
       if (path.includes('notification') || path.endsWith('/notification')) return 'notificationManagement';
+      if (path.includes('TaskManagement') || path.endsWith('/TaskManagement') || path.includes('taskManagement') || path.endsWith('/taskManagement')) return 'taskManagement';
     }
     
     // 系统管理相关
@@ -106,10 +108,19 @@ export default function Dashboard() {
       return 'hardwareManagement';
     }
     
+    // 任务管理相关(优先处理,避免被隔离作业匹配)
+    if (path.includes('/task-management') || path.includes('/taskManagement') || path.includes('/admin-work') || path.includes('/adminWork') || path.includes('TaskManagement')) {
+      return 'taskManagement';
+    }
+    
+    // 我的任务相关(优先处理,避免被隔离作业匹配)
+    if (path.includes('/my-task') || path.includes('/myTask') || path.includes('/my-work') || path.includes('/myWork')) {
+      return 'myTask';
+    }
+    
     // 隔离作业相关
     if (path === '/jobTicket' || path.startsWith('/jobTicket') || path === '/isolation' || path.startsWith('/isolation') || path === '/CustomWorkflow' || path.startsWith('/CustomWorkflow') || path === '/sopm' || path.startsWith('/sopm')) {
       if (path.includes('/form') || path.endsWith('/form')) return 'formManagement';
-      if (path.includes('/my-task') || path.includes('/myTask') || path.endsWith('/my-task') || path.endsWith('/myTask')) return 'myTask';
       if (path.includes('job') || path.endsWith('/job')) return 'workManagement';
       if (path.includes('sop') || path.endsWith('/sop')) return 'sopManagement';
       if (path.includes('design') || path.endsWith('/design')) return 'processDesign';
@@ -654,6 +665,11 @@ export default function Dashboard() {
         console.log('设置我的任务菜单:', { menuKey: 'myTask' });
         setActiveMenu('isolationWork');
         setActiveSubMenu('myTask');
+      } else if (menuKey === 'taskManagement' || menuKey === 'task-management' || menuKey === 'taskmanagement') {
+        // 任务管理
+        console.log('设置任务管理菜单:', { menuKey });
+        setActiveMenu('taskManagement');
+        setActiveSubMenu('taskManagement');
       } else if (menuKey === 'formManagement' || menuKey === 'processDesign' || menuKey === 'sopManagement' || 
                  menuKey === 'workManagement' || menuKey === 'processTemplate') {
         // 隔离作业的子菜单
@@ -960,6 +976,7 @@ export default function Dashboard() {
                     <button
                       key={item.key}
                       onClick={() => {
+                        console.log('点击主菜单:', { menuKey: item.key, hasSubMenus: !!filteredSubMenuConfig[item.key] });
                         // 保存菜单状态到 sessionStorage
                         const firstSubMenu = filteredSubMenuConfig[item.key]?.[0];
                         if (firstSubMenu) {
@@ -972,8 +989,14 @@ export default function Dashboard() {
                         if (location.pathname.startsWith('/lock-cabinet/detail')) {
                           navigate('/dashboard');
                         }
-                        setActiveMenu(item.key);
-                        setActiveSubMenu(firstSubMenu?.key || '');
+                        // 任务管理菜单如果没有子菜单,设置 activeSubMenu 为 'taskManagement'
+                        if (item.key === 'taskManagement' && !firstSubMenu) {
+                          setActiveMenu('taskManagement');
+                          setActiveSubMenu('taskManagement');
+                        } else {
+                          setActiveMenu(item.key);
+                          setActiveSubMenu(firstSubMenu?.key || '');
+                        }
                       }}
                       className={`flex items-center gap-2 px-4 py-2.5 rounded-xl transition-all duration-300 ${
                         isActive
@@ -1158,6 +1181,9 @@ export default function Dashboard() {
             } else if (activeSubMenu === 'myTask' || activeSubMenu === 'mytask' || location.pathname.includes('/my-task') || location.pathname.includes('/myTask')) {
               console.log('✅ 渲染 MyTask 组件', { activeMenu, activeSubMenu, pathname: location.pathname });
               return <MyTask />;
+            } else if (activeSubMenu === 'taskManagement' || activeSubMenu === 'taskmanagement' || activeSubMenu === 'task-management' || location.pathname.includes('/task-management') || location.pathname.includes('/taskManagement')) {
+              console.log('✅ 渲染 TaskManagement 组件(从隔离作业子菜单)', { activeMenu, activeSubMenu, pathname: location.pathname });
+              return <TaskManagement />;
             } else {
               const subMenuName = filteredSubMenuConfig[activeMenu]?.find(item => item.key === activeSubMenu)?.name || activeSubMenu;
               console.log('⚠️ 渲染 IsolationWork 组件', { activeMenu, activeSubMenu, subMenuName });
@@ -1166,6 +1192,11 @@ export default function Dashboard() {
           })()
         ) : activeMenu === 'myTask' || location.pathname.includes('/my-task') ? (
           <MyTask />
+        ) : activeMenu === 'taskManagement' || activeSubMenu === 'taskManagement' || location.pathname.includes('/task-management') || location.pathname.includes('/taskManagement') ? (
+          (() => {
+            console.log('✅ 渲染 TaskManagement 组件', { activeMenu, activeSubMenu, pathname: location.pathname });
+            return <TaskManagement />;
+          })()
         ) : activeMenu === 'notificationManagement' ? (
           <NotificationManagement />
         ) : (

+ 18 - 0
src/api/mytask/index.ts

@@ -89,3 +89,21 @@ export const myTaskApi = {
     return axiosInstance.post('/iscs/workflow-work/updateNodeApproval', params);
   },
 };
+
+// 任务管理 API
+export const taskManagementApi = {
+  // 获取任务管理列表(分页)
+  getAdminWorkPage: (params?: MyTaskPageParam) => {
+    return axiosInstance.get<PageResponse<MyTaskVO>>('/iscs/workflow-work/getAdminWorkPage', { params });
+  },
+  
+  // 获取任务管理节点详情(与我的任务使用相同的接口)
+  getAdminWorkNodeDetail: (nodeId: number) => {
+    return axiosInstance.get<MyTaskNodeDetailVO>(`/iscs/workflow-work/getMyWorkNodeDetail?nodeId=${nodeId}`);
+  },
+  
+  // 更新节点审批状态(任务管理)
+  updateNodeApproval: (params: UpdateNodeApprovalParam) => {
+    return axiosInstance.post('/iscs/workflow-work/updateNodeApproval', params);
+  },
+};

Plik diff jest za duży
+ 875 - 159
src/components/IsolationWork.tsx


+ 261 - 71
src/components/MyTask.tsx

@@ -11,16 +11,137 @@ import { dateFormatter } from '../utils/formatTime';
 import { DICT_TYPE, getDictLabel } from '../utils/dict';
 import { setConfAndFields2, FormCreateData } from '../utils/formCreate';
 
+// 辅助函数:安全地将值转换为 dayjs 对象
+const safeToDayjs = (value: any): dayjs.Dayjs | null => {
+  if (!value && value !== 0) {
+    return null;
+  }
+  
+  // 如果是数字(时间戳)
+  if (typeof value === 'number') {
+    // dayjs 默认将数字当作毫秒级时间戳处理
+    // 但如果数字小于 10000000000(10位),可能是秒级时间戳,需要转换为毫秒
+    let timestamp = value;
+    const originalTimestamp = timestamp;
+    if (timestamp > 0 && timestamp < 10000000000) {
+      // 秒级时间戳,转换为毫秒
+      timestamp = timestamp * 1000;
+      console.log(`时间戳转换: 秒级 -> 毫秒级`, { original: originalTimestamp, converted: timestamp });
+    }
+    const dayjsValue = dayjs(timestamp);
+    if (dayjsValue.isValid()) {
+      console.log(`时间戳解析成功:`, {
+        original: value,
+        timestamp,
+        date: dayjsValue.format('YYYY-MM-DD HH:mm:ss'),
+        year: dayjsValue.year()
+      });
+      return dayjsValue;
+    } else {
+      console.warn(`时间戳解析失败:`, { original: value, timestamp });
+      return null;
+    }
+  }
+  
+  // 如果是字符串
+  if (typeof value === 'string' && value.trim() !== '') {
+    // 尝试判断是否为时间戳字符串
+    const numValue = Number(value);
+    if (!isNaN(numValue) && numValue > 0 && numValue.toString() === value.trim()) {
+      // 是纯数字字符串,可能是时间戳
+      // 判断是秒级还是毫秒级时间戳
+      let timestamp = numValue;
+      const originalTimestamp = timestamp;
+      if (timestamp < 10000000000) {
+        // 秒级时间戳(10位数字),转换为毫秒
+        timestamp = timestamp * 1000;
+        console.log(`时间戳转换: 秒级 -> 毫秒级`, { original: originalTimestamp, converted: timestamp });
+      } else {
+        console.log(`时间戳转换: 毫秒级时间戳`, { timestamp });
+      }
+      // 否则是毫秒级时间戳(13位或更多),直接使用
+      const dayjsValue = dayjs(timestamp);
+      if (dayjsValue.isValid()) {
+        console.log(`时间戳解析成功:`, {
+          original: value,
+          timestamp,
+          date: dayjsValue.format('YYYY-MM-DD HH:mm:ss'),
+          year: dayjsValue.year()
+        });
+        return dayjsValue;
+      } else {
+        console.warn(`时间戳解析失败:`, { original: value, timestamp });
+        return null;
+      }
+    } else {
+      // 普通日期字符串
+      const dayjsValue = dayjs(value);
+      return dayjsValue.isValid() ? dayjsValue : null;
+    }
+  }
+  
+  // 如果已经是 dayjs 对象,检查是否有 isValid 方法
+  if (value && typeof value === 'object' && typeof value.isValid === 'function') {
+    try {
+      return value.isValid() ? value : null;
+    } catch (e) {
+      // isValid 调用失败,尝试重新创建
+    }
+  }
+  
+  // 如果是对象,检查是否有 isValid 方法(可能是序列化后的 dayjs 对象)
+  if (value && typeof value === 'object') {
+    // 尝试重新创建 dayjs 对象
+    try {
+      // 如果有 $d 属性,可能是 dayjs 对象
+      if (value.$d) {
+        const dayjsValue = dayjs(value.$d);
+        return dayjsValue.isValid() ? dayjsValue : null;
+      }
+      // 如果有时间戳相关的属性
+      if (value.valueOf && typeof value.valueOf === 'function') {
+        const timestamp = value.valueOf();
+        if (typeof timestamp === 'number') {
+          const dayjsValue = dayjs(timestamp);
+          return dayjsValue.isValid() ? dayjsValue : null;
+        }
+      }
+    } catch (e) {
+      // 转换失败,返回 null
+      return null;
+    }
+  }
+  
+  return null;
+};
+
+// 辅助函数:验证 dayjs 对象是否有效
+const isValidDayjs = (value: any): boolean => {
+  if (!value) return false;
+  if (typeof value !== 'object') return false;
+  if (typeof value.isValid !== 'function') return false;
+  try {
+    return value.isValid();
+  } catch (e) {
+    return false;
+  }
+};
+
 // 辅助函数:将表单值中的日期字符串转换为 dayjs 对象
 const convertDateValues = (formValues: any, fields: any[]): any => {
   const fieldTypeMap: { [key: string]: string } = {};
   
   // 建立字段名到类型的映射
   fields.forEach((field: any) => {
-    const fieldObj = typeof field === 'string' ? JSON.parse(field) : field;
-    const fieldName = fieldObj.name || fieldObj.field;
-    if (fieldName) {
-      fieldTypeMap[fieldName] = fieldObj.type;
+    try {
+      const fieldObj = typeof field === 'string' ? JSON.parse(field) : field;
+      const fieldName = fieldObj.name || fieldObj.field;
+      if (fieldName) {
+        fieldTypeMap[fieldName] = fieldObj.type;
+      }
+    } catch (e) {
+      // 解析失败,跳过
+      console.warn('解析字段配置失败:', e);
     }
   });
   
@@ -30,71 +151,28 @@ const convertDateValues = (formValues: any, fields: any[]): any => {
     const fieldType = fieldTypeMap[fieldName];
     const value = formValues[fieldName];
     
-    if (fieldType === 'date' || fieldType === 'datetime') {
-      // 日期或日期时间类型
-      // 优先处理毫秒级时间戳
-      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 && typeof value.isValid === 'function') {
-        // 已经是 dayjs 对象,验证 isValid 是函数
-        convertedValues[fieldName] = value.isValid() ? value : undefined;
-      } else {
-        convertedValues[fieldName] = undefined;
-      }
+    if (fieldType === 'date' || fieldType === 'datetime' || fieldType === 'timepicker') {
+      // 日期、日期时间或时间选择器类型
+      convertedValues[fieldName] = safeToDayjs(value) || undefined;
     } else if (fieldType === 'daterange') {
       // 日期范围类型
       if (Array.isArray(value) && value.length === 2) {
         const [start, end] = value;
-        // 处理开始时间
-        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 && typeof start.isValid === 'function') {
-          startDayjs = start.isValid() ? start : null;
-        }
-        // 处理结束时间
-        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 && typeof end.isValid === 'function') {
-          endDayjs = end.isValid() ? end : null;
-        }
+        const startDayjs = safeToDayjs(start);
+        const endDayjs = safeToDayjs(end);
+        
+        // 调试日志
         if (startDayjs && endDayjs) {
+          console.log(`日期范围字段 ${fieldName} 转换成功:`, {
+            original: { start, end },
+            converted: {
+              start: startDayjs.format('YYYY-MM-DD HH:mm:ss'),
+              end: endDayjs.format('YYYY-MM-DD HH:mm:ss')
+            }
+          });
           convertedValues[fieldName] = [startDayjs, endDayjs];
         } else {
+          console.warn(`日期范围字段 ${fieldName} 转换失败:`, { start, end, startDayjs, endDayjs });
           convertedValues[fieldName] = undefined;
         }
       } else {
@@ -859,6 +937,7 @@ export default function MyTask() {
         workName: record.name || data?.workName || data?.name,
         orderNo: record.orderNo || data?.orderNo,
         workerUserName: record.workerUserName || data?.workerUserName,
+        initiatorName: record.initiatorName || data?.initiatorName || data?.initiator || record.initiator || '',
         workTime: record.workTime || data?.workTime,
         // 确保 type 字段存在,使用第一层 data.type
         type: data?.type || data?.nodeType || '',
@@ -1128,10 +1207,90 @@ export default function MyTask() {
                   const fieldsArray = Array.isArray(fields) ? fields : [];
                   formValues = convertDateValues(formValues, fieldsArray);
                   
+                  // 清理无效的日期值,确保所有日期字段都是有效的 dayjs 对象或 undefined
+                  const cleanedFormValues: any = {};
+                  Object.keys(formValues).forEach(key => {
+                    const value = formValues[key];
+                    const field = fieldsArray.find((f: any) => {
+                      const fieldObj = typeof f === 'string' ? JSON.parse(f) : f;
+                      return (fieldObj.name || fieldObj.field) === key;
+                    });
+                    
+                    if (field) {
+                      const fieldObj = typeof field === 'string' ? JSON.parse(field) : field;
+                      const fieldType = fieldObj.type;
+                      
+                      if (fieldType === 'date' || fieldType === 'datetime' || fieldType === 'timepicker') {
+                        // 日期、日期时间或时间选择器类型:确保是有效的 dayjs 对象或 undefined
+                        if (isValidDayjs(value)) {
+                          // 已经是有效的 dayjs 对象
+                          cleanedFormValues[key] = value;
+                        } else if (value !== null && value !== undefined) {
+                          // 尝试转换
+                          const dayjsValue = safeToDayjs(value);
+                          cleanedFormValues[key] = dayjsValue || undefined;
+                        } else {
+                          cleanedFormValues[key] = undefined;
+                        }
+                      } else if (fieldType === 'daterange') {
+                        // 日期范围类型:确保数组中的每个元素都是有效的 dayjs 对象
+                        if (Array.isArray(value) && value.length === 2) {
+                          const [start, end] = value;
+                          // 验证开始时间
+                          let startDayjs: dayjs.Dayjs | null = null;
+                          if (isValidDayjs(start)) {
+                            startDayjs = start;
+                          } else {
+                            startDayjs = safeToDayjs(start);
+                          }
+                          // 验证结束时间
+                          let endDayjs: dayjs.Dayjs | null = null;
+                          if (isValidDayjs(end)) {
+                            endDayjs = end;
+                          } else {
+                            endDayjs = safeToDayjs(end);
+                          }
+                          cleanedFormValues[key] = (startDayjs && endDayjs) ? [startDayjs, endDayjs] : undefined;
+                        } else {
+                          cleanedFormValues[key] = undefined;
+                        }
+                      } else {
+                        // 其他类型直接使用
+                        cleanedFormValues[key] = value;
+                      }
+                    } else {
+                      // 没有找到字段定义,直接使用原值
+                      cleanedFormValues[key] = value;
+                    }
+                  });
+                  
                   // 延迟一下,确保表单字段已经渲染
                   setTimeout(() => {
-                    detailForm.setFieldsValue(formValues);
-                    console.log('MyTask: 表单数据已回填', formValues);
+                    try {
+                      detailForm.setFieldsValue(cleanedFormValues);
+                      console.log('MyTask: 表单数据已回填', cleanedFormValues);
+                    } catch (e) {
+                      console.error('MyTask: 设置表单值失败', e);
+                      // 如果设置失败,尝试只设置非日期字段
+                      const safeValues: any = {};
+                      Object.keys(cleanedFormValues).forEach(key => {
+                        const value = cleanedFormValues[key];
+                        const field = fieldsArray.find((f: any) => {
+                          const fieldObj = typeof f === 'string' ? JSON.parse(f) : f;
+                          return (fieldObj.name || fieldObj.field) === key;
+                        });
+                        if (field) {
+                          const fieldObj = typeof field === 'string' ? JSON.parse(field) : field;
+                          const fieldType = fieldObj.type;
+                          if (fieldType !== 'date' && fieldType !== 'datetime' && fieldType !== 'daterange' && fieldType !== 'timepicker') {
+                            safeValues[key] = value;
+                          }
+                        } else {
+                          safeValues[key] = value;
+                        }
+                      });
+                      detailForm.setFieldsValue(safeValues);
+                    }
                   }, 100);
                 } catch (e) {
                   console.error('MyTask: 解析表单数据失败', e);
@@ -1246,7 +1405,21 @@ export default function MyTask() {
       dataIndex: 'orderNo',
       width: 180,
       align: 'center',
-      render: (text: string) => text || '-',
+      render: (text: string, record: MyTaskVO) => {
+        if (!text) return '-';
+        return (
+          <span
+            className="text-blue-600 hover:text-blue-800 cursor-pointer hover:underline"
+            onClick={(e) => {
+              e.stopPropagation();
+              e.preventDefault();
+              handleViewDetail(record);
+            }}
+          >
+            {text}
+          </span>
+        );
+      },
     },
     {
       title: '作业名称',
@@ -1254,6 +1427,22 @@ export default function MyTask() {
       width: 220,
       align: 'center',
       ellipsis: true,
+      render: (text: string, record: MyTaskVO) => {
+        if (!text) return '-';
+        return (
+          <span
+            className="text-blue-600 hover:text-blue-800 cursor-pointer hover:underline"
+            onClick={(e) => {
+              e.stopPropagation();
+              e.preventDefault();
+              handleViewDetail(record);
+            }}
+            title={text}
+          >
+            {text}
+          </span>
+        );
+      },
     },
     {
       title: '负责人',
@@ -1417,16 +1606,16 @@ export default function MyTask() {
           setApprovalComment('');
         }}
         footer={null}
-        width={800}
+        width={1000}
         destroyOnClose
         confirmLoading={detailLoading}
         maskClosable={false}
         zIndex={1000}
         styles={{
           body: {
-            minHeight: '500px',
-            maxHeight: '600px',
-            height: '600px',
+            minHeight: '600px',
+            maxHeight: '700px',
+            height: '700px',
             padding: 0,
             display: 'flex',
             flexDirection: 'column',
@@ -1463,8 +1652,9 @@ export default function MyTask() {
               </div>
               <div className="text-sm flex gap-4" style={{ color: '#898f9a', marginTop: '12px' }}>
                 <span>作业编号:{detailData.orderNo || '-'}</span>
-                <span>负责人:{detailData.workerUserName || '-'}</span>
-                <span>时间:{detailData.workTime ? dateFormatter(detailData.workTime) : '-'}</span>
+                <span>任务负责人:{detailData.workerUserName || '-'}</span>
+                <span>作业负责人:{detailData.initiatorName || '-'}</span>
+                <span>发起时间:{detailData.workTime ? dateFormatter(detailData.workTime) : '-'}</span>
               </div>
             </div>
 

+ 110 - 49
src/components/ProcessDesigner.tsx

@@ -946,7 +946,7 @@ export default function ProcessDesigner() {
   // 表单列表
   const [formList, setFormList] = useState<FormVO[]>([]);
   // 隔离方式字典数据
-  const [isolationMethodDictList, setIsolationMethodDictList] = useState<DictDataVO[]>([]);
+  const [isolationTypeDictList, setIsolationTypeDictList] = useState<DictDataVO[]>([]);
 
   const loadNodeCache = useCallback((nodeId: string) => {
     try {
@@ -1006,13 +1006,22 @@ export default function ProcessDesigner() {
             ? { ...n.data, ...cachedData }
             : n.data;
           
+          // 将 responsible 转换为 workerUserId,并确保值是字符串
+          const { responsible, ...restData } = mergedData;
+          const processedData = {
+            ...restData,
+            workerUserId: responsible !== undefined && responsible !== null && responsible !== '' 
+              ? String(responsible) 
+              : '',
+          };
+          
           const nodeObj: any = {
             uuid: n.id,
             type: n.type,
             position: n.position,
-            nodeName: mergedData.label || '',
-            nodeIcon: mergedData.icon || mergedData.type || n.type || '',
-            data: mergedData,
+            nodeName: processedData.label || '',
+            nodeIcon: processedData.icon || processedData.type || n.type || '',
+            data: processedData,
           };
           return nodeObj;
         }),
@@ -1195,10 +1204,10 @@ export default function ProcessDesigner() {
       });
       const data = (response as any)?.data || response;
       const list = data?.list || [];
-      setIsolationMethodDictList(list);
+      setIsolationTypeDictList(list);
     } catch (error: any) {
       console.error('获取隔离方式字典失败:', error);
-      setIsolationMethodDictList([]);
+      setIsolationTypeDictList([]);
     }
   };
 
@@ -1251,10 +1260,10 @@ export default function ProcessDesigner() {
     responsible: '',
     remark: '',
     formId: '',
-    isolationMethod: '', // 隔离方式
+    isolationType: '', // 隔离方式
     isolationPoints: [] as string[], // 隔离点选择(多选)
     isolationNode: [] as string[], // 隔离节点(多选)
-    selectedIsolationNodeId: '', // 选择的隔离/方案节点ID(用于解除隔离节点)
+    isolationNodeUuid: '', // 选择的隔离/方案节点ID(用于解除隔离节点)
     lockPerson: '', // 上锁人
     coLockPersons: [] as string[], // 共锁人(多选)
     notificationMethods: {
@@ -1270,17 +1279,18 @@ export default function ProcessDesigner() {
   // 实时缓存并更新节点配置,避免切换节点后丢失未保存的输入
   useEffect(() => {
     if (selectedNode) {
+      const { isolationMethod, ...restData } = selectedNode.data || {};
       const updatedData = {
-        ...selectedNode.data,
+        ...restData,
         label: nodeConfig.nodeName,
         icon: nodeConfig.nodeIcon,
         responsible: nodeConfig.responsible,
         remark: nodeConfig.remark,
         formId: nodeConfig.formId,
-        isolationMethod: nodeConfig.isolationMethod,
+        isolationType: nodeConfig.isolationType,
         isolationPoints: nodeConfig.isolationPoints,
         isolationNode: nodeConfig.isolationNode,
-        selectedIsolationNodeId: nodeConfig.selectedIsolationNodeId,
+        isolationNodeUuid: nodeConfig.isolationNodeUuid,
         lockPerson: nodeConfig.lockPerson,
         coLockPersons: nodeConfig.coLockPersons,
         notificationMethods: nodeConfig.notificationMethods,
@@ -1339,19 +1349,26 @@ export default function ProcessDesigner() {
         const topLevelLabel = node.nodeName || '';
         const topLevelIcon = node.nodeIcon || '';
         
+        // 兼容处理:优先使用 workerUserId,如果没有则使用 responsible(向后兼容),并确保值是字符串
+        const workerUserIdValue = nodeData.workerUserId !== undefined && nodeData.workerUserId !== null && nodeData.workerUserId !== ''
+          ? String(nodeData.workerUserId)
+          : (nodeData.responsible !== undefined && nodeData.responsible !== null && nodeData.responsible !== ''
+              ? String(nodeData.responsible)
+              : '');
+        
         // 确保所有字段都存在,使用默认值填充缺失的字段
         const completeData = {
           label: topLevelLabel || nodeData.label || '', // 优先使用顶层的 nodeName
           type: nodeData.type || node.type,
           nodeId: nodeData.nodeId || '',
           icon: topLevelIcon || nodeData.icon || nodeData.type || node.type, // 优先使用顶层的 nodeIcon
-          responsible: nodeData.responsible || '',
+          responsible: workerUserIdValue, // 内部仍使用 responsible 字段名,但值来自 workerUserId 或 responsible
           remark: nodeData.remark || '',
           formId: nodeData.formId || nodeData.submitForm ? String(nodeData.formId || nodeData.submitForm) : '', // 兼容旧的 submitForm 字段,确保是字符串类型
-          isolationMethod: nodeData.isolationMethod || '',
+          isolationType: nodeData.isolationType || '',
           isolationPoints: Array.isArray(nodeData.isolationPoints) ? nodeData.isolationPoints : [],
           isolationNode: Array.isArray(nodeData.isolationNode) ? nodeData.isolationNode : [],
-          selectedIsolationNodeId: nodeData.selectedIsolationNodeId || '',
+          isolationNodeUuid: nodeData.isolationNodeUuid || '',
           lockPerson: nodeData.lockPerson || '',
           coLockPersons: Array.isArray(nodeData.coLockPersons) ? nodeData.coLockPersons : [],
           notificationMethods: nodeData.notificationMethods || {
@@ -1698,19 +1715,19 @@ export default function ProcessDesigner() {
     const config = nodeConfigs.find(c => c.type === source.type);
     
     // 如果是解除隔离节点且已选择了隔离节点,则从隔离节点获取配置
-    let isolationMethod = source.isolationMethod || '';
+    let isolationType = source.isolationType || '';
     let isolationPointsData = source.isolationPoints || [];
     let lockPerson = source.lockPerson || '';
     let coLockPersons = source.coLockPersons || [];
     let responsible = source.responsible || '';
     
-    if (source.type === 'releaseIsolation' && source.selectedIsolationNodeId) {
-      const targetNode = nodes.find(n => n.id === source.selectedIsolationNodeId && n.data?.type === 'isolation');
+    if (source.type === 'releaseIsolation' && source.isolationNodeUuid) {
+      const targetNode = nodes.find(n => n.id === source.isolationNodeUuid && n.data?.type === 'isolation');
       if (targetNode) {
         // 优先从缓存中读取,缓存中没有则从 node.data 读取
         const targetCache = loadNodeCache(targetNode.id);
         const targetSource = targetCache || targetNode.data || {};
-        isolationMethod = targetSource.isolationMethod || '';
+        isolationType = targetSource.isolationType || '';
         isolationPointsData = targetSource.isolationPoints || [];
         lockPerson = targetSource.lockPerson || '';
         coLockPersons = targetSource.coLockPersons || [];
@@ -1724,10 +1741,10 @@ export default function ProcessDesigner() {
       responsible: responsible,
       remark: source.remark || '',
       formId: source.formId || source.submitForm ? String(source.formId || source.submitForm) : '', // 兼容旧的 submitForm 字段,确保是字符串类型
-      isolationMethod: isolationMethod,
+      isolationType: isolationType,
       isolationPoints: isolationPointsData,
       isolationNode: Array.isArray(source.isolationNode) ? source.isolationNode : (source.isolationNode ? [source.isolationNode] : []),
-      selectedIsolationNodeId: source.selectedIsolationNodeId || '',
+      isolationNodeUuid: source.isolationNodeUuid || '',
       lockPerson: lockPerson,
       coLockPersons: coLockPersons,
       notificationMethods: source.notificationMethods || {
@@ -1994,20 +2011,21 @@ export default function ProcessDesigner() {
     setNodes((nds) => {
       const updatedNodes = nds.map((node) => {
         if (node.id === selectedNode.id) {
+          const { isolationMethod, ...restNodeData } = node.data || {};
           const updatedNode = {
             ...node,
             data: {
-              ...node.data,
+              ...restNodeData,
               label: nodeConfig.nodeName,
               type: nodeConfig.nodeIcon || node.data?.type, // 更新节点类型(图标)
               icon: nodeConfig.nodeIcon, // 保存图标名称
               responsible: nodeConfig.responsible,
               remark: nodeConfig.remark,
               formId: nodeConfig.formId ? String(nodeConfig.formId) : '',
-              isolationMethod: nodeConfig.isolationMethod,
+              isolationType: nodeConfig.isolationType,
               isolationPoints: nodeConfig.isolationPoints,
               isolationNode: nodeConfig.isolationNode,
-              selectedIsolationNodeId: nodeConfig.selectedIsolationNodeId,
+              isolationNodeUuid: nodeConfig.isolationNodeUuid,
               lockPerson: nodeConfig.lockPerson,
               coLockPersons: nodeConfig.coLockPersons,
               notificationMethods: nodeConfig.notificationMethods,
@@ -2067,13 +2085,22 @@ export default function ProcessDesigner() {
             ? { ...n.data, ...cachedData }
             : n.data;
           
+          // 将 responsible 转换为 workerUserId,并确保值是字符串
+          const { responsible, ...restData } = mergedData;
+          const processedData = {
+            ...restData,
+            workerUserId: responsible !== undefined && responsible !== null && responsible !== '' 
+              ? String(responsible) 
+              : '',
+          };
+          
           const nodeObj: any = {
             uuid: n.id,
             type: n.type,
             position: n.position,
-            nodeName: mergedData.label || '',
-            nodeIcon: mergedData.icon || mergedData.type || n.type || '',
-            data: mergedData,
+            nodeName: processedData.label || '',
+            nodeIcon: processedData.icon || processedData.type || n.type || '',
+            data: processedData,
           };
           return nodeObj;
         }),
@@ -2159,13 +2186,22 @@ export default function ProcessDesigner() {
             ? { ...n.data, ...cachedData }
             : n.data;
           
+          // 将 responsible 转换为 workerUserId,并确保值是字符串
+          const { responsible, ...restData } = mergedData;
+          const processedData = {
+            ...restData,
+            workerUserId: responsible !== undefined && responsible !== null && responsible !== '' 
+              ? String(responsible) 
+              : '',
+          };
+          
           const nodeObj: any = {
             uuid: n.id,
             type: n.type,
             position: n.position,
-            nodeName: mergedData.label || '',
-            nodeIcon: mergedData.icon || mergedData.type || n.type || '',
-            data: mergedData,
+            nodeName: processedData.label || '',
+            nodeIcon: processedData.icon || processedData.type || n.type || '',
+            data: processedData,
           };
           return nodeObj;
         }),
@@ -2267,13 +2303,22 @@ export default function ProcessDesigner() {
                   ? { ...n.data, ...cachedData }
                   : n.data;
                 
+                // 将 responsible 转换为 workerUserId,并确保值是字符串
+                const { responsible, ...restData } = mergedData;
+                const processedData = {
+                  ...restData,
+                  workerUserId: responsible !== undefined && responsible !== null && responsible !== '' 
+                    ? String(responsible) 
+                    : '',
+                };
+                
                 const nodeObj: any = {
                   uuid: n.id,
                   type: n.type,
                   position: n.position,
-                  nodeName: mergedData.label || '',
-                  nodeIcon: mergedData.icon || mergedData.type || n.type || '',
-                  data: mergedData,
+                  nodeName: processedData.label || '',
+                  nodeIcon: processedData.icon || processedData.type || n.type || '',
+                  data: processedData,
                 };
                 return nodeObj;
               }),
@@ -2379,15 +2424,24 @@ export default function ProcessDesigner() {
           ? { ...n.data, ...cachedData }
           : n.data;
         
+        // 将 responsible 转换为 workerUserId,并确保值是字符串
+        const { responsible, ...restData } = mergedData;
+        const processedData = {
+          ...restData,
+          workerUserId: responsible !== undefined && responsible !== null && responsible !== '' 
+            ? String(responsible) 
+            : '',
+        };
+        
         // 导出时将id字段改为uuid,但值保持不变
         // 将 nodeName 和 nodeIcon 从 data 中提取到顶层,方便后端识别,但保留 data 中的原始字段
         const nodeObj: any = {
           uuid: n.id, // 字段名改为uuid,值保持不变
           type: n.type,
           position: n.position,
-          nodeName: mergedData.label || '', // 从 data.label 提取到顶层作为 nodeName
-          nodeIcon: mergedData.icon || mergedData.type || n.type || '', // 从 data.icon 或 data.type 提取到顶层作为 nodeIcon
-          data: mergedData, // 保留完整的 data,包括 label 和 icon
+          nodeName: processedData.label || '', // 从 data.label 提取到顶层作为 nodeName
+          nodeIcon: processedData.icon || processedData.type || n.type || '', // 从 data.icon 或 data.type 提取到顶层作为 nodeIcon
+          data: processedData, // 保留完整的 data,包括 label 和 icon,但 responsible 已转换为 workerUserId
         };
         return nodeObj;
       }),
@@ -2502,19 +2556,26 @@ export default function ProcessDesigner() {
         const topLevelLabel = node.nodeName || '';
         const topLevelIcon = node.nodeIcon || '';
         
+        // 兼容处理:优先使用 workerUserId,如果没有则使用 responsible(向后兼容),并确保值是字符串
+        const workerUserIdValue = nodeData.workerUserId !== undefined && nodeData.workerUserId !== null && nodeData.workerUserId !== ''
+          ? String(nodeData.workerUserId)
+          : (nodeData.responsible !== undefined && nodeData.responsible !== null && nodeData.responsible !== ''
+              ? String(nodeData.responsible)
+              : '');
+        
         // 确保所有字段都存在,使用默认值填充缺失的字段
         const completeData = {
           label: topLevelLabel || nodeData.label || '', // 优先使用顶层的 nodeName
           type: nodeData.type || node.type,
           nodeId: nodeData.nodeId || '',
           icon: topLevelIcon || nodeData.icon || nodeData.type || node.type, // 优先使用顶层的 nodeIcon
-          responsible: nodeData.responsible || '',
+          responsible: workerUserIdValue, // 内部仍使用 responsible 字段名,但值来自 workerUserId 或 responsible
           remark: nodeData.remark || '',
           formId: nodeData.formId || nodeData.submitForm ? String(nodeData.formId || nodeData.submitForm) : '', // 兼容旧的 submitForm 字段,确保是字符串类型
-          isolationMethod: nodeData.isolationMethod || '',
+          isolationType: nodeData.isolationType || '',
           isolationPoints: Array.isArray(nodeData.isolationPoints) ? nodeData.isolationPoints : [],
           isolationNode: Array.isArray(nodeData.isolationNode) ? nodeData.isolationNode : [],
-          selectedIsolationNodeId: nodeData.selectedIsolationNodeId || '',
+          isolationNodeUuid: nodeData.isolationNodeUuid || '',
           lockPerson: nodeData.lockPerson || '',
           coLockPersons: Array.isArray(nodeData.coLockPersons) ? nodeData.coLockPersons : [],
           notificationMethods: nodeData.notificationMethods || {
@@ -2582,10 +2643,10 @@ export default function ProcessDesigner() {
             responsible: source.responsible || '',
             remark: source.remark || '',
             formId: source.formId || source.submitForm ? String(source.formId || source.submitForm) : '', // 兼容旧的 submitForm 字段,确保是字符串类型
-            isolationMethod: source.isolationMethod || '',
+            isolationType: source.isolationType || '',
             isolationPoints: source.isolationPoints || [],
             isolationNode: Array.isArray(source.isolationNode) ? source.isolationNode : (source.isolationNode ? [source.isolationNode] : []),
-            selectedIsolationNodeId: source.selectedIsolationNodeId || '',
+            isolationNodeUuid: source.isolationNodeUuid || '',
             lockPerson: source.lockPerson || '',
             coLockPersons: source.coLockPersons || [],
             notificationMethods: source.notificationMethods || {
@@ -3207,7 +3268,7 @@ export default function ProcessDesigner() {
                                     选择隔离节点
                                   </label>
                                   <Select
-                                    value={nodeConfig.selectedIsolationNodeId || undefined}
+                                    value={nodeConfig.isolationNodeUuid || undefined}
                                     onChange={(value) => {
                                       setNodeConfig((prev) => {
                                         const target = nodes.find(n => n.id === value && n.data?.type === 'isolation');
@@ -3217,8 +3278,8 @@ export default function ProcessDesigner() {
                                           const source = cache || target.data || {};
                                           return {
                                             ...prev,
-                                            selectedIsolationNodeId: value || '',
-                                            isolationMethod: source.isolationMethod || '',
+                                            isolationNodeUuid: value || '',
+                                            isolationType: source.isolationType || '',
                                             isolationPoints: source.isolationPoints || [],
                                             isolationNode: Array.isArray(source.isolationNode)
                                               ? source.isolationNode
@@ -3228,7 +3289,7 @@ export default function ProcessDesigner() {
                                             coLockPersons: source.coLockPersons || [],
                                           };
                                         }
-                                        return { ...prev, selectedIsolationNodeId: value || '' };
+                                        return { ...prev, isolationNodeUuid: value || '' };
                                       });
                                     }}
                                     placeholder="请选择隔离节点"
@@ -3283,16 +3344,16 @@ export default function ProcessDesigner() {
                                   隔离方式
                                 </label>
                                 <Select
-                                  value={nodeConfig.isolationMethod || undefined}
+                                  value={nodeConfig.isolationType || undefined}
                                   onChange={(value) =>
-                                    setNodeConfig({ ...nodeConfig, isolationMethod: value || '' })
+                                    setNodeConfig({ ...nodeConfig, isolationType: value || '' })
                                   }
                                   placeholder="请选择隔离方式"
                                   className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
                                   allowClear
                                   disabled={selectedNode.data?.type === 'releaseIsolation'}
                                 >
-                                  {isolationMethodDictList.map((item) => (
+                                  {isolationTypeDictList.map((item) => (
                                     <Select.Option key={item.id} value={item.value}>
                                       {item.label}
                                     </Select.Option>
@@ -3326,7 +3387,7 @@ export default function ProcessDesigner() {
 
                               {/* 盲板和拆除:显示负责人 */}
                               {/* 字典值:0=盲板,1=上锁挂牌,2=拆除 */}
-                              {(nodeConfig.isolationMethod === '0' || nodeConfig.isolationMethod === '2') && (
+                              {(nodeConfig.isolationType === '0' || nodeConfig.isolationType === '2') && (
                                 <div>
                                   <label className="block text-sm font-medium text-gray-700 mb-2">
                                     负责人
@@ -3352,7 +3413,7 @@ export default function ProcessDesigner() {
                               )}
 
                               {/* 上锁挂牌:显示上锁人和共锁人 */}
-                              {nodeConfig.isolationMethod === '1' && (
+                              {nodeConfig.isolationType === '1' && (
                                 <>
                                   <div>
                                     <label className="block text-sm font-medium text-gray-700 mb-2">

+ 2164 - 0
src/components/TaskManagement.tsx

@@ -0,0 +1,2164 @@
+import React, { useState, useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { Eye, Search, RotateCcw } from 'lucide-react';
+import { Button, Space, Table as AntdTable, Input, message, Modal, Form as AntdForm, Card, Alert, Select, DatePicker, InputNumber, Switch, Radio, Checkbox, Cascader, Upload } from 'antd';
+import { UploadOutlined, LockOutlined, KeyOutlined } from '@ant-design/icons';
+import type { ColumnsType } from 'antd/es/table';
+import { toast } from 'sonner';
+import dayjs, { Dayjs } from 'dayjs';
+import { taskManagementApi, MyTaskVO, MyTaskPageParam, PageResponse, MyTaskNodeDetailVO, UpdateNodeApprovalParam } from '../api/mytask';
+import { dateFormatter } from '../utils/formatTime';
+import { DICT_TYPE, getDictLabel } from '../utils/dict';
+import { setConfAndFields2, FormCreateData } from '../utils/formCreate';
+
+// 辅助函数:安全地将值转换为 dayjs 对象
+const safeToDayjs = (value: any): dayjs.Dayjs | null => {
+  if (!value && value !== 0) {
+    return null;
+  }
+  
+  // 如果是数字(时间戳)
+  if (typeof value === 'number') {
+    // dayjs 默认将数字当作毫秒级时间戳处理
+    // 但如果数字小于 10000000000(10位),可能是秒级时间戳,需要转换为毫秒
+    let timestamp = value;
+    const originalTimestamp = timestamp;
+    if (timestamp > 0 && timestamp < 10000000000) {
+      // 秒级时间戳,转换为毫秒
+      timestamp = timestamp * 1000;
+      console.log(`时间戳转换: 秒级 -> 毫秒级`, { original: originalTimestamp, converted: timestamp });
+    }
+    const dayjsValue = dayjs(timestamp);
+    if (dayjsValue.isValid()) {
+      console.log(`时间戳解析成功:`, {
+        original: value,
+        timestamp,
+        date: dayjsValue.format('YYYY-MM-DD HH:mm:ss'),
+        year: dayjsValue.year()
+      });
+      return dayjsValue;
+    } else {
+      console.warn(`时间戳解析失败:`, { original: value, timestamp });
+      return null;
+    }
+  }
+  
+  // 如果是字符串
+  if (typeof value === 'string' && value.trim() !== '') {
+    // 尝试判断是否为时间戳字符串
+    const numValue = Number(value);
+    if (!isNaN(numValue) && numValue > 0 && numValue.toString() === value.trim()) {
+      // 是纯数字字符串,可能是时间戳
+      // 判断是秒级还是毫秒级时间戳
+      let timestamp = numValue;
+      const originalTimestamp = timestamp;
+      if (timestamp < 10000000000) {
+        // 秒级时间戳(10位数字),转换为毫秒
+        timestamp = timestamp * 1000;
+        console.log(`时间戳转换: 秒级 -> 毫秒级`, { original: originalTimestamp, converted: timestamp });
+      } else {
+        console.log(`时间戳转换: 毫秒级时间戳`, { timestamp });
+      }
+      // 否则是毫秒级时间戳(13位或更多),直接使用
+      const dayjsValue = dayjs(timestamp);
+      if (dayjsValue.isValid()) {
+        console.log(`时间戳解析成功:`, {
+          original: value,
+          timestamp,
+          date: dayjsValue.format('YYYY-MM-DD HH:mm:ss'),
+          year: dayjsValue.year()
+        });
+        return dayjsValue;
+      } else {
+        console.warn(`时间戳解析失败:`, { original: value, timestamp });
+        return null;
+      }
+    } else {
+      // 普通日期字符串
+      const dayjsValue = dayjs(value);
+      return dayjsValue.isValid() ? dayjsValue : null;
+    }
+  }
+  
+  // 如果已经是 dayjs 对象,检查是否有 isValid 方法
+  if (value && typeof value === 'object' && typeof value.isValid === 'function') {
+    try {
+      return value.isValid() ? value : null;
+    } catch (e) {
+      // isValid 调用失败,尝试重新创建
+    }
+  }
+  
+  // 如果是对象,检查是否有 isValid 方法(可能是序列化后的 dayjs 对象)
+  if (value && typeof value === 'object') {
+    // 尝试重新创建 dayjs 对象
+    try {
+      // 如果有 $d 属性,可能是 dayjs 对象
+      if (value.$d) {
+        const dayjsValue = dayjs(value.$d);
+        return dayjsValue.isValid() ? dayjsValue : null;
+      }
+      // 如果有时间戳相关的属性
+      if (value.valueOf && typeof value.valueOf === 'function') {
+        const timestamp = value.valueOf();
+        if (typeof timestamp === 'number') {
+          const dayjsValue = dayjs(timestamp);
+          return dayjsValue.isValid() ? dayjsValue : null;
+        }
+      }
+    } catch (e) {
+      // 转换失败,返回 null
+      return null;
+    }
+  }
+  
+  return null;
+};
+
+// 辅助函数:验证 dayjs 对象是否有效
+const isValidDayjs = (value: any): boolean => {
+  if (!value) return false;
+  if (typeof value !== 'object') return false;
+  if (typeof value.isValid !== 'function') return false;
+  try {
+    return value.isValid();
+  } catch (e) {
+    return false;
+  }
+};
+
+// 辅助函数:将表单值中的日期字符串转换为 dayjs 对象
+const convertDateValues = (formValues: any, fields: any[]): any => {
+  const fieldTypeMap: { [key: string]: string } = {};
+  
+  // 建立字段名到类型的映射
+  fields.forEach((field: any) => {
+    try {
+      const fieldObj = typeof field === 'string' ? JSON.parse(field) : field;
+      const fieldName = fieldObj.name || fieldObj.field;
+      if (fieldName) {
+        fieldTypeMap[fieldName] = fieldObj.type;
+      }
+    } catch (e) {
+      // 解析失败,跳过
+      console.warn('解析字段配置失败:', e);
+    }
+  });
+  
+  // 转换日期值
+  const convertedValues: any = {};
+  Object.keys(formValues).forEach((fieldName) => {
+    const fieldType = fieldTypeMap[fieldName];
+    const value = formValues[fieldName];
+    
+    if (fieldType === 'date' || fieldType === 'datetime' || fieldType === 'timepicker') {
+      // 日期、日期时间或时间选择器类型
+      convertedValues[fieldName] = safeToDayjs(value) || undefined;
+    } else if (fieldType === 'daterange') {
+      // 日期范围类型
+      if (Array.isArray(value) && value.length === 2) {
+        const [start, end] = value;
+        const startDayjs = safeToDayjs(start);
+        const endDayjs = safeToDayjs(end);
+        
+        // 调试日志
+        if (startDayjs && endDayjs) {
+          console.log(`日期范围字段 ${fieldName} 转换成功:`, {
+            original: { start, end },
+            converted: {
+              start: startDayjs.format('YYYY-MM-DD HH:mm:ss'),
+              end: endDayjs.format('YYYY-MM-DD HH:mm:ss')
+            }
+          });
+          convertedValues[fieldName] = [startDayjs, endDayjs];
+        } else {
+          console.warn(`日期范围字段 ${fieldName} 转换失败:`, { start, end, startDayjs, endDayjs });
+          convertedValues[fieldName] = undefined;
+        }
+      } else {
+        convertedValues[fieldName] = undefined;
+      }
+    } else {
+      // 其他类型直接使用原值
+      convertedValues[fieldName] = value;
+    }
+  });
+  
+  return convertedValues;
+};
+
+export default function TaskManagement() {
+  const navigate = useNavigate();
+  
+  const [loading, setLoading] = useState(true);
+  const [list, setList] = useState<MyTaskVO[]>([]);
+  const [total, setTotal] = useState(0);
+  const [queryParams, setQueryParams] = useState<MyTaskPageParam>({
+    pageNo: 1,
+    pageSize: 10,
+    key: '',
+  });
+  const [searchKey, setSearchKey] = useState('');
+  const [approvalStatusDictList, setApprovalStatusDictList] = useState<any[]>([]);
+  
+  // 节点详情弹框相关状态
+  const [detailVisible, setDetailVisible] = useState(false);
+  const [detailLoading, setDetailLoading] = useState(false);
+  const [detailData, setDetailData] = useState<MyTaskNodeDetailVO | null>(null);
+  const [formData, setFormData] = useState<FormCreateData>({
+    rule: [],
+    option: {}
+  });
+  const [formLoading, setFormLoading] = useState(false); // 表单加载状态
+  const [originalFields, setOriginalFields] = useState<string[]>([]); // 保存原始的 fields 数组(JSON 字符串数组)
+  const [originalConf, setOriginalConf] = useState<string>(''); // 保存原始的 conf(JSON 字符串)
+  const [detailForm] = AntdForm.useForm();
+  const [approvalComment, setApprovalComment] = useState(''); // 审核意见
+  const [approvalLoading, setApprovalLoading] = useState(false); // 审核操作loading状态
+  const [submitLoading, setSubmitLoading] = useState(false); // 提交操作loading状态
+  
+  
+  // 组件挂载时打印调试信息
+  useEffect(() => {
+    console.log('TaskManagement 组件已加载');
+  }, []);
+  
+  // 监听弹框状态变化
+  useEffect(() => {
+    console.log('TaskManagement: detailVisible 状态变化', detailVisible, 'detailData:', detailData);
+  }, [detailVisible, detailData]);
+  
+  // 获取审批状态字典
+  const getApprovalStatusDictList = async () => {
+    try {
+      const { dictDataApi } = await import('../api/DictData');
+      const response = await dictDataApi.getDictDataPage({
+        pageNo: 1,
+        pageSize: -1,
+        dictType: 'approval_status',
+      });
+      const data = (response as any)?.data || response;
+      const dictList = data?.list || [];
+      setApprovalStatusDictList(dictList);
+      console.log('TaskManagement: 获取审批状态字典成功', dictList);
+    } catch (error: any) {
+      console.error('获取审批状态字典失败:', error);
+    }
+  };
+
+  useEffect(() => {
+    getApprovalStatusDictList();
+  }, []);
+  
+  /** 查询列表 */
+  const getList = async (params?: MyTaskPageParam) => {
+    const searchParams = params || queryParams;
+    setLoading(true);
+    try {
+      console.log('TaskManagement: 开始获取任务管理列表', searchParams);
+      const response = await taskManagementApi.getAdminWorkPage(searchParams);
+      console.log('TaskManagement: 接口响应', response);
+      const data = (response as any)?.data || response;
+      const pageData = (data as PageResponse<MyTaskVO>);
+      console.log('TaskManagement: 解析后的数据', pageData);
+      setList(pageData.list || []);
+      setTotal(pageData.total || 0);
+    } catch (error: any) {
+      console.error('TaskManagement: 获取任务管理列表失败', error);
+      toast.error(error.message || '获取任务管理列表失败');
+      setList([]);
+      setTotal(0);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    console.log('TaskManagement: useEffect 触发,queryParams:', queryParams);
+    getList();
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [queryParams.pageNo, queryParams.pageSize, queryParams.key]);
+
+  // 搜索
+  const handleSearch = () => {
+    setQueryParams({
+      ...queryParams,
+      pageNo: 1,
+      key: searchKey.trim() || undefined,
+    });
+  };
+
+  // 重置
+  const handleReset = () => {
+    setSearchKey('');
+    setQueryParams({
+      ...queryParams,
+      pageNo: 1,
+      key: undefined,
+    });
+  };
+
+  // 默认表单配置
+  const defaultFormConfig = {
+    name: '',
+    labelPosition: 'right',
+    formSize: 'middle',
+    labelSuffix: '',
+    labelWidth: 100,
+    hideRequiredMark: false,
+    showValidationError: true,
+    inlineValidation: false,
+    showSubmitButton: false,
+    showResetButton: false,
+  };
+
+  // 渲染字段预览(支持嵌套结构)
+  const renderFieldPreview = (field: any, parentSpanStyle?: React.CSSProperties): React.ReactNode => {
+    const formConfig = formData.option?.formConfig || defaultFormConfig;
+    const layoutColumns = formConfig.layoutColumns || 1;
+    const spanStyle = parentSpanStyle || (layoutColumns > 1 ? { gridColumn: `span ${Math.min(layoutColumns, field.span || 1)}` } : undefined);
+
+    // 处理容器类型(card 和 grid)
+    if (field.type === 'card') {
+      const children = field.children || [];
+      // 优先使用 label(字段名称),如果没有则使用 cardTitle,最后使用默认值
+      const cardTitle = field.label || field.cardTitle || '卡片容器';
+      return (
+        <div key={field.id} style={spanStyle} className="mb-4">
+          <Card title={cardTitle} className="w-full">
+            <div className="space-y-4">
+              {children.map((child: any) => renderFieldPreview(child))}
+            </div>
+          </Card>
+        </div>
+      );
+    }
+
+    if (field.type === 'grid') {
+      const gridColumns = field.gridColumns || 2;
+      const children = field.children || [];
+      return (
+        <div key={field.id} style={spanStyle} className="mb-4">
+          <div
+            style={{
+              display: 'grid',
+              gridTemplateColumns: `repeat(${gridColumns}, minmax(0, 1fr))`,
+              gap: '16px',
+            }}
+          >
+            {children.map((child: any) => {
+              const childSpanStyle = gridColumns > 1 
+                ? { gridColumn: `span ${Math.min(gridColumns, child.span || 1)}` } 
+                : undefined;
+              return (
+                <div key={child.id} style={childSpanStyle}>
+                  {renderFieldPreview(child)}
+                </div>
+              );
+            })}
+          </div>
+        </div>
+      );
+    }
+
+    // 处理 alert 类型
+    if (field.type === 'alert') {
+      const themeClass =
+        field.alertTheme === 'dark'
+          ? 'bg-gray-800 text-white border-gray-700'
+          : '';
+      return (
+        <div key={field.id} className="mb-4" style={spanStyle}>
+          <Alert
+            message={field.alertTitle || field.label}
+            description={field.alertDescription}
+            type={field.alertType || 'info'}
+            showIcon={field.alertShowIcon !== false}
+            closable={field.alertClosable}
+            closeText={field.alertCloseText}
+            className={`${field.alertCentered ? 'text-center' : ''} ${themeClass}`}
+            banner
+            style={field.style}
+          />
+          {field.hint && <div className="text-xs text-gray-400 mt-1">{field.hint}</div>}
+        </div>
+      );
+    }
+
+    // 处理普通字段
+    switch (field.type) {
+      case 'textarea':
+        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}
+            >
+              <Input.TextArea 
+                placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请输入'} 
+                rows={4}
+                allowClear={field.showClear}
+                maxLength={field.maxLength}
+                readOnly={field.readOnly}
+                disabled={field.disabled}
+                size={field.size || 'middle'}
+              />
+            </AntdForm.Item>
+          </div>
+        );
+      case 'password':
+        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}
+            >
+              <Input.Password 
+                placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请输入'}
+                allowClear={field.showClear}
+                maxLength={field.maxLength}
+                readOnly={field.readOnly}
+                disabled={field.disabled}
+                size={field.size || 'middle'}
+              />
+            </AntdForm.Item>
+          </div>
+        );
+      case 'number':
+        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}
+            >
+              <InputNumber 
+                style={{ width: '100%' }} 
+                placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请输入'}
+                max={field.maxLength}
+                readOnly={field.readOnly}
+                disabled={field.disabled}
+                size={field.size || 'middle'}
+              />
+            </AntdForm.Item>
+          </div>
+        );
+      case 'select':
+        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}
+            >
+              <Select
+                placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择'}
+                allowClear={field.showClear}
+                disabled={field.disabled}
+                size={field.size || 'middle'}
+              >
+                {(field.options || []).map((opt: any, idx: number) => (
+                  <Select.Option key={idx} value={opt.value}>{opt.label}</Select.Option>
+                ))}
+              </Select>
+            </AntdForm.Item>
+          </div>
+        );
+      case '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}
+              getValueFromEvent={(value) => {
+                // 选择日期时,将 dayjs 对象转换为毫秒级时间戳
+                if (!value) return undefined;
+                if (value && typeof value === 'object' && 'valueOf' in value && typeof value.valueOf === 'function' && 'isValid' in value && typeof value.isValid === 'function') {
+                  // 确保是有效的 dayjs 对象
+                  if (value.isValid()) {
+                    return value.valueOf(); // dayjs 的 valueOf() 返回毫秒级时间戳
+                  }
+                  return undefined;
+                }
+                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 && typeof value.isValid === 'function') {
+                  return value.isValid() ? value : undefined;
+                }
+                return undefined;
+              }}
+            >
+              <DatePicker 
+                style={{ width: '100%' }} 
+                placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择日期'}
+                allowClear={field.showClear}
+                disabled={field.disabled}
+                size={field.size || 'middle'}
+              />
+            </AntdForm.Item>
+          </div>
+        );
+      case 'daterange':
+        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}
+              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 && typeof start.valueOf === 'function' && 'isValid' in start && typeof start.isValid === 'function' &&
+                    end && typeof end === 'object' && 'valueOf' in end && typeof end.valueOf === 'function' && 'isValid' in end && typeof end.isValid === 'function') {
+                  // 确保都是有效的 dayjs 对象
+                  if (start.isValid() && end.isValid()) {
+                    return [start.valueOf(), end.valueOf()]; // 返回毫秒级时间戳数组
+                  }
+                  return undefined;
+                }
+                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 && typeof start.isValid === 'function') {
+                    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 && typeof end.isValid === 'function') {
+                    endDayjs = end.isValid() ? end : null;
+                  }
+                  
+                  if (startDayjs && endDayjs) {
+                    return [startDayjs, endDayjs];
+                  }
+                }
+                return undefined;
+              }}
+            >
+              <DatePicker.RangePicker 
+                style={{ width: '100%' }} 
+                placeholder={Array.isArray(field.placeholder) ? field.placeholder as [string, string] : ['开始日期', '结束日期']}
+                allowClear={field.showClear}
+                disabled={field.disabled}
+                size={field.size || 'middle'}
+              />
+            </AntdForm.Item>
+          </div>
+        );
+      case '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}
+              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 && typeof value.isValid === 'function') {
+                  return value.isValid() ? value : undefined;
+                }
+                return undefined;
+              }}
+            >
+              <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>
+        );
+      case 'switch':
+        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}
+              valuePropName="checked"
+            >
+              <Switch disabled={field.disabled} />
+            </AntdForm.Item>
+          </div>
+        );
+      case 'radio':
+        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}
+            >
+              <Radio.Group disabled={field.disabled}>
+                {(field.options || []).map((opt: any, idx: number) => (
+                  <Radio key={idx} value={opt.value}>{opt.label}</Radio>
+                ))}
+              </Radio.Group>
+            </AntdForm.Item>
+          </div>
+        );
+      case 'checkbox':
+        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}
+            >
+              <Checkbox.Group disabled={field.disabled}>
+                {(field.options || []).map((opt: any, idx: number) => (
+                  <Checkbox key={idx} value={opt.value}>{opt.label}</Checkbox>
+                ))}
+              </Checkbox.Group>
+            </AntdForm.Item>
+          </div>
+        );
+      case 'cascader':
+        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}
+            >
+              <Cascader
+                placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请选择'}
+                options={field.cascaderOptions || []}
+                className="w-full"
+                allowClear={field.showClear}
+                disabled={field.disabled}
+                size={field.size || 'middle'}
+              />
+            </AntdForm.Item>
+          </div>
+        );
+      case 'upload':
+        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}
+            >
+              <Upload
+                disabled={field.disabled}
+                maxCount={field.uploadType === 'single-image' ? 1 : field.maxCount || (field.uploadType === 'multiple-image' ? 9 : undefined)}
+                accept={field.accept || (field.uploadType === 'single-image' || field.uploadType === 'multiple-image' ? 'image/*' : undefined)}
+                listType={(field.uploadType === 'file' ? 'text' : 'picture-card') as 'text' | 'picture-card' | 'picture'}
+                multiple={field.uploadType !== 'single-image'}
+              >
+                {field.uploadType === 'file' ? (
+                  <Button icon={<UploadOutlined />}>上传文件</Button>
+                ) : (
+                  <div>
+                    <UploadOutlined />
+                    <div style={{ marginTop: 8 }}>上传</div>
+                  </div>
+                )}
+              </Upload>
+            </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
+              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}
+            >
+              <Input 
+                type={field.inputType || 'text'}
+                placeholder={typeof field.placeholder === 'string' ? field.placeholder : '请输入'}
+                allowClear={field.showClear}
+                maxLength={field.maxLength}
+                readOnly={field.readOnly}
+                disabled={field.disabled}
+                size={field.size || 'middle'}
+              />
+            </AntdForm.Item>
+          </div>
+        );
+    }
+  };
+
+  // 查看节点详情(节点详情弹框)
+  const handleViewDetail = async (record: MyTaskVO) => {
+    console.log('TaskManagement: handleViewDetail 被调用', record);
+    
+    if (!record.nodeId) {
+      console.warn('TaskManagement: 节点ID不存在', record);
+      message.warning('节点ID不存在');
+      return;
+    }
+    
+    setDetailLoading(true);
+    setDetailVisible(true); // 先打开弹框显示加载状态
+    console.log('TaskManagement: 弹框状态已设置为 true (加载中)');
+    
+    try {
+      console.log('TaskManagement: 开始获取节点详情', { nodeId: record.nodeId });
+      const response = await taskManagementApi.getAdminWorkNodeDetail(record.nodeId);
+      console.log('TaskManagement: 节点详情响应', response);
+      
+      let data: any = response;
+    
+      // 直接从第一层 data.formId 获取 formId
+      let extractedFormId: number | undefined = undefined;
+      if (data?.formId !== undefined && data?.formId !== null) {
+        const formIdValue = data.formId;
+        if (typeof formIdValue === 'string') {
+          const parsed = parseInt(formIdValue, 10);
+          if (!isNaN(parsed)) {
+            extractedFormId = parsed;
+            console.log('TaskManagement: ✅ 从 data.formId (字符串) 提取到 formId', extractedFormId);
+          }
+        } else if (typeof formIdValue === 'number') {
+          extractedFormId = formIdValue;
+          console.log('TaskManagement: ✅ 从 data.formId (数字) 提取到 formId', extractedFormId);
+        }
+      }
+      
+      console.log('TaskManagement: formId 提取结果', { 
+        extractedFormId, 
+        '原始 data.formId': data?.formId,
+        'data.formId 类型': typeof data?.formId
+      });
+      
+      if (extractedFormId === undefined) {
+        console.warn('TaskManagement: ⚠️ 未能提取到 formId', {
+          'data.formId': data?.formId,
+          'data': data
+        });
+      }
+      
+      // 合并列表数据中的作业信息(作业名称、编号、负责人、时间等)
+      // 直接使用第一层 data,不解析 data.data
+      const detailDataWithWorkInfo: MyTaskNodeDetailVO = {
+        ...(data || {}),
+        workName: record.name || data?.workName || data?.name,
+        orderNo: record.orderNo || data?.orderNo,
+        workerUserName: record.workerUserName || data?.workerUserName,
+        initiatorName: record.initiatorName || data?.initiatorName || data?.initiator || record.initiator || '',
+        workTime: record.workTime || data?.workTime,
+        // 确保 type 字段存在,使用第一层 data.type
+        type: data?.type || data?.nodeType || '',
+        nodeType: data?.nodeType || data?.type || '', // 同时设置 nodeType 作为兼容
+        // 使用提取到的 formId
+        formId: extractedFormId,
+      };
+      
+      console.log('TaskManagement: 合并后的详情数据 - type 和 formId 字段', {
+        'data.type': data?.type,
+        'data.nodeType': data?.nodeType,
+        'data.formId': data?.formId,
+        'detailDataWithWorkInfo.type': detailDataWithWorkInfo.type,
+        'detailDataWithWorkInfo.nodeType': detailDataWithWorkInfo.nodeType,
+        'detailDataWithWorkInfo.formId': detailDataWithWorkInfo.formId,
+      });
+      
+      console.log('TaskManagement: 合并后的详情数据', detailDataWithWorkInfo);
+      console.log('TaskManagement: 节点类型', detailDataWithWorkInfo.type);
+      
+      // 先设置数据,确保弹框有内容显示
+      setDetailData(detailDataWithWorkInfo);
+      console.log('TaskManagement: detailData 已设置', detailDataWithWorkInfo);
+      
+      // 确保弹框是打开状态
+      setDetailVisible(true);
+      setDetailLoading(false);
+      console.log('TaskManagement: 弹框状态设置为 true,loading 设置为 false');
+      
+      // 根据节点类型决定是否需要获取表单
+      // isolation、releaseIsolation 和 returnLock 不需要表单,其他类型(review 和其他)需要根据 formId 获取表单
+      const nodeType = detailDataWithWorkInfo.type || detailDataWithWorkInfo.nodeType || '';
+      const isIsolation = nodeType === 'isolation' || nodeType === '隔离' || nodeType === '隔离/方案';
+      const isReleaseIsolation = nodeType === 'releaseIsolation' || nodeType === '解除隔离';
+      const isReturnLock = nodeType === 'returnLock';
+      
+      // 检查任务状态是否为"已通过"
+      const isApproved = detailDataWithWorkInfo.approvalStatus === 'approved';
+      
+      // 获取 formId(可能为 0,所以需要明确检查 undefined 和 null)
+      const formId = detailDataWithWorkInfo.formId;
+      // formId 可能是数字(包括 0)或字符串,只要不是 undefined、null 或空字符串,就认为有 formId
+      const hasFormId = formId !== undefined && formId !== null && (
+        typeof formId === 'number' || 
+        (typeof formId === 'string' && formId !== '' && formId.trim() !== '')
+      );
+      
+      // 检查是否有 formData(已提交的表单数据)
+      const hasFormData = detailDataWithWorkInfo.formData && (
+        (typeof detailDataWithWorkInfo.formData === 'string' && detailDataWithWorkInfo.formData.trim() !== '') ||
+        (typeof detailDataWithWorkInfo.formData === 'object' && Object.keys(detailDataWithWorkInfo.formData).length > 0)
+      );
+      
+      console.log('TaskManagement: 表单获取判断', {
+        nodeType,
+        isIsolation,
+        isReleaseIsolation,
+        isReturnLock,
+        isApproved,
+        hasFormData,
+        formId,
+        formIdType: typeof formId,
+        hasFormId,
+        '从formData获取': isApproved && hasFormData,
+        '从formId获取': !isIsolation && !isReleaseIsolation && !isReturnLock && hasFormId && !(isApproved && hasFormData),
+        'detailDataWithWorkInfo': detailDataWithWorkInfo,
+      });
+      
+      // 如果任务状态为"已通过"且有 formData,则从 formData 中解析表单结构
+      if (isApproved && hasFormData) {
+        console.log('TaskManagement: ✅ 任务已通过,从 formData 中解析表单结构');
+        setFormLoading(true);
+        
+        try {
+          // 解析 formData(可能是 JSON 字符串或对象)
+          let parsedFormData: any;
+          if (typeof detailDataWithWorkInfo.formData === 'string') {
+            parsedFormData = JSON.parse(detailDataWithWorkInfo.formData);
+          } else {
+            parsedFormData = detailDataWithWorkInfo.formData;
+          }
+          
+          console.log('TaskManagement: 解析后的 formData', parsedFormData);
+          
+          // 从 formData 中提取 conf 和 fields
+          const conf = parsedFormData.conf;
+          const fields = parsedFormData.fields;
+          
+          if (conf && fields) {
+            // 保存原始的 conf 和 fields(JSON 字符串格式)
+            const confString = typeof conf === 'string' ? conf : JSON.stringify(conf);
+            const fieldsArray = Array.isArray(fields) ? fields : [];
+            const fieldsStringArray = fieldsArray.map((field: any) => {
+              return typeof field === 'string' ? field : JSON.stringify(field);
+            });
+            
+            setOriginalConf(confString);
+            setOriginalFields(fieldsStringArray);
+            console.log('TaskManagement: 从 formData 保存原始表单数据', {
+              conf: confString,
+              fieldsCount: fieldsStringArray.length,
+              fields: fieldsStringArray
+            });
+            
+            // 解析 fields 中的每个字段,提取 value 值用于回显
+            const formValues: any = {};
+            const fieldTypeMap: { [key: string]: string } = {}; // 存储字段名到类型的映射
+            
+            // 先解析所有字段,建立字段类型映射
+            fieldsStringArray.forEach((fieldString: string) => {
+              try {
+                const fieldObj = typeof fieldString === 'string' ? JSON.parse(fieldString) : fieldString;
+                const fieldName = fieldObj.name || fieldObj.field;
+                if (fieldName) {
+                  fieldTypeMap[fieldName] = fieldObj.type;
+                }
+              } catch (e) {
+                console.error('TaskManagement: 解析字段 JSON 失败', e, fieldString);
+              }
+            });
+            
+            // 提取值并转换日期类型
+            fieldsStringArray.forEach((fieldString: string) => {
+              try {
+                const fieldObj = typeof fieldString === 'string' ? JSON.parse(fieldString) : fieldString;
+                const fieldName = fieldObj.name || fieldObj.field;
+                if (fieldName && fieldObj.value !== undefined && fieldObj.value !== null) {
+                  const fieldType = fieldTypeMap[fieldName];
+                  let value = fieldObj.value;
+                  
+                  // 如果是日期类型,转换为 dayjs 对象
+                  if (fieldType === 'date' || fieldType === 'datetime') {
+                    if (typeof value === 'string' && value.trim() !== '') {
+                      const dayjsValue = dayjs(value);
+                      formValues[fieldName] = dayjsValue.isValid() ? dayjsValue : undefined;
+                    } else {
+                      formValues[fieldName] = undefined;
+                    }
+                  } else if (fieldType === 'daterange') {
+                    // 日期范围类型,转换为 dayjs 数组
+                    if (Array.isArray(value) && value.length === 2) {
+                      const [start, end] = value;
+                      const startDayjs = typeof start === 'string' && start ? dayjs(start) : null;
+                      const endDayjs = typeof end === 'string' && end ? dayjs(end) : null;
+                      if (startDayjs && startDayjs.isValid() && endDayjs && endDayjs.isValid()) {
+                        formValues[fieldName] = [startDayjs, endDayjs];
+                      } else {
+                        formValues[fieldName] = undefined;
+                      }
+                    } else {
+                      formValues[fieldName] = undefined;
+                    }
+                  } else {
+                    // 其他类型直接使用原值
+                    formValues[fieldName] = value;
+                  }
+                }
+              } catch (e) {
+                console.error('TaskManagement: 解析字段 JSON 失败', e, fieldString);
+              }
+            });
+            
+            console.log('TaskManagement: 从 formData 提取的表单值', formValues);
+            
+            // 设置表单配置和字段
+            setConfAndFields2(setFormData, conf, fields);
+            console.log('TaskManagement: 表单配置已设置(从 formData)');
+            
+            // 转换日期值为 dayjs 对象
+            const convertedFormValues = convertDateValues(formValues, fieldsStringArray);
+            
+            // 回填表单值
+            setTimeout(() => {
+              detailForm.setFieldsValue(convertedFormValues);
+              console.log('TaskManagement: 表单数据已回填(从 formData)', convertedFormValues);
+            }, 100);
+          } else {
+            console.warn('TaskManagement: formData 中缺少 conf 或 fields', { conf: !!conf, fields: !!fields });
+            message.warning('表单数据不完整');
+          }
+        } catch (e) {
+          console.error('TaskManagement: 解析 formData 失败', e);
+          message.error('解析表单数据失败: ' + (e instanceof Error ? e.message : String(e)));
+        } finally {
+          setFormLoading(false);
+        }
+      }
+      // 如果不是隔离类型,且有表单ID,且不是从 formData 获取,则从接口获取表单配置
+      else if (!isIsolation && !isReleaseIsolation && !isReturnLock && hasFormId) {
+        console.log('TaskManagement: ✅ 满足条件,开始获取表单', { formId, nodeType });
+        // 重置表单数据
+        setFormData({ rule: [], option: {} });
+        setOriginalFields([]);
+        setOriginalConf('');
+        setFormLoading(true);
+        
+        // 立即调用接口,不使用 setTimeout,确保接口被调用
+        (async () => {
+          try {
+            // 确保 formId 是数字类型
+            const numericFormId = typeof formId === 'string' ? parseInt(formId, 10) : formId;
+            if (isNaN(numericFormId)) {
+              console.error('TaskManagement: formId 不是有效数字', formId);
+              message.error('表单ID无效');
+              setFormLoading(false);
+              return;
+            }
+            
+            console.log('TaskManagement: 🚀 开始调用表单接口', { formId: numericFormId, nodeType, url: `/bpm/form/get?id=${numericFormId}` });
+            const FormApi = await import('../api/bpm/form');
+            const formDetailResponse = await FormApi.getForm(numericFormId);
+            console.log('TaskManagement: ✅ 表单接口调用成功,原始响应', formDetailResponse);
+            
+            // 处理响应数据(可能包含 code 和 data 包装)
+            let formDetail: any = formDetailResponse;
+            if (formDetailResponse && typeof formDetailResponse === 'object' && 'data' in formDetailResponse) {
+              // 如果响应有 data 字段,使用 data
+              formDetail = (formDetailResponse as any).data || formDetailResponse;
+            }
+            
+            console.log('TaskManagement: 处理后的表单详情', formDetail);
+            
+            // 获取 conf 和 fields
+            const conf = formDetail?.conf || formDetail?.formConfig;
+            const fields = formDetail?.fields || formDetail?.formFields;
+            
+            console.log('TaskManagement: 表单配置和字段', { 
+              hasConf: !!conf, 
+              confType: typeof conf,
+              hasFields: !!fields,
+              fieldsType: Array.isArray(fields) ? 'array' : typeof fields,
+              fieldsLength: Array.isArray(fields) ? fields.length : 0
+            });
+            
+            // 解析表单配置和字段
+            if (conf && fields) {
+              // 保存原始的 conf 和 fields(JSON 字符串格式)
+              const confString = typeof conf === 'string' ? conf : JSON.stringify(conf);
+              const fieldsArray = Array.isArray(fields) ? fields : [];
+              const fieldsStringArray = fieldsArray.map((field: any) => {
+                return typeof field === 'string' ? field : JSON.stringify(field);
+              });
+              
+              setOriginalConf(confString);
+              setOriginalFields(fieldsStringArray);
+              console.log('TaskManagement: 保存原始表单数据', {
+                conf: confString,
+                fieldsCount: fieldsStringArray.length,
+                fields: fieldsStringArray
+              });
+              
+              // setConfAndFields2 会自动处理 JSON 字符串解析
+              setConfAndFields2(setFormData, conf, fields);
+              console.log('TaskManagement: 表单配置已设置', { 
+                conf: typeof conf === 'string' ? 'JSON字符串' : '对象',
+                fieldsCount: Array.isArray(fields) ? fields.length : 0 
+              });
+              
+              // 如果有表单数据值,回填到表单(在表单配置设置后)
+              if (detailDataWithWorkInfo.formData) {
+                try {
+                  let formValues = typeof detailDataWithWorkInfo.formData === 'string' 
+                    ? JSON.parse(detailDataWithWorkInfo.formData) 
+                    : detailDataWithWorkInfo.formData;
+                  
+                  // 转换日期值为 dayjs 对象
+                  const fieldsArray = Array.isArray(fields) ? fields : [];
+                  formValues = convertDateValues(formValues, fieldsArray);
+                  
+                  // 清理无效的日期值,确保所有日期字段都是有效的 dayjs 对象或 undefined
+                  const cleanedFormValues: any = {};
+                  Object.keys(formValues).forEach(key => {
+                    const value = formValues[key];
+                    const field = fieldsArray.find((f: any) => {
+                      const fieldObj = typeof f === 'string' ? JSON.parse(f) : f;
+                      return (fieldObj.name || fieldObj.field) === key;
+                    });
+                    
+                    if (field) {
+                      const fieldObj = typeof field === 'string' ? JSON.parse(field) : field;
+                      const fieldType = fieldObj.type;
+                      
+                      if (fieldType === 'date' || fieldType === 'datetime' || fieldType === 'timepicker') {
+                        // 日期、日期时间或时间选择器类型:确保是有效的 dayjs 对象或 undefined
+                        if (isValidDayjs(value)) {
+                          // 已经是有效的 dayjs 对象
+                          cleanedFormValues[key] = value;
+                        } else if (value !== null && value !== undefined) {
+                          // 尝试转换
+                          const dayjsValue = safeToDayjs(value);
+                          cleanedFormValues[key] = dayjsValue || undefined;
+                        } else {
+                          cleanedFormValues[key] = undefined;
+                        }
+                      } else if (fieldType === 'daterange') {
+                        // 日期范围类型:确保数组中的每个元素都是有效的 dayjs 对象
+                        if (Array.isArray(value) && value.length === 2) {
+                          const [start, end] = value;
+                          // 验证开始时间
+                          let startDayjs: dayjs.Dayjs | null = null;
+                          if (isValidDayjs(start)) {
+                            startDayjs = start;
+                          } else {
+                            startDayjs = safeToDayjs(start);
+                          }
+                          // 验证结束时间
+                          let endDayjs: dayjs.Dayjs | null = null;
+                          if (isValidDayjs(end)) {
+                            endDayjs = end;
+                          } else {
+                            endDayjs = safeToDayjs(end);
+                          }
+                          cleanedFormValues[key] = (startDayjs && endDayjs) ? [startDayjs, endDayjs] : undefined;
+                        } else {
+                          cleanedFormValues[key] = undefined;
+                        }
+                      } else {
+                        // 其他类型直接使用
+                        cleanedFormValues[key] = value;
+                      }
+                    } else {
+                      // 没有找到字段定义,直接使用原值
+                      cleanedFormValues[key] = value;
+                    }
+                  });
+                  
+                  // 延迟一下,确保表单字段已经渲染
+                  setTimeout(() => {
+                    try {
+                      detailForm.setFieldsValue(cleanedFormValues);
+                      console.log('TaskManagement: 表单数据已回填', cleanedFormValues);
+                    } catch (e) {
+                      console.error('TaskManagement: 设置表单值失败', e);
+                      // 如果设置失败,尝试只设置非日期字段
+                      const safeValues: any = {};
+                      Object.keys(cleanedFormValues).forEach(key => {
+                        const value = cleanedFormValues[key];
+                        const field = fieldsArray.find((f: any) => {
+                          const fieldObj = typeof f === 'string' ? JSON.parse(f) : f;
+                          return (fieldObj.name || fieldObj.field) === key;
+                        });
+                        if (field) {
+                          const fieldObj = typeof field === 'string' ? JSON.parse(field) : field;
+                          const fieldType = fieldObj.type;
+                          if (fieldType !== 'date' && fieldType !== 'datetime' && fieldType !== 'daterange' && fieldType !== 'timepicker') {
+                            safeValues[key] = value;
+                          }
+                        } else {
+                          safeValues[key] = value;
+                        }
+                      });
+                      detailForm.setFieldsValue(safeValues);
+                    }
+                  }, 100);
+                } catch (e) {
+                  console.error('TaskManagement: 解析表单数据失败', e);
+                }
+              }
+            } else {
+              console.warn('TaskManagement: 表单详情缺少配置或字段', { conf: !!conf, fields: !!fields, formDetail });
+              message.warning('表单配置不完整');
+            }
+          } catch (e) {
+            console.error('TaskManagement: 获取表单详情失败', e);
+            message.error('获取表单详情失败: ' + (e instanceof Error ? e.message : String(e)));
+            // 即使获取表单失败,也继续显示弹框
+          } finally {
+            setFormLoading(false);
+          }
+        })();
+      } else {
+        setFormLoading(false);
+        if (isIsolation || isReleaseIsolation || isReturnLock) {
+          console.log('TaskManagement: ⚠️ 隔离类型节点,跳过表单配置获取', { nodeType, isIsolation, isReleaseIsolation, isReturnLock });
+        } else if (!hasFormId) {
+          console.warn('TaskManagement: ⚠️ 没有表单ID,跳过表单配置获取', { 
+            formId, 
+            formIdType: typeof formId,
+            hasFormId,
+            nodeType,
+            'detailDataWithWorkInfo': detailDataWithWorkInfo 
+          });
+          // 不显示提示,静默处理
+        } else {
+          console.warn('TaskManagement: ⚠️ 未知原因未获取表单', { 
+            nodeType, 
+            isIsolation, 
+            isReleaseIsolation,
+            isReturnLock, 
+            hasFormId,
+            formId 
+          });
+        }
+      }
+      
+      // 注意:表单数据回填已移到表单配置加载完成后,确保表单字段已渲染
+      
+      // 回显审核意见(如果有 approvalOpinion 字段,且不是 "pending")
+      if (detailDataWithWorkInfo.approvalOpinion && detailDataWithWorkInfo.approvalOpinion !== 'pending') {
+        setApprovalComment(detailDataWithWorkInfo.approvalOpinion);
+        console.log('TaskManagement: 回显审核意见', detailDataWithWorkInfo.approvalOpinion);
+      } else {
+        setApprovalComment(''); // 重置审核意见(包括 pending 的情况)
+      }
+      
+      setDetailLoading(false);
+      console.log('TaskManagement: 弹框已打开', { detailVisible: true, detailData: detailDataWithWorkInfo });
+    } catch (error: any) {
+      console.error('TaskManagement: 获取节点详情失败', error);
+      toast.error(error.message || '获取节点详情失败');
+      // 即使接口失败,也显示弹框(显示错误信息)
+      if (!detailData) {
+        setDetailData({
+          id: record.nodeId,
+          nodeId: record.nodeId,
+          workId: record.workId,
+          workName: record.name,
+          orderNo: record.orderNo,
+          workerUserName: record.workerUserName,
+          workTime: record.workTime,
+          type: '',
+        } as MyTaskNodeDetailVO);
+      }
+      setDetailVisible(true);
+    } finally {
+      setDetailLoading(false);
+    }
+  };
+
+  // 获取任务状态显示文本(使用 approval_status 字典)
+  const getTaskStatusText = (status: string | number | undefined): string => {
+    if (!status) return '未知';
+    const statusStr = String(status).toLowerCase();
+    const statusItem = approvalStatusDictList.find(item => String(item.value).toLowerCase() === statusStr);
+    if (statusItem) {
+      return statusItem.label || statusItem.name || '未知';
+    }
+    // 如果没有找到字典值,使用默认映射
+    const statusMap: Record<string, string> = {
+      'pending': '待审核',
+      'approved': '已通过',
+      'rejected': '已驳回',
+      'unaudited': '未审核',
+    };
+    return statusMap[statusStr] || '未知';
+  };
+
+  // 获取任务状态样式(审批状态)
+  const getTaskStatusClassName = (status: string | number | undefined): string => {
+    if (!status) return 'bg-gray-100 text-gray-600';
+    const statusStr = String(status).toLowerCase();
+    const statusMap: Record<string, string> = {
+      'pending': 'bg-yellow-100 text-yellow-700',
+      'approved': 'bg-green-100 text-green-700',
+      'rejected': 'bg-red-100 text-red-700',
+      'unaudited': 'bg-gray-100 text-gray-600',
+    };
+    return statusMap[statusStr] || 'bg-gray-100 text-gray-600';
+  };
+
+  // 表格列配置
+  const columns: ColumnsType<MyTaskVO> = [
+    {
+      title: '作业编号',
+      dataIndex: 'orderNo',
+      width: 180,
+      align: 'center',
+      render: (text: string, record: MyTaskVO) => {
+        if (!text) return '-';
+        return (
+          <span
+            className="text-blue-600 hover:text-blue-800 cursor-pointer hover:underline"
+            onClick={(e) => {
+              e.stopPropagation();
+              e.preventDefault();
+              handleViewDetail(record);
+            }}
+          >
+            {text}
+          </span>
+        );
+      },
+    },
+    {
+      title: '作业名称',
+      dataIndex: 'name',
+      width: 220,
+      align: 'center',
+      ellipsis: true,
+      render: (text: string, record: MyTaskVO) => {
+        if (!text) return '-';
+        return (
+          <span
+            className="text-blue-600 hover:text-blue-800 cursor-pointer hover:underline"
+            onClick={(e) => {
+              e.stopPropagation();
+              e.preventDefault();
+              handleViewDetail(record);
+            }}
+            title={text}
+          >
+            {text}
+          </span>
+        );
+      },
+    },
+    {
+      title: '负责人',
+      dataIndex: 'workerUserName',
+      width: 150,
+      align: 'center',
+      render: (text: string, record: MyTaskVO) => {
+        // 优先使用 workerUserName,如果没有则尝试从其他字段获取
+        return text || record.responsibleName || record.responsible || record.initiatorName || record.initiator || '-';
+      },
+    },
+    {
+      title: '当前任务',
+      dataIndex: 'currentNodeName',
+      width: 180,
+      align: 'center',
+      render: (text: string, record: MyTaskVO) => {
+        // 优先使用 currentNodeName,如果没有则使用 currentNode
+        return text || record.currentNode || '-';
+      },
+    },
+    {
+      title: '任务开始时间',
+      dataIndex: 'workTime',
+      width: 180,
+      align: 'center',
+      render: (text: string | number | Date | undefined, record: MyTaskVO) => {
+        // 优先使用 workTime,如果没有则使用其他时间字段
+        const time = text || record.taskStartTime || record.initiationTime || record.initiateTime;
+        return time ? dateFormatter(time) : '-';
+      },
+    },
+    {
+      title: '任务状态',
+      dataIndex: 'approvalStatus',
+      width: 120,
+      align: 'center',
+      render: (status: string | number | undefined, record: MyTaskVO) => {
+        // 优先使用 approvalStatus,如果没有则使用其他状态字段
+        const taskStatus = status || record.taskStatus || record.status;
+        return (
+          <span
+            className={`inline-flex px-3 py-1 rounded-lg text-xs ${getTaskStatusClassName(taskStatus)}`}
+          >
+            {getTaskStatusText(taskStatus)}
+          </span>
+        );
+      },
+    },
+    {
+      title: '操作',
+      width: 120,
+      align: 'center',
+      fixed: 'right',
+      render: (_: any, record: MyTaskVO) => (
+        <Space size="small">
+          <Button
+            type="link"
+            size="small"
+            icon={<Eye className="w-4 h-4" />}
+            onClick={(e) => {
+              e.stopPropagation();
+              e.preventDefault();
+              handleViewDetail(record);
+            }}
+          >
+            查看详情
+          </Button>
+        </Space>
+      ),
+    },
+  ];
+
+  // 计算分页数据
+  const totalPages = Math.ceil(total / queryParams.pageSize) || 1;
+
+  return (
+    <div className="space-y-6">
+      {/* 操作栏和表格容器 */}
+      <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
+        {/* 查询与操作栏 */}
+        <div className="p-4 lg:p-5 border-b border-gray-200/50">
+          <div className="flex flex-wrap items-center gap-3">
+            <Input
+              value={searchKey}
+              onChange={(e) => setSearchKey(e.target.value)}
+              placeholder="请输入作业编号或者作业名称进行查询"
+              allowClear
+              className="flex-1"
+              style={{ minWidth: 200 }}
+              onPressEnter={handleSearch}
+            />
+            <Space size="small">
+              <Button type="primary" icon={<Search className="w-4 h-4" />} onClick={handleSearch}>
+                搜索
+              </Button>
+              <Button icon={<RotateCcw className="w-4 h-4" />} onClick={handleReset}>
+                重置
+              </Button>
+            </Space>
+          </div>
+        </div>
+
+        {/* 表格容器 */}
+        <div className="overflow-hidden min-w-0">
+          <AntdTable
+            loading={loading}
+            columns={columns}
+            dataSource={list}
+            rowKey={(record) => record.id || Math.random()}
+            pagination={false}
+            scroll={{ x: 'max-content' }}
+            locale={{
+              emptyText: '暂无数据',
+            }}
+          />
+        </div>
+      </div>
+
+      {/* 分页 */}
+      {total > 0 && (
+        <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
+          <div className="flex items-center justify-between">
+            <div className="text-sm text-gray-600">
+              共 <span className="text-blue-600 font-medium">{total}</span> 条记录
+            </div>
+            <div className="flex gap-2">
+              <Button
+                onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo - 1 })}
+                disabled={queryParams.pageNo <= 1}
+              >
+                上一页
+              </Button>
+              <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
+                {queryParams.pageNo} / {totalPages}
+              </span>
+              <Button
+                onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo + 1 })}
+                disabled={queryParams.pageNo >= totalPages}
+              >
+                下一页
+              </Button>
+            </div>
+          </div>
+        </div>
+      )}
+
+      {/* 详情弹框 */}
+      <Modal
+        title={null}
+        open={detailVisible}
+        onCancel={() => {
+          console.log('TaskManagement: 弹框关闭');
+          setDetailVisible(false);
+          setDetailData(null);
+          setFormData({ rule: [], option: {} });
+          setFormLoading(false);
+          setOriginalFields([]);
+          setOriginalConf('');
+          detailForm.resetFields();
+          setApprovalComment('');
+        }}
+        footer={null}
+        width={1000}
+        destroyOnClose
+        confirmLoading={detailLoading}
+        maskClosable={false}
+        zIndex={1000}
+        styles={{
+          body: {
+            minHeight: '600px',
+            maxHeight: '700px',
+            height: '700px',
+            padding: 0,
+            display: 'flex',
+            flexDirection: 'column',
+            overflow: 'hidden'
+          }
+        }}
+      >
+        {(() => {
+          console.log('TaskManagement: Modal 内容渲染', { detailLoading, detailData, detailVisible });
+          if (detailLoading) {
+            return <div className="py-8 text-center">加载中...</div>;
+          }
+          if (detailData) {
+            return (
+              <div style={{ 
+                display: 'flex',
+                flexDirection: 'column',
+                height: '100%',
+                overflow: 'hidden'
+              }}>
+            {/* 标题区域 */}
+            <div className="mb-4" style={{ padding: '20px 24px 0' }}>
+              <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
+                <div style={{ 
+                  width: '3px', 
+                  height: '24px', 
+                  backgroundColor: '#025fff',
+                  borderRadius: '2px',
+                  flexShrink: 0
+                }}></div>
+                <h2 className="text-xl font-semibold mb-2" style={{ color: '#025fff', marginBottom: 0 }}>
+                  {detailData.workName || detailData.name || '作业详情'}
+                </h2>
+              </div>
+              <div className="text-sm flex gap-4" style={{ color: '#898f9a', marginTop: '12px' }}>
+                <span>作业编号:{detailData.orderNo || '-'}</span>
+                <span>任务负责人:{detailData.workerUserName || '-'}</span>
+                <span>作业负责人:{detailData.initiatorName || '-'}</span>
+                <span>发起时间:{detailData.workTime ? dateFormatter(detailData.workTime) : '-'}</span>
+              </div>
+            </div>
+
+            {/* 内容区域 - 可滚动 */}
+            <div className="mt-6" style={{ 
+              padding: '0 24px',
+              flex: 1,
+              overflowY: 'auto',
+              minHeight: 0
+            }}>
+              {(() => {
+                // 检查节点状态是否为已通过(approved),如果是则禁用所有输入和按钮
+                const isApproved = detailData?.approvalStatus === 'approved';
+                
+                // 从 detailData.type 或 detailData.nodeType 获取节点类型
+                const nodeType = String(detailData?.type || detailData?.nodeType || '').trim();
+                console.log('TaskManagement: 渲染内容区域 - 节点类型', nodeType, 'detailData:', detailData);
+               
+                const isReview = nodeType === 'review';
+                const isIsolation = nodeType === 'isolation';
+                const isReleaseIsolation = nodeType === 'releaseIsolation';
+                const isReturnLock = nodeType === 'returnLock';
+                
+                console.log('TaskManagement: 节点类型判断结果', { 
+                  isReview, 
+                  isIsolation, 
+                  isReleaseIsolation,
+                  isReturnLock,
+                  nodeType,
+                  'detailData.type': detailData.type,
+                  'detailData.nodeType': detailData.nodeType,
+                  'typeof detailData.type': typeof detailData.type,
+                  'String(detailData.type)': String(detailData.type)
+                });
+
+                // 隔离/方案节点、解除隔离节点和还锁节点
+                if (isIsolation || isReleaseIsolation || isReturnLock) {
+                  console.log('TaskManagement: ✅ 进入隔离节点渲染分支', { isIsolation, isReleaseIsolation, isReturnLock, nodeType });
+                  const isolationContent = (
+                    <div 
+                      key="isolation-content"
+                      style={{
+                        display: 'flex',
+                        justifyContent: 'center',
+                        alignItems: 'center',
+                        minHeight: '400px',
+                        padding: '40px 20px'
+                      }}
+                    >
+                      <Card
+                        style={{
+                          width: '100%',
+                          maxWidth: '500px',
+                          textAlign: 'center',
+                          borderRadius: '12px',
+                          boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
+                          border: '1px solid #e8e8e8'
+                        }}
+                        bodyStyle={{
+                          padding: '40px 30px'
+                        }}
+                      >
+                        <div style={{ marginBottom: '24px' }}>
+                          <LockOutlined 
+                            style={{ 
+                              fontSize: '64px', 
+                              color: '#1890ff',
+                              display: 'block'
+                            }} 
+                          />
+                        </div>
+                        <div
+                          style={{
+                            fontSize: '20px',
+                            fontWeight: 500,
+                            color: '#333',
+                            lineHeight: '1.6'
+                          }}
+                        >
+                          请前往锁控柜进行取锁,取钥匙操作
+                        </div>
+                      </Card>
+                    </div>
+                  );
+                  console.log('TaskManagement: 隔离节点内容已创建', isolationContent);
+                  return isolationContent;
+                }
+
+                // 审核类型节点 (review)
+                if (isReview) {
+                  return (
+                    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
+                      <div className="space-y-6" style={{ padding: '0 24px', flex: 1, overflowY: 'auto', minHeight: 0 }}>
+                      {/* 自定义表单 */}
+                      {formLoading ? (
+                        <div className="py-8 text-center text-gray-500">表单加载中...</div>
+                      ) : formData.rule && formData.rule.length > 0 ? (
+                        <div>
+                          {(() => {
+                            const formConfig = formData.option?.formConfig || defaultFormConfig;
+                            const layoutColumns = formConfig.layoutColumns || 1;
+                            const gridStyle = layoutColumns > 1 ? {
+                              display: 'grid',
+                              gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
+                              gap: '12px',
+                              rowGap: '16px',
+                            } : undefined;
+                            
+                            return (
+                              <AntdForm
+                                form={detailForm}
+                                layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
+                                size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
+                                requiredMark={formConfig.hideRequiredMark ? false : undefined}
+                                labelCol={formConfig.labelWidth ? { 
+                                  style: { 
+                                    width: `${formConfig.labelWidth}px`,
+                                    textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
+                                  } 
+                                } : undefined}
+                              >
+                                <div 
+                                  style={gridStyle} 
+                                  className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}
+                                >
+                                  <style>{`
+                                    .form-detail-grid .ant-form-item {
+                                      margin-bottom: 12px;
+                                    }
+                                  `}</style>
+                                  {(formData.rule || []).map((field: any) => {
+                                    // 如果已通过,禁用所有字段
+                                    const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
+                                    return renderFieldPreview(fieldWithDisabled);
+                                  })}
+                                </div>
+                              </AntdForm>
+                            );
+                          })()}
+                        </div>
+                      ) : (
+                        <div className="py-4 text-center text-gray-400 text-sm">暂无表单内容</div>
+                      )}
+
+                      {/* 审核意见 - label 和 textarea 同一行 */}
+                      <div className="flex items-start" style={{ gap: '16px', backgroundColor: '#f5f5f5', padding: '12px 12px 22px 12px', borderRadius: '12px', border: '1px solid #e0e0e0', marginBottom: '12px' }}>
+                        <label 
+                          className="text-sm font-medium text-gray-700 whitespace-nowrap pt-2"
+                          style={{ 
+                            width: defaultFormConfig.labelWidth ? `${defaultFormConfig.labelWidth - 20}px` : '80px',
+                            textAlign: defaultFormConfig.labelPosition === 'left' ? 'left' : defaultFormConfig.labelPosition === 'right' ? 'right' : 'left',
+                            flexShrink: 0
+                          }}
+                        >
+                          审核意见
+                        </label>
+                        <div className="flex-1">
+                          <Input.TextArea
+                            rows={4}
+                            value={approvalComment}
+                            onChange={(e) => setApprovalComment(e.target.value)}
+                            placeholder="请输入审核意见"
+                            maxLength={500}
+                            showCount
+                            disabled={isApproved}
+                            readOnly={isApproved}
+                          />
+                        </div>
+                      </div>
+
+                      </div>
+                      {/* 底部按钮 */}
+                      <div 
+                        className="flex justify-end gap-3"
+                        style={{
+                          padding: '16px 24px',
+                          borderTop: '1px solid #f0f0f0',
+                          flexShrink: 0
+                        }}
+                      >
+                        <Button
+                          danger
+                          loading={approvalLoading}
+                          disabled={isApproved}
+                          onClick={async () => {
+                            const nodeId = detailData.nodeId || detailData.id;
+                            if (!nodeId) {
+                              message.error('节点ID不存在');
+                              return;
+                            }
+                            
+                            // 如果有自定义表单,先校验表单
+                            if (formData.rule && formData.rule.length > 0) {
+                              try {
+                                // 校验表单所有字段
+                                await detailForm.validateFields();
+                                console.log('TaskManagement: 表单校验通过');
+                              } catch (error: any) {
+                                // 校验失败,显示错误信息
+                                console.error('TaskManagement: 表单校验失败', error);
+                                if (error.errorFields && error.errorFields.length > 0) {
+                                  // 获取第一个错误字段的提示信息
+                                  const firstError = error.errorFields[0];
+                                  const errorMessage = firstError.errors?.[0] || '请完善表单内容';
+                                  message.error(errorMessage);
+                                } else {
+                                  message.error('请完善表单内容');
+                                }
+                                return; // 阻止提交
+                              }
+                            }
+                            
+                            setApprovalLoading(true);
+                            try {
+                              const params: UpdateNodeApprovalParam = {
+                                nodeId: nodeId,
+                                approvalStatus: 'rejected',
+                                approvalOpinion: approvalComment || undefined,
+                              };
+                              
+                              console.log('TaskManagement: 调用审核不通过接口', params);
+                              await taskManagementApi.updateNodeApproval(params);
+                              message.success('审核不通过操作成功');
+                              
+                              // 关闭弹框
+                              setDetailVisible(false);
+                              setDetailData(null);
+                              setFormData({ rule: [], option: {} });
+                              setFormLoading(false);
+                              setOriginalFields([]);
+                              setOriginalConf('');
+                              detailForm.resetFields();
+                              setApprovalComment('');
+                              
+                              // 刷新列表
+                              getList();
+                            } catch (error: any) {
+                              console.error('TaskManagement: 审核不通过失败', error);
+                              message.error(error?.message || '审核不通过操作失败');
+                            } finally {
+                              setApprovalLoading(false);
+                            }
+                          }}
+                        >
+                          审核不通过
+                        </Button>
+                        <Button
+                          type="primary"
+                          loading={approvalLoading}
+                          disabled={isApproved}
+                          onClick={async () => {
+                            const nodeId = detailData.nodeId || detailData.id;
+                            if (!nodeId) {
+                              message.error('节点ID不存在');
+                              return;
+                            }
+                            
+                            // 如果有自定义表单,先校验表单
+                            if (formData.rule && formData.rule.length > 0) {
+                              try {
+                                // 校验表单所有字段
+                                await detailForm.validateFields();
+                                console.log('TaskManagement: 表单校验通过');
+                              } catch (error: any) {
+                                // 校验失败,显示错误信息
+                                console.error('TaskManagement: 表单校验失败', error);
+                                if (error.errorFields && error.errorFields.length > 0) {
+                                  // 获取第一个错误字段的提示信息
+                                  const firstError = error.errorFields[0];
+                                  const errorMessage = firstError.errors?.[0] || '请完善表单内容';
+                                  message.error(errorMessage);
+                                } else {
+                                  message.error('请完善表单内容');
+                                }
+                                return; // 阻止提交
+                              }
+                            }
+                            
+                            setApprovalLoading(true);
+                            try {
+                              const params: UpdateNodeApprovalParam = {
+                                nodeId: nodeId,
+                                approvalStatus: 'approved',
+                                approvalOpinion: approvalComment || undefined,
+                              };
+                              
+                              console.log('TaskManagement: 调用审核通过接口', params);
+                              await taskManagementApi.updateNodeApproval(params);
+                              message.success('审核通过操作成功');
+                              
+                              // 关闭弹框
+                              setDetailVisible(false);
+                              setDetailData(null);
+                              setFormData({ rule: [], option: {} });
+                              setFormLoading(false);
+                              setOriginalFields([]);
+                              setOriginalConf('');
+                              detailForm.resetFields();
+                              setApprovalComment('');
+                              
+                              // 刷新列表
+                              getList();
+                            } catch (error: any) {
+                              console.error('TaskManagement: 审核通过失败', error);
+                              message.error(error?.message || '审核通过操作失败');
+                            } finally {
+                              setApprovalLoading(false);
+                            }
+                          }}
+                        >
+                          审核通过
+                        </Button>
+                      </div>
+                    </div>
+                  );
+                }
+
+                // 其他节点类型(需要根据 formId 获取表单)
+                return (
+                  <div style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
+                    <div className="space-y-6" style={{ padding: '0 24px', flex: 1, overflowY: 'auto', minHeight: 0 }}>
+                    {/* 自定义表单 */}
+                    {formLoading ? (
+                      <div className="py-8 text-center text-gray-500">表单加载中...</div>
+                    ) : formData.rule && formData.rule.length > 0 ? (
+                      <div>
+                        {(() => {
+                          const formConfig = formData.option?.formConfig || defaultFormConfig;
+                          const layoutColumns = formConfig.layoutColumns || 1;
+                          const gridStyle = layoutColumns > 1 ? {
+                            display: 'grid',
+                            gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
+                            gap: '12px',
+                            rowGap: '16px',
+                          } : undefined;
+                          
+                          return (
+                            <AntdForm
+                              form={detailForm}
+                              layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
+                              size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
+                              requiredMark={formConfig.hideRequiredMark ? false : undefined}
+                              labelCol={formConfig.labelWidth ? { 
+                                style: { 
+                                  width: `${formConfig.labelWidth}px`,
+                                  textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
+                                } 
+                              } : undefined}
+                            >
+                              <div 
+                                style={gridStyle} 
+                                className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}
+                              >
+                                <style>{`
+                                  .form-detail-grid .ant-form-item {
+                                    margin-bottom: 12px;
+                                  }
+                                `}</style>
+                                {(formData.rule || []).map((field: any) => {
+                                  // 如果已通过,禁用所有字段
+                                  const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
+                                  return renderFieldPreview(fieldWithDisabled);
+                                })}
+                              </div>
+                            </AntdForm>
+                          );
+                        })()}
+                      </div>
+                    ) : (
+                      <div className="py-4 text-center text-gray-400 text-sm">暂无表单内容</div>
+                    )}
+
+                    </div>
+                    {/* 底部按钮 */}
+                    <div 
+                      className="flex justify-end gap-3"
+                      style={{
+                        padding: '16px 24px',
+                        borderTop: '1px solid #f0f0f0',
+                        flexShrink: 0
+                      }}
+                    >
+                      <Button
+                        onClick={() => {
+                          setDetailVisible(false);
+                          setDetailData(null);
+                          setFormData({ rule: [], option: {} });
+                          setOriginalFields([]);
+                          setOriginalConf('');
+                          detailForm.resetFields();
+                        }}
+                      >
+                        取消
+                      </Button>
+                      <Button
+                        type="primary"
+                        loading={submitLoading}
+                        disabled={isApproved}
+                        onClick={async () => {
+                          try {
+                            // 验证表单
+                            const values = await detailForm.validateFields();
+                            
+                            // 获取节点ID
+                            const nodeId = detailData.nodeId || detailData.id;
+                            if (!nodeId) {
+                              message.error('节点ID不存在');
+                              return;
+                            }
+                            
+                            setSubmitLoading(true);
+                            
+                            // 将填写值添加到原始 fields 数组的每个 JSON 字符串中
+                            const fieldsWithValues = originalFields.map((fieldString: string) => {
+                              try {
+                                // 解析字段 JSON 字符串
+                                const fieldObj = JSON.parse(fieldString);
+                                
+                                // 获取字段名:优先使用 name(表单实际使用的字段名),其次使用 field
+                                const fieldName = fieldObj.name || fieldObj.field;
+                                
+                                // 获取填写值
+                                const fieldValue = values[fieldName];
+                                
+                                // 添加或更新 value 字段
+                                fieldObj.value = fieldValue !== undefined && fieldValue !== null ? fieldValue : '';
+                                
+                                // 转回 JSON 字符串
+                                return JSON.stringify(fieldObj);
+                              } catch (e) {
+                                console.error('TaskManagement: 解析字段 JSON 失败', e, fieldString);
+                                return fieldString; // 如果解析失败,返回原始字符串
+                              }
+                            });
+                            
+                            // 构建完整的表单数据对象
+                            const submitData = {
+                              id: detailData.formId || detailData.id,
+                              name: detailData.nodeName || '未知',
+                              conf: originalConf,
+                              fields: fieldsWithValues
+                            };
+                            
+                            // 将表单数据转换为 JSON 字符串
+                            const formDataString = JSON.stringify(submitData);
+                            
+                            // 调用接口
+                            const params: UpdateNodeApprovalParam = {
+                              nodeId: nodeId,
+                              approvalStatus: 'approved',
+                              approvalOpinion: undefined, // 非审核节点,不需要审批意见
+                              formData: formDataString
+                            };
+                            
+                            console.log('TaskManagement: 调用提交接口', params);
+                            await taskManagementApi.updateNodeApproval(params);
+                            
+                            // 根据节点类型显示不同的成功消息
+                            const nodeType = String(detailData?.type || detailData?.nodeType || '').trim();
+                            const isComplete = nodeType === 'complete';
+                            message.success(isComplete ? '完成成功' : '提交成功');
+                            
+                            // 关闭弹框
+                            setDetailVisible(false);
+                            setDetailData(null);
+                            setFormData({ rule: [], option: {} });
+                            setFormLoading(false);
+                            setOriginalFields([]);
+                            setOriginalConf('');
+                            detailForm.resetFields();
+                            
+                            // 刷新列表
+                            getList();
+                          } catch (error: any) {
+                            console.error('TaskManagement: 提交失败', error);
+                            if (error?.errorFields) {
+                              // 表单验证失败
+                              message.error('请填写完整的表单内容');
+                            } else {
+                              message.error(error?.message || '提交失败');
+                            }
+                          } finally {
+                            setSubmitLoading(false);
+                          }
+                        }}
+                      >
+                        {(() => {
+                          // 从 detailData.type 或 detailData.nodeType 获取节点类型
+                          const nodeType = String(detailData?.type || detailData?.nodeType || '').trim();
+                          const isComplete = nodeType === 'complete';
+                          return isComplete ? '完成' : '提交';
+                        })()}
+                      </Button>
+                    </div>
+                  </div>
+                );
+              })()}
+            </div>
+          </div>
+            );
+          }
+          return <div className="py-8 text-center text-gray-500">暂无数据</div>;
+        })()}
+      </Modal>
+    </div>
+  );
+}

+ 10 - 0
src/utils/permission.ts

@@ -255,6 +255,16 @@ export const mapMenuPathToKey = (path: string): string | null => {
     return 'dashboard';
   }
   
+  // 任务管理相关
+  if (path.includes('/task-management') || path.includes('/taskManagement') || path.includes('/admin-work') || path.includes('/adminWork')) {
+    return 'taskManagement';
+  }
+  
+  // 我的任务相关
+  if (path.includes('/my-task') || path.includes('/myTask') || path.includes('/my-work')) {
+    return 'myTask';
+  }
+  
   return null;
 };
 

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików