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, Image, Avatar } from 'antd'; import { UploadOutlined, LockOutlined, KeyOutlined, WarningOutlined, CheckCircleOutlined, BarcodeOutlined, SendOutlined, EnvironmentOutlined, CloseCircleOutlined } 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 { fileApi } from '../api/file'; import { dateFormatter } from '../utils/formatTime'; import { DICT_TYPE, getDictLabel } from '../utils/dict'; import { setConfAndFields2, FormCreateData } from '../utils/formCreate'; import urgecy1Icon from '../assets/urgecy1.png'; import urgecy2Icon from '../assets/urgecy2.png'; import urgecy3Icon from '../assets/urgecy3.png'; import { useTranslation } from 'react-i18next'; import FormUploadField from './FormUploadField'; // 辅助函数:安全地将值转换为 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 { t } = useTranslation(); const navigate = useNavigate(); const [loading, setLoading] = useState(true); const [list, setList] = useState([]); const [total, setTotal] = useState(0); // 从 sessionStorage 读取 status 参数 const getInitialStatus = () => { const status = sessionStorage.getItem('taskManagementStatus'); return status || undefined; }; const [queryParams, setQueryParams] = useState({ pageNo: 1, pageSize: 10, key: undefined, status: getInitialStatus(), }); const [searchOrderNo, setSearchOrderNo] = useState(''); const [searchName, setSearchName] = useState(''); const [approvalStatusDictList, setApprovalStatusDictList] = useState([]); const [urgencyLevelDictList, setUrgencyLevelDictList] = useState([]); const [jobCategoryDictList, setJobCategoryDictList] = useState([]); // 作业分类 work_type // 节点详情弹框相关状态 const [detailVisible, setDetailVisible] = useState(false); const [detailLoading, setDetailLoading] = useState(false); const [detailData, setDetailData] = useState(null); const [formData, setFormData] = useState({ rule: [], option: {} }); const [formLoading, setFormLoading] = useState(false); // 表单加载状态 const [originalFields, setOriginalFields] = useState([]); // 保存原始的 fields 数组(JSON 字符串数组) const [originalConf, setOriginalConf] = useState(''); // 保存原始的 conf(JSON 字符串) const [detailForm] = AntdForm.useForm(); const [approvalComment, setApprovalComment] = useState(''); // 审核意见 const [approvalLoading, setApprovalLoading] = useState(false); // 审核操作loading状态 const [submitLoading, setSubmitLoading] = useState(false); // 提交操作loading状态 // 隔离方式字典(isolation_method:0=盲板,1=上锁挂牌,2=拆除) const [isolationMethodDictList, setIsolationMethodDictList] = useState([]); // 盲板/拆除类型时的设备编号与附件(仅隔离/方案、解除隔离且隔离方式为盲板或拆除时使用) const [isolationDeviceNumber, setIsolationDeviceNumber] = useState(''); const [isolationFileList, setIsolationFileList] = useState([]); const [isolationSubmitLoading, setIsolationSubmitLoading] = useState(false); // 组件挂载时打印调试信息,并从 sessionStorage 读取 status useEffect(() => { console.log('TaskManagement 组件已加载'); const status = sessionStorage.getItem('taskManagementStatus'); if (status) { console.log('从 sessionStorage 读取到 status:', status); setQueryParams(prev => ({ ...prev, status: status, pageNo: 1, // 重置到第一页 })); // 读取后清除 sessionStorage,避免下次进入时自动应用 sessionStorage.removeItem('taskManagementStatus'); } }, []); // 监听弹框状态变化 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); } }; // 获取紧急程度字典 const getUrgencyLevelDictList = async () => { try { const { dictDataApi } = await import('../api/DictData'); const response = await dictDataApi.getDictDataPage({ pageNo: 1, pageSize: -1, dictType: 'urgency_level', }); const data = (response as any)?.data || response; const dictList = data?.list || []; setUrgencyLevelDictList(dictList); console.log('TaskManagement: 获取紧急程度字典成功', dictList); } catch (error: any) { console.error('获取紧急程度字典失败:', error); } }; // 获取隔离方式字典(isolation_method) const getIsolationMethodDictList = async () => { try { const { dictDataApi } = await import('../api/DictData'); const response = await dictDataApi.getDictDataPage({ pageNo: 1, pageSize: -1, dictType: 'isolation_method', }); const data = (response as any)?.data || response; const dictList = data?.list || []; setIsolationMethodDictList(dictList); } catch (error: any) { console.error('获取隔离方式字典失败:', error); } }; // 获取作业分类字典(work_type) const getJobCategoryDictList = async () => { try { const { dictDataApi } = await import('../api/DictData'); const response = await dictDataApi.getDictDataPage({ pageNo: 1, pageSize: -1, dictType: 'work_type', }); const data = (response as any)?.data || response; setJobCategoryDictList(data?.list || []); } catch (error: any) { console.error('获取作业分类字典失败:', error); setJobCategoryDictList([]); } }; useEffect(() => { getApprovalStatusDictList(); getUrgencyLevelDictList(); getIsolationMethodDictList(); getJobCategoryDictList(); }, []); /** 查询列表 */ 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); console.log('TaskManagement: 解析后的数据', pageData); setList(pageData.list || []); setTotal(pageData.total || 0); } catch (error: any) { console.error('TaskManagement: 获取任务管理列表失败', error); toast.error(error.message || t('form.fetchTaskListFailed')); 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, queryParams.status]); // 搜索:两个条件都通过 key 传参,值为具体输入(多个用空格拼接) const handleSearch = () => { const parts = [searchOrderNo.trim(), searchName.trim()].filter(Boolean); setQueryParams({ ...queryParams, pageNo: 1, key: parts.length ? parts.join(' ') : undefined, }); }; // 重置 const handleReset = () => { setSearchOrderNo(''); setSearchName(''); setQueryParams({ ...queryParams, pageNo: 1, key: undefined, status: undefined, // 重置时也清除 status }); }; // 默认表单配置 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 (
{children.map((child: any) => renderFieldPreview(child))}
); } if (field.type === 'grid') { const gridColumns = field.gridColumns || 2; const children = field.children || []; return (
{children.map((child: any) => { const childSpanStyle = gridColumns > 1 ? { gridColumn: `span ${Math.min(gridColumns, child.span || 1)}` } : undefined; return (
{renderFieldPreview(child)}
); })}
); } // 处理 alert 类型 if (field.type === 'alert') { const themeClass = field.alertTheme === 'dark' ? 'bg-gray-800 text-white border-gray-700' : ''; return (
{field.hint &&
{field.hint}
}
); } // 与表单设计器一致:每字段 labelCol/wrapperCol const getItemLayout = (f: any) => { const isTop = formConfig.labelPosition === 'top'; const w = f.labelWidth ?? formConfig.labelWidth ?? 100; const effective = (typeof w === 'number' && w > 0) ? w : 100; return { labelCol: isTop ? undefined : { flex: `${effective}px`, style: { minWidth: `${effective}px`, textAlign: formConfig.labelPosition === 'right' ? 'right' : 'left' } }, wrapperCol: isTop ? undefined : { flex: 'auto', style: { minWidth: 0 } }, }; }; const itemLayout = getItemLayout(field); // 处理普通字段 switch (field.type) { case 'textarea': return (
); case 'password': return (
); case 'number': return (
); case 'select': return (
); case 'date': return (
{ // 选择日期时,将 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; }} >
); case 'daterange': return (
{ // 选择日期范围时,将 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; }} >
); case 'datetime': return (
{ // 选择日期时间时,将 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; }} >
); case 'switch': return (
); case 'radio': return (
{(field.options || []).map((opt: any, idx: number) => ( {opt.label} ))}
); case 'checkbox': return (
{(field.options || []).map((opt: any, idx: number) => ( {opt.label} ))}
); case 'cascader': return (
); case 'upload': return (
); case 'time': case 'timepicker': return (
); default: // 检查 inputType 是否为时间类型 const inputType = field.inputType || field.type; if (inputType === 'date' || inputType === 'datetime' || inputType === 'time' || inputType === 'timepicker') { // 如果是时间类型,使用 DatePicker if (inputType === 'datetime') { return (
); } else if (inputType === 'time' || inputType === 'timepicker') { return (
); } else { // date 类型 return (
); } } // 其他类型使用 Input return (
); } }; // 查看节点详情(节点详情弹框) 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: data?.initiatorName || data?.initiator || record.initiatorName || 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 isolationType = String(detailDataWithWorkInfo?.isolationType ?? '').trim(); const isBlindOrDismantle = isolationType === '0' || isolationType === '2'; // 0=盲板 2=拆除 // 检查任务状态是否为"已通过" 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, }); // 当前节点无表单(无 formId 且不会从 formData 解析出表单)时清空表单 state,避免完成/结束节点误展示上一节点的自定义表单 if (!hasFormId && !(isApproved && hasFormData)) { setFormData({ rule: [], option: {} }); setOriginalFields([]); setOriginalConf(''); } // 打开盲板/拆除详情时先清空设备编号和附件,后续若有 formData 再回填 if (isIsolation || isReleaseIsolation) { const isolationType = String(detailDataWithWorkInfo.isolationType ?? '').trim(); if (isolationType === '0' || isolationType === '2') { setIsolationDeviceNumber(''); setIsolationFileList([]); } } // 如果任务状态为"已通过"且有 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); // 有 conf/fields 时也回显 deviceNumber、attachments(解除隔离/隔离方案完成查看详情);attachments 可能是 JSON 字符串 const normAttachments = (att: any): any[] => { if (att == null) return []; if (Array.isArray(att)) return att; if (typeof att === 'string' && att.trim()) { try { const p = JSON.parse(att); return Array.isArray(p) ? p : []; } catch { return []; } } return []; }; const listInConf = normAttachments(parsedFormData.attachments).length ? normAttachments(parsedFormData.attachments) : normAttachments((detailDataWithWorkInfo as any).attachments); const deviceNumInConf = parsedFormData.deviceNumber ?? (detailDataWithWorkInfo as any).deviceNumber; const hasDeviceInConf = deviceNumInConf !== undefined && deviceNumInConf !== null; if (hasDeviceInConf || listInConf.length > 0) { setIsolationDeviceNumber(deviceNumInConf ?? ''); setIsolationFileList( listInConf.map((item: any, idx: number) => ({ uid: `echo-${idx}-${item.url || item.name || idx}`, name: item.name || `文件${idx + 1}`, url: item.url || item.response, status: 'done', response: item.url || item.response, })) ); console.log('TaskManagement: 盲板/拆除 deviceNumber/attachments 已回显', { deviceNumber: parsedFormData.deviceNumber, attachmentsCount: listInConf.length }); } } else if ( parsedFormData.deviceNumber !== undefined || (() => { const a = parsedFormData.attachments; if (a == null) return false; if (Array.isArray(a)) return a.length > 0; if (typeof a === 'string' && a.trim()) { try { const p = JSON.parse(a); return Array.isArray(p) && p.length > 0; } catch { return false; } } return false; })() ) { // 盲板/拆除 仅 deviceNumber/attachments 无 conf/fields 时的回显 const listElse = (() => { const a = parsedFormData.attachments; if (a == null) return []; if (Array.isArray(a)) return a; if (typeof a === 'string' && a.trim()) { try { const p = JSON.parse(a); return Array.isArray(p) ? p : []; } catch { return []; } } return []; })(); setIsolationDeviceNumber(parsedFormData.deviceNumber ?? ''); setIsolationFileList( listElse.map((item: any, idx: number) => ({ uid: `echo-${idx}-${item.url || item.name || idx}`, name: item.name || `文件${idx + 1}`, url: item.url || item.response, status: 'done', response: item.url || item.response, })) ); console.log('TaskManagement: 盲板/拆除 formData 已回显', { deviceNumber: parsedFormData.deviceNumber, attachmentsCount: listElse.length }); } else { // 完成/结束等节点可能无表单内容,不提示“表单数据不完整”,有则显示、无则不显示即可 console.warn('TaskManagement: formData 中缺少 conf 或 fields', { conf: !!conf, fields: !!fields }); } } catch (e) { console.error('TaskManagement: 解析 formData 失败', e); message.error('解析表单数据失败: ' + (e instanceof Error ? e.message : String(e))); } finally { setFormLoading(false); } } // 有 formId 时从接口获取表单:① 审核等非隔离节点 ② 盲板/拆除节点(隔离或解除隔离且隔离方式为盲板或拆除) else if (hasFormId && ( (!isIsolation && !isReleaseIsolation && !isReturnLock) || ((isIsolation || isReleaseIsolation) && isBlindOrDismantle) )) { console.log('TaskManagement: ✅ 满足条件,开始获取表单', { formId, nodeType, isBlindOrDismantle }); // 重置表单数据 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 }); // 仅任务已完成(approved)时回显表单数据;进行中一律不回显并清空表单 const shouldEchoFormData = detailDataWithWorkInfo.formData && isApproved; if (shouldEchoFormData) { 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 if (!isApproved) { // 任务进行中:清空自定义表单,避免带出历史或隔离方案等填写内容 setTimeout(() => { try { detailForm.resetFields(); } catch (_) { /* ignore */ } }, 100); } // 隔离/解除隔离完成查看详情:从 formData 或节点详情顶层回显 deviceNumber、attachments(部分解除隔离节点仅顶层有数据) if (isApproved && (isIsolation || isReleaseIsolation)) { try { const normAtt = (att: any): any[] => { if (att == null) return []; if (Array.isArray(att)) return att; if (typeof att === 'string' && att.trim()) { try { const p = JSON.parse(att); return Array.isArray(p) ? p : []; } catch { return []; } } return []; }; let deviceNum: any; let listF: any[]; if (detailDataWithWorkInfo.formData) { const parsed = typeof detailDataWithWorkInfo.formData === 'string' ? JSON.parse(detailDataWithWorkInfo.formData) : detailDataWithWorkInfo.formData; listF = normAtt(parsed.attachments).length ? normAtt(parsed.attachments) : normAtt((detailDataWithWorkInfo as any).attachments); deviceNum = parsed.deviceNumber ?? (detailDataWithWorkInfo as any).deviceNumber; } else { deviceNum = (detailDataWithWorkInfo as any).deviceNumber; listF = normAtt((detailDataWithWorkInfo as any).attachments); } const hasDev = deviceNum !== undefined && deviceNum !== null; if (hasDev || listF.length > 0) { setIsolationDeviceNumber(deviceNum ?? ''); setIsolationFileList( listF.map((item: any, idx: number) => ({ uid: `echo-${idx}-${item.url || item.name || idx}`, name: item.name || `文件${idx + 1}`, url: item.url || item.response, status: 'done', response: item.url || item.response, })) ); console.log('TaskManagement: formId 分支 deviceNumber/attachments 已回显', { deviceNumber: deviceNum, attachmentsCount: listF.length }); } } catch (_) { /* ignore */ } } } 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); // 隔离/解除隔离已完成但未走 formData 或 formId 分支时,从节点详情顶层回显 deviceNumber、attachments if (isApproved && (isIsolation || isReleaseIsolation)) { try { const normAtt = (att: any): any[] => { if (att == null) return []; if (Array.isArray(att)) return att; if (typeof att === 'string' && att.trim()) { try { const p = JSON.parse(att); return Array.isArray(p) ? p : []; } catch { return []; } } return []; }; const deviceNum = (detailDataWithWorkInfo as any).deviceNumber; const listF = normAtt((detailDataWithWorkInfo as any).attachments); if ((deviceNum !== undefined && deviceNum !== null) || listF.length > 0) { setIsolationDeviceNumber(deviceNum ?? ''); setIsolationFileList( listF.map((item: any, idx: number) => ({ uid: `echo-${idx}-${item.url || item.name || idx}`, name: item.name || `文件${idx + 1}`, url: item.url || item.response, status: 'done', response: item.url || item.response, })) ); console.log('TaskManagement: 隔离/解除隔离(无formId分支) deviceNumber/attachments 已从顶层回显', { deviceNumber: deviceNum, attachmentsCount: listF.length }); } } catch (_) { /* ignore */ } } 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 = { 'pending': '待审核', 'approved': '已通过', 'rejected': '已驳回', 'unaudited': '未审核', }; return statusMap[statusStr] || '未知'; }; // 获取任务状态样式(审批状态) const getTaskStatusStyle = (status: string | number | undefined): React.CSSProperties => { if (!status) { return { backgroundColor: '#e5e5e5', color: '#333333', }; } const statusStr = String(status).toLowerCase(); // 先获取状态文本,根据文本判断颜色 const statusText = getTaskStatusText(status); const statusTextLower = statusText.toLowerCase(); // 根据状态文本判断颜色 // 待执行、待发布:灰色 #e5e5e5 if (statusTextLower.includes('待执行') || statusTextLower.includes('待发布') || statusTextLower.includes('未开始') || statusTextLower.includes('未审核') || statusTextLower.includes('待开始') || statusTextLower.includes('unaudited')) { return { backgroundColor: '#e5e5e5', color: '#333333', }; } // 进行中:蓝色 #1677ff if (statusTextLower.includes('进行中') || statusTextLower.includes('待审核') || statusTextLower.includes('pending') || statusTextLower.includes('执行中')) { return { backgroundColor: '#1677ff', color: '#ffffff', }; } // 已完成:深绿色 #15803d if (statusTextLower.includes('已完成') || statusTextLower.includes('已通过') || statusTextLower.includes('approved') || statusTextLower.includes('完成') || statusTextLower.includes('执行完成')) { return { backgroundColor: '#15803d', color: '#ffffff', }; } // 如果没有匹配到,默认灰色 return { backgroundColor: '#e5e5e5', color: '#333333', }; }; // 作业状态文案(workDetail.status:作业整体状态,与任务状态 approvalStatus/nodeProgress 不同) const getWorkStatusText = (status: string | number | undefined): string => { if (status == null || String(status).trim() === '') return t('workJobDetail.statusUnknown'); const s = String(status).toLowerCase(); // 后端可能直接返回「执行完成」「已完成」等中文文案,这里统一映射为「已完成」 if (s.includes('执行完成') || s.includes('已完成') || s === 'completed') { return '已完成'; } const map: Record = { pending: t('workJobDetail.statusPending'), running: t('workJobDetail.statusRunning'), completed: t('workJobDetail.statusCompleted'), rejected: t('workJobDetail.statusRejected'), skipped: t('workJobDetail.statusSkipped'), cancelled: t('workJobDetail.statusCancelled'), }; return map[s] ?? String(status) ?? t('workJobDetail.statusUnknown'); }; const getWorkStatusStyle = (status: string | number | undefined): React.CSSProperties => { const s = String(status || '').toLowerCase(); if (['running', '2'].includes(s)) return { backgroundColor: '#1677ff', color: '#fff' }; if (['completed', 'approved'].includes(s)) return { backgroundColor: '#15803d', color: '#fff' }; if (['rejected', 'cancelled'].includes(s)) return { backgroundColor: '#ff4d4f', color: '#fff' }; if (['pending', '1'].includes(s)) return { backgroundColor: '#e5e5e5', color: '#333' }; return { backgroundColor: '#e2e8f0', color: '#64748b' }; }; // 获取紧急程度样式 // 获取紧急程度图标和样式(根据字典 value 判断:0=一般,1=紧急,2=非常紧急) const getUrgencyLevelIconAndStyle = (urgencyValue: string | number | undefined): { icon: React.ReactNode; style: React.CSSProperties } => { if (urgencyValue === null || urgencyValue === undefined || urgencyValue === '') { return { icon: null, style: { backgroundColor: '#e5e5e5', color: '#333333', }, }; } const value = Number(urgencyValue); // 0 = 一般:使用 urgecy1.png 图标 + 黑色文字 if (value === 0) { return { icon: ( 一般 ), style: { backgroundColor: 'transparent', color: '#000000', }, }; } // 1 = 紧急:使用 urgecy2.png 图标 + 橙色加粗文字 if (value === 1) { return { icon: ( 紧急 ), style: { backgroundColor: 'transparent', color: '#fa8c16', fontWeight: 'bold', }, }; } // 2 = 非常紧急:使用 urgecy3.png 图标 + 红色加粗文字 if (value === 2) { return { icon: ( 非常紧急 ), style: { backgroundColor: 'transparent', color: '#ff4d4f', fontWeight: 'bold', }, }; } // 如果没有匹配到,默认灰色 return { icon: null, style: { backgroundColor: '#e5e5e5', color: '#333333', }, }; }; // 获取紧急程度样式(保留用于向后兼容) const getUrgencyLevelStyle = (urgencyValue: string | number | undefined): React.CSSProperties => { return getUrgencyLevelIconAndStyle(urgencyValue).style; }; // 表格列配置 const columns: ColumnsType = [ { title: t('form.taskId'), dataIndex: 'orderNo', width: 180, align: 'center', render: (text: string) => text || '-', }, { title: t('form.taskName'), dataIndex: 'name', width: 220, align: 'center', ellipsis: true, render: (text: string) => text || '-', }, { title: t('form.currentTask'), dataIndex: 'currentNodeName', width: 180, align: 'center', ellipsis: true, render: (text: string, record: MyTaskVO) => { const displayText = text || record.currentNode || '-'; if (!displayText || displayText === '-') return displayText; return ( { e.currentTarget.style.textDecoration = 'underline'; }} onMouseLeave={(e) => { e.currentTarget.style.textDecoration = 'none'; }} onClick={(e) => { e.stopPropagation(); e.preventDefault(); handleViewDetail(record); }} title={displayText} > {displayText} ); }, }, { title: t('form.taskStatus'), dataIndex: 'approvalStatus', width: 120, align: 'center', render: (status: string | number | undefined, record: MyTaskVO) => { // 优先使用 approvalStatus,如果没有则使用其他状态字段 const taskStatus = status || record.taskStatus || record.status; // 列表里“已完成/已通过”使用浅绿,弹框/详情仍保持深绿 const baseStyle = getTaskStatusStyle(taskStatus); const statusTextLower = String(getTaskStatusText(taskStatus) || '').toLowerCase(); const listStyle = statusTextLower.includes('已完成') || statusTextLower.includes('已通过') || statusTextLower.includes('approved') || statusTextLower.includes('完成') || statusTextLower.includes('执行完成') ? { ...baseStyle, backgroundColor: '#0acb57', color: '#ffffff' } : baseStyle; return ( {getTaskStatusText(taskStatus)} ); }, }, { title: t('form.responsiblePerson'), dataIndex: 'workerUserName', width: 150, align: 'center', render: (text: string, record: MyTaskVO) => { // 优先使用 workerUserName,如果没有则尝试从其他字段获取 return text || record.responsibleName || record.responsible || record.initiatorName || record.initiator || '-'; }, }, { title: t('form.taskStartTime'), 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: t('form.urgencyLevel'), dataIndex: 'urgencyLevel', width: 120, align: 'center', render: (urgencyLevel: string | number | undefined, record: MyTaskVO) => { const urgencyValue = urgencyLevel || record.urgencyLevel; const urgencyItem = urgencyLevelDictList.find(item => String(item.value) === String(urgencyValue)); const urgencyText = urgencyItem ? (urgencyItem.label || '') : (urgencyValue ? String(urgencyValue) : '-'); if (!urgencyValue || urgencyValue === null || urgencyValue === undefined || urgencyValue === '') { return -; } const { icon, style } = getUrgencyLevelIconAndStyle(urgencyValue); return ( {icon} {urgencyText} ); }, }, { title: t('common.operation'), width: 120, align: 'center', fixed: 'right', render: (_: any, record: MyTaskVO) => { // 获取任务状态文本 const statusText = getTaskStatusText(record.approvalStatus); const statusTextLower = statusText.toLowerCase(); // 判断是否为进行中状态 const isInProgress = statusTextLower.includes('进行中') || statusTextLower.includes('待审核') || statusTextLower.includes('pending') || statusTextLower.includes('执行中'); // 判断是否为已完成状态 const isCompleted = statusTextLower.includes('已完成') || statusTextLower.includes('已通过') || statusTextLower.includes('approved') || statusTextLower.includes('完成') || statusTextLower.includes('执行完成'); // 根据状态显示不同的文字 const buttonText = isInProgress ? t('form.handleNow') : isCompleted ? t('form.viewDetail') : t('form.viewDetail'); return ( ); }, }, ]; // 计算分页数据 const totalPages = Math.ceil(total / queryParams.pageSize) || 1; return (
{/* 操作栏和表格容器 */}
{/* 查询与操作栏 */}
{/* 搜索条件:放宽到 280px,可缩小但不挤压不可见 */}
{t('form.workOrderNo')}: setSearchOrderNo(e.target.value)} placeholder={t('form.workOrderNoPlaceholder')} allowClear className="min-w-0 w-full" onPressEnter={handleSearch} />
{t('form.workName')}: setSearchName(e.target.value)} placeholder={t('form.workNamePlaceholder')} allowClear className="min-w-0 w-full" onPressEnter={handleSearch} />
{/* 表格容器:不设 overflow,避免出现第二层纵向滚动条,仅用 Table 的 scroll.x 做横向滚动 */}
record.id || Math.random()} pagination={false} scroll={{ x: 1200 }} locale={{ emptyText: t('form.noData'), }} />
{/* 分页 */} {total > 0 && (
{t('form.totalRecords')} {total} {t('form.recordsUnit')}
{queryParams.pageNo} / {totalPages}
)} {/* 详情弹框 */} { console.log('TaskManagement: 弹框关闭'); setDetailVisible(false); setDetailData(null); setFormData({ rule: [], option: {} }); setFormLoading(false); setOriginalFields([]); setOriginalConf(''); detailForm.resetFields(); setApprovalComment(''); setIsolationDeviceNumber(''); setIsolationFileList([]); }} 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
{t('form.loading')}
; } if (detailData) { return (
{/* 标题区域:左上角显示作业状态(有 workDetail 时),否则显示任务状态 */}

{detailData.workName || detailData.name || t('form.workDetail')}

{(() => { const workStatus = (detailData as any).workDetail?.status; if (workStatus != null && String(workStatus).trim() !== '') { const baseStyle = getWorkStatusStyle(workStatus); const workText = getWorkStatusText(workStatus); const isCompleted = String(workText || '').toLowerCase().includes('已完成'); const headerStyle = isCompleted ? { ...baseStyle, backgroundColor: '#0acb57', color: '#ffffff' } : baseStyle; return ( {workText} ); } return ( { const s = detailData.approvalStatus ?? detailData.taskStatus ?? (detailData as any).nodeProgress; const baseStyle = getTaskStatusStyle(s); const textLower = String(getTaskStatusText(s) || '').toLowerCase(); const isCompleted = textLower.includes('已完成') || textLower.includes('已通过') || textLower.includes('approved') || textLower.includes('完成') || textLower.includes('执行完成'); return isCompleted ? { ...baseStyle, backgroundColor: '#0acb57', color: '#ffffff' } : baseStyle; })()} > {getTaskStatusText(detailData.approvalStatus ?? detailData.taskStatus ?? (detailData as any).nodeProgress)} ); })()}
{t('form.workOrderNo')}:{detailData.orderNo || '-'} {t('form.workResponsible')}:{detailData.initiatorName || '-'} {t('form.taskResponsible')}:{detailData.workerUserName || '-'} {t('form.initiationTime')}:{(detailData as any).workDetail?.initiationTime != null ? dateFormatter((detailData as any).workDetail.initiationTime) : (detailData.initiationTime != null ? dateFormatter(detailData.initiationTime) : (detailData.workTime ? dateFormatter(detailData.workTime) : '-'))}
{/* 内容区域 - 可滚动 */}
{(() => { // 检查节点状态是否为已通过(approved),如果是则禁用所有输入和按钮 const isApproved = detailData?.approvalStatus === 'approved'; // 从 detailData.type 或 detailData.nodeType 获取节点类型 const nodeType = String(detailData?.type || detailData?.nodeType || '').trim(); console.log('TaskManagement: 渲染内容区域 - 节点类型', nodeType, 'detailData:', detailData); // 创建作业类型节点:展示作业详情(作业类型、作业名称、作业发起人、作业分类、作业内容等) if (nodeType === 'createJob') { const wd = (detailData as any).workDetail || {}; const getLabelFromList = (list: any[], val: any) => list?.find((d: any) => String(d.value) === String(val))?.label ?? val; const taskStartTime = (detailData as any).startTime ?? (detailData as any).createTime ?? detailData.workTime; const taskStatus = detailData.approvalStatus ?? (detailData as any).taskStatus ?? (detailData as any).nodeProgress; const jobCategoryVal = (wd as any).workCategory ?? (wd as any).jobCategory ?? wd.type; const jobCategoryLabel = jobCategoryVal != null ? (getLabelFromList(jobCategoryDictList, jobCategoryVal) || jobCategoryVal) : '-'; return (
{/* 任务概览卡片 */}
任务名称:{detailData?.nodeName ?? '-'} { const baseStyle = getTaskStatusStyle(taskStatus); const textLower = String(getTaskStatusText(taskStatus) || '').toLowerCase(); const isCompleted = textLower.includes('已完成') || textLower.includes('已通过') || textLower.includes('approved') || textLower.includes('完成') || textLower.includes('执行完成'); const badgeStyle = isCompleted ? { ...baseStyle, backgroundColor: '#0acb57', color: '#ffffff', marginLeft: 10 } : { ...baseStyle, marginLeft: 10 }; return badgeStyle; })()} > {getTaskStatusText(taskStatus)} 负责人:{detailData?.workerUserName ?? '-'} 开始时间:{taskStartTime != null ? dateFormatter(taskStartTime) : '-'}
{/* 作业属性卡片 */}
作业名称
{wd.name ?? detailData.workName ?? detailData.name ?? '-'}
作业分类
{jobCategoryLabel}
{t('workJobDetail.workflowTemplate') || '流程模板'}
{(wd as any).designName ?? '-'}
{t('workJobDetail.urgencyLevel') || '紧急程度'}
{(wd as any).urgencyLevel != null ? (getLabelFromList(urgencyLevelDictList, (wd as any).urgencyLevel) || (wd as any).urgencyLevel) : '-'}
{/* 作业内容卡片 */}
{t('workJobDetail.jobContent') || '作业内容'}
{(wd.description != null && String(wd.description).trim() !== '') ? String(wd.description).trim() : '-'}
); } 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) }); // 当前节点是否有表单:无 formId 的节点(如完成/结束)不应渲染自定义表单 const hasFormIdForCurrentNode = detailData?.formId !== undefined && detailData?.formId !== null && ( typeof detailData.formId === 'number' || (typeof detailData.formId === 'string' && String(detailData.formId).trim() !== '') ); // 隔离/方案节点、解除隔离节点和还锁节点 if (isIsolation || isReleaseIsolation || isReturnLock) { const isolationType = String(detailData?.isolationType ?? '').trim(); const isLockoutTagout = isolationType === '1'; // 上锁挂牌 const isBlindPlate = isolationType === '0'; // 盲板 const isDismantle = isolationType === '2'; // 拆除 const showLockCabinet = isReturnLock || (isLockoutTagout && (isIsolation || isReleaseIsolation)); const showDeviceForm = (isIsolation || isReleaseIsolation) && (isBlindPlate || isDismantle); if (showDeviceForm) { const deviceLabel = isReleaseIsolation ? (isBlindPlate ? t('form.releaseBlindPlateDeviceNo') : t('form.restoreDeviceNo')) : (isBlindPlate ? t('form.blindPlateDeviceNo') : t('form.dismantleDeviceNo')); const formIdVal = detailData?.formId; const hasFormIdForBlindDismantle = formIdVal !== undefined && formIdVal !== null && ( typeof formIdVal === 'number' || (typeof formIdVal === 'string' && String(formIdVal).trim() !== '') ); return (
!isApproved && setIsolationDeviceNumber(e.target.value)} placeholder={`请输入${deviceLabel}`} maxLength={100} size="large" disabled={isApproved} prefix={} style={{ borderRadius: 10, borderColor: '#d9e8ff', fontSize: 15 }} />
{isApproved && isolationFileList.length > 0 ? (
{isolationFileList.filter((f: any) => { const u = f.url || f.response; return /\.(jpg|jpeg|png|gif|webp|bmp)(\?|$)/i.test(String(u || '')) || /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(String(f.name || '')); }).map((file: any) => ( {file.name} ))} {isolationFileList.filter((f: any) => { const u = f.url || f.response; return !/\.(jpg|jpeg|png|gif|webp|bmp)(\?|$)/i.test(String(u || '')) && !/\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(String(f.name || '')); }).map((file: any) => ( {file.name} ))}
) : isApproved ? (
暂无附件
) : ( setIsolationFileList(fileList.slice(-5))} multiple showUploadList={{ showRemoveIcon: true }} customRequest={async ({ file, onSuccess, onError }) => { try { const url = await fileApi.upload(file as File); onSuccess?.(url); } catch (e: any) { onError?.(e); message.error(e?.message || '文件上传失败'); } }} style={{ borderRadius: 12, padding: '32px 20px', background: 'linear-gradient(180deg, #f5faff 0%, #e8f4ff 50%, #e0efff 100%)', border: '2px dashed #91caff', }} >

点击或拖拽文件到此区域上传

最多 5 个文件,支持多选

)}
{/* 盲板/拆除有 formId 时在设备编号和附件上传下方渲染自定义表单,可滚动查看 */} {hasFormIdForBlindDismantle && (
{formLoading ? (
{t('form.formLoading')}
) : formData.rule && formData.rule.length > 0 ? ( (() => { 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 (
{(formData.rule || []).map((field: any) => { const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field; return renderFieldPreview(fieldWithDisabled); })}
); })() ) : null}
)}
); } console.log('TaskManagement: ✅ 进入隔离节点渲染分支', { isIsolation, isReleaseIsolation, isReturnLock, nodeType, isolationType }); // 隔离/方案(上锁挂牌)节点:三步并列分栏展示 if (isIsolation && isLockoutTagout) { const points: any[] = Array.isArray((detailData as any)?.points) ? (detailData as any).points : []; const keysList: any[] = Array.isArray((detailData as any)?.keys) ? (detailData as any).keys : []; const jtLockerUsers: any[] = Array.isArray((detailData as any)?.nodeUserList) ? ((detailData as any).nodeUserList || []).filter((u: any) => String(u?.type || '').toLowerCase() === 'jtlocker') : []; const jtCoLockerUsers: any[] = Array.isArray((detailData as any)?.nodeUserList) ? ((detailData as any).nodeUserList || []).filter((u: any) => String(u?.type || '').toLowerCase() === 'jtcolocker') : []; const lockPersons: any[] = jtLockerUsers; const getPointName = (p: any) => p?.pointName ?? p?.name ?? p?.pointLabel ?? p?.pointCode ?? (p?.id != null ? `点位${p.id}` : '-'); const isLocked = (p: any) => Boolean(p?.lockTime) || p?.locked === true || String(p?.status || '').toLowerCase() === 'locked'; const lockBadgeStyle = (locked: boolean): React.CSSProperties => locked ? { backgroundColor: '#15803d', color: '#ffffff' } : { backgroundColor: '#e2e8f0', color: '#475569' }; const getKeyStatus = (k: any) => { const status = String(k?.keyStatus ?? '').trim(); // 钥匙状态(0-待取出 1-已取出 2-已归还) if (status === '0') return 'to_take'; if (status === '1') return 'taken'; if (status === '2') return 'returned'; return 'unknown'; }; const getUserAvatarSrc = (u: any) => u?.avatar ?? u?.avatarUrl ?? u?.headImg ?? u?.headImage ?? u?.headUrl ?? u?.profilePhoto ?? u?.photo ?? undefined; const getUserName = (u: any) => u?.nickname ?? u?.nickName ?? u?.name ?? u?.username ?? '-'; const getLockerStatus = (u: any) => { // 上锁人状态:0未上锁、1已上锁、2已解锁 const s = String(u?.status ?? '').trim(); if (s === '1') return 'locked'; if (s === '2') return 'unlocked_done'; if (s === '0') return 'unlocked'; return 'unknown'; }; return (
{/* 第一步:取挂锁,取钥匙 */}
第一步:取挂锁,取钥匙
是否上锁
{(lockPersons.length > 0 ? lockPersons : [{ __empty: true }]).map((u: any, idx: number) => { if (u.__empty) { return (
暂无上锁人
); } const name = getUserName(u); const avatarSrc = getUserAvatarSrc(u); const st = getLockerStatus(u); return (
{String(name || '-').slice(0, 1)}
{name}
{st === 'unknown' ? ( <> 未知 ) : st === 'locked' ? ( <> 已上锁 ) : st === 'unlocked_done' ? ( <> 已解锁 ) : ( <> 未上锁 )}
); })}
本次能量隔离任务中需要隔离上锁的点位
{(points.length > 0 ? points : [{ __empty: true }]).map((p: any, idx: number) => { if (p.__empty) { return (
暂无点位
); } const locked = isLocked(p); return (
{getPointName(p)}
{locked ? '已上锁' : '未上锁'}
); })}
{/* 第二步:上锁后归还钥匙 */}
第二步:上锁后归还钥匙
是否有待归还的钥匙
{(keysList.length > 0 ? keysList : [{ __empty: true }]).map((k: any, idx: number) => { if (k.__empty) { return (
暂无钥匙
); } const status = getKeyStatus(k); const isReturned = status === 'returned'; let statusText = '未知'; let statusColor = '#6b7280'; if (status === 'to_take') { statusText = '待取出'; statusColor = '#f97316'; } else if (status === 'taken') { statusText = '已取出'; statusColor = '#dc2626'; } else if (isReturned) { statusText = '已归还'; statusColor = '#15803d'; } const returnedAt = k?.giveBackTime ?? k?.returnTime ?? k?.returnedTime ?? k?.backTime ?? k?.keyReturnTime ?? k?.updateTime ?? k?.returnedAt ?? k?.returnAt; return (
{k?.keyNfc ?? k?.keyName ?? k?.name ?? k?.code ?? '-'}
{status === 'returned' ? ( ) : status === 'taken' ? ( ) : status === 'to_take' ? ( ) : ( )} {statusText}
归还时间:{isReturned && returnedAt != null && String(returnedAt).trim() !== '' ? dateFormatter(returnedAt) : '-'}
); })}
{/* 第三步:共锁人共锁 */}
第三步:共锁人共锁
是否共锁
{(jtCoLockerUsers.length > 0 ? jtCoLockerUsers : [{ __empty: true }]).map((u: any, idx: number) => { if (u.__empty) { return (
暂无共锁人
); } const name = getUserName(u); const avatarSrc = getUserAvatarSrc(u); // 共锁人状态: 0未共锁、1已共锁、2已解共锁 const lockedStatus = String(u?.status ?? '').trim(); const coLockStatusText = lockedStatus === '1' ? '已共锁' : lockedStatus === '2' ? '已解共锁' : '未共锁'; const coLockStatusColor = lockedStatus === '1' ? '#15803d' : lockedStatus === '2' ? '#15803d' : '#dc2626'; return (
{String(name || '-').slice(0, 1)}
{name}
{lockedStatus === '1' ? ( ) : lockedStatus === '2' ? ( ) : ( )} {coLockStatusText}
); })}
); } // 解除隔离(上锁挂牌)节点:三步并列分栏展示 if (isReleaseIsolation && isLockoutTagout) { const releaseKeysList: any[] = Array.isArray((detailData as any)?.keys) ? (detailData as any).keys : []; const releaseCoLockers: any[] = Array.isArray((detailData as any)?.nodeUserList) ? ((detailData as any).nodeUserList || []).filter((u: any) => String(u?.type || '').toLowerCase() === 'jtcolocker') : []; const releaseLockPersons: any[] = Array.isArray((detailData as any)?.nodeUserList) ? ((detailData as any).nodeUserList || []).filter((u: any) => String(u?.type || '').toLowerCase() === 'jtlocker') : []; const getReleaseKeyStatus = (k: any) => { const s = String(k?.keyStatus ?? '').trim(); // 钥匙状态(0-待取出 1-已取出 2-已归还) if (s === '0') return 'to_take'; if (s === '1') return 'taken'; if (s === '2') return 'returned'; return 'unknown'; }; const getReleaseUserAvatarSrc = (u: any) => u?.avatar ?? u?.avatarUrl ?? u?.headImg ?? u?.headImage ?? u?.headUrl ?? u?.profilePhoto ?? u?.photo ?? undefined; const getReleaseUserName = (u: any) => u?.nickname ?? u?.nickName ?? u?.name ?? u?.username ?? '-'; return (
{/* 第一步:解除共锁 */}
第一步:解除共锁
是否解除共锁
{(releaseCoLockers.length > 0 ? releaseCoLockers : [{ __empty: true }]).map((u: any, idx: number) => { if (u.__empty) { return (
暂无共锁人
); } const name = getReleaseUserName(u); const avatarSrc = getReleaseUserAvatarSrc(u); // 共锁人状态: 0未共锁、1已共锁、2已解共锁 const coLockStatus = String(u?.status ?? '').trim(); const coLockText = coLockStatus === '1' ? '已共锁' : coLockStatus === '2' ? '已解共锁' : '未共锁'; const coLockColor = coLockStatus === '1' ? '#15803d' : coLockStatus === '2' ? '#15803d' : '#dc2626'; return (
{String(name || '-').slice(0, 1)}
{name}
{coLockStatus === '1' ? : coLockStatus === '2' ? : } {coLockText}
); })}
{/* 第二步:取钥匙(只显示待取出/已取出,接口若为2已归还则视为已取出) */}
第二步:取钥匙
钥匙状态
{(releaseKeysList.length > 0 ? releaseKeysList : [{ __empty: true }]).map((k: any, idx: number) => { if (k.__empty) { return (
暂无钥匙
); } const status = getReleaseKeyStatus(k); // 第二步只显示两种:0待取出 / 1或2 均显示已取出(已归还说明必然已取出过) const step2Taken = status !== 'to_take'; const step2StatusText = step2Taken ? '已取出' : '待取出'; return (
{k?.keyNfc ?? k?.keyName ?? k?.name ?? '-'}
{step2Taken ? : } {step2StatusText}
); })}
{/* 第三步:还钥匙 */}
第三步:还钥匙
解锁人
{(releaseLockPersons.length > 0 ? releaseLockPersons : [{ __empty: true }]).map((u: any, idx: number) => { if (u.__empty) { return (
暂无解锁人
); } const name = getReleaseUserName(u); const avatarSrc = getReleaseUserAvatarSrc(u); // 上锁人状态: 0未上锁、1已上锁、2已解锁 const lockStatus = String(u?.status ?? '').trim(); const lockText = lockStatus === '1' ? '已上锁' : lockStatus === '2' ? '已解锁' : '未上锁'; const lockColor = lockStatus === '1' ? '#15803d' : lockStatus === '2' ? '#15803d' : '#dc2626'; return (
{String(name || '-').slice(0, 1)}
{name}
{lockStatus === '1' ? : lockStatus === '2' ? : } {lockText}
); })}
钥匙状态
{(releaseKeysList.length > 0 ? releaseKeysList : [{ __empty: true }]).map((k: any, idx: number) => { if (k.__empty) { return (
暂无钥匙
); } const status = getReleaseKeyStatus(k); // 第三步显示三种状态:0待取出、1已取出、2已归还 const step3StatusText = status === 'returned' ? '已归还' : status === 'taken' ? '已取出' : status === 'to_take' ? '待取出' : '未知'; const step3Color = status === 'returned' ? '#15803d' : status === 'taken' ? '#f97316' : '#dc2626'; const returnedAt = k?.giveBackTime ?? k?.returnTime ?? k?.returnedTime ?? k?.backTime ?? k?.keyReturnTime ?? k?.updateTime ?? k?.returnedAt ?? k?.returnAt; return (
{k?.keyNfc ?? k?.keyName ?? k?.name ?? '-'}
{status === 'returned' ? : status === 'taken' ? : } {step3StatusText}
归还时间:{returnedAt != null && String(returnedAt).trim() !== '' ? dateFormatter(returnedAt) : '-'}
); })}
); } const isolationContent = (
{t('form.lockCabinetTip')}
完成取锁、取钥匙后,任务将自动推进
); return isolationContent; } // 审核类型节点 (review) - 表单直接放在外层,取消内置白框避免挤压 if (isReview) { return (
{/* 自定义表单:仅当前节点有 formId 时渲染,避免完成/结束节点误展示上一节点表单 */} {formLoading ? (
{t('form.formLoading')}
) : hasFormIdForCurrentNode && formData.rule && formData.rule.length > 0 ? (
{(() => { 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 (
{(formData.rule || []).map((field: any) => { const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field; return renderFieldPreview(fieldWithDisabled); })}
); })()}
) : (
无需操作
)} {/* 审核意见 */}
setApprovalComment(e.target.value)} placeholder={t('form.reviewCommentPlaceholder')} maxLength={500} showCount disabled={isApproved} readOnly={isApproved} style={{ borderRadius: 10, borderColor: '#d9e8ff' }} />
); } // 其他节点类型(录入信息等,需要根据 formId 获取表单)- 样式与创建作业弹框一致:无浅蓝背景、无底部操作按钮 return (
{/* 自定义表单:仅当前节点有 formId 时渲染,避免完成/结束节点误展示上一节点表单 */} {formLoading ? (
{t('form.formLoading')}
) : hasFormIdForCurrentNode && formData.rule && formData.rule.length > 0 ? (
{(() => { 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 (
{(formData.rule || []).map((field: any) => { // 如果已通过,禁用所有字段 const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field; return renderFieldPreview(fieldWithDisabled); })}
); })()}
) : (
无需操作
)}
); })()}
); } return
{t('form.noData')}
; })()}
); }