|
@@ -40,7 +40,7 @@ import { userApi } from '../api/user';
|
|
|
import { workflowDesignApi } from '../api/WorkflowDesign';
|
|
import { workflowDesignApi } from '../api/WorkflowDesign';
|
|
|
import { getFormPage } from '../api/bpm/form';
|
|
import { getFormPage } from '../api/bpm/form';
|
|
|
import { segregationPointApi } from '../api/spm';
|
|
import { segregationPointApi } from '../api/spm';
|
|
|
-import { Select, Input, Checkbox, Tabs } from 'antd';
|
|
|
|
|
|
|
+import { Select, Input, Checkbox, Tabs, Tooltip } from 'antd';
|
|
|
|
|
|
|
|
interface WorkflowStep {
|
|
interface WorkflowStep {
|
|
|
id: string;
|
|
id: string;
|
|
@@ -281,7 +281,7 @@ function SimpleCustomNode({ data, selected, id }: any) {
|
|
|
}}
|
|
}}
|
|
|
/>
|
|
/>
|
|
|
) : (
|
|
) : (
|
|
|
- getNodeIcon(data.type || 'createJob', 'pending')
|
|
|
|
|
|
|
+ getNodeIcon(data.type || 'createJob', status)
|
|
|
)}
|
|
)}
|
|
|
</div>
|
|
</div>
|
|
|
<div className="font-semibold text-sm text-gray-900 leading-tight text-center break-words w-full flex-1 flex items-center justify-center px-1">
|
|
<div className="font-semibold text-sm text-gray-900 leading-tight text-center break-words w-full flex-1 flex items-center justify-center px-1">
|
|
@@ -362,22 +362,49 @@ const getStatusIcon = (status: 'completed' | 'in_progress' | 'pending') => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 获取节点状态
|
|
|
|
|
|
|
+// 获取节点状态(根据 approvalStatus 字段直接映射)
|
|
|
const getNodeStatus = (workNode: WorkflowWorkNodeDO, nodeMap: Map<string, WorkflowWorkNodeDO>): 'completed' | 'in_progress' | 'pending' => {
|
|
const getNodeStatus = (workNode: WorkflowWorkNodeDO, nodeMap: Map<string, WorkflowWorkNodeDO>): 'completed' | 'in_progress' | 'pending' => {
|
|
|
if (!workNode) return 'pending';
|
|
if (!workNode) return 'pending';
|
|
|
|
|
|
|
|
- if (workNode.approvalStatus === 'approved') {
|
|
|
|
|
- return 'completed';
|
|
|
|
|
- } else if (workNode.approvalStatus === 'unaudited' || workNode.approvalStatus === 'pending') {
|
|
|
|
|
- if (!workNode.parentUuid || workNode.parentUuid === '') {
|
|
|
|
|
|
|
+ const approvalStatus = workNode.approvalStatus;
|
|
|
|
|
+
|
|
|
|
|
+ // 处理字符串类型的 approvalStatus
|
|
|
|
|
+ if (typeof approvalStatus === 'string') {
|
|
|
|
|
+ const statusStr = approvalStatus.toLowerCase();
|
|
|
|
|
+ if (statusStr === 'approved') {
|
|
|
return 'completed';
|
|
return 'completed';
|
|
|
|
|
+ } else if (statusStr === 'running' || statusStr === 'in_progress') {
|
|
|
|
|
+ return 'in_progress';
|
|
|
|
|
+ } else if (statusStr === 'pending' || statusStr === 'unaudited') {
|
|
|
|
|
+ // 对于 pending/unaudited,需要判断是否可以执行(父节点是否已完成)
|
|
|
|
|
+ if (!workNode.parentUuid || workNode.parentUuid === '') {
|
|
|
|
|
+ // 没有父节点,可能是第一个节点,状态为已完成
|
|
|
|
|
+ return 'completed';
|
|
|
|
|
+ }
|
|
|
|
|
+ // 检查父节点状态
|
|
|
|
|
+ const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
|
|
|
|
|
+ const allParentsCompleted = parentUuids.every(parentUuid => {
|
|
|
|
|
+ const parentNode = nodeMap.get(parentUuid);
|
|
|
|
|
+ return parentNode && (parentNode.approvalStatus === 'approved' || parentNode.approvalStatus === 'Approved');
|
|
|
|
|
+ });
|
|
|
|
|
+ return allParentsCompleted ? 'in_progress' : 'pending';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return 'pending';
|
|
|
}
|
|
}
|
|
|
- const parentNode = nodeMap.get(workNode.parentUuid);
|
|
|
|
|
- if (parentNode && parentNode.approvalStatus === 'approved') {
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ // 处理数字类型的 approvalStatus
|
|
|
|
|
+ else if (typeof approvalStatus === 'number') {
|
|
|
|
|
+ // 假设:1-待执行,2-进行中,3-已完成
|
|
|
|
|
+ if (approvalStatus === 3) {
|
|
|
|
|
+ return 'completed';
|
|
|
|
|
+ } else if (approvalStatus === 2) {
|
|
|
return 'in_progress';
|
|
return 'in_progress';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return 'pending';
|
|
|
}
|
|
}
|
|
|
- return 'pending';
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 默认状态为待执行
|
|
|
return 'pending';
|
|
return 'pending';
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -389,6 +416,13 @@ interface WorkflowRendererProps {
|
|
|
getExecutorInfo: (workNode: WorkflowWorkNodeDO, type: string) => string;
|
|
getExecutorInfo: (workNode: WorkflowWorkNodeDO, type: string) => string;
|
|
|
getNodeIcon: (type: string, status: 'completed' | 'in_progress' | 'pending') => React.ReactNode;
|
|
getNodeIcon: (type: string, status: 'completed' | 'in_progress' | 'pending') => React.ReactNode;
|
|
|
formatDate: (date: string | number | Date) => string;
|
|
formatDate: (date: string | number | Date) => string;
|
|
|
|
|
+ approvalStatusDictList: DictDataVO[];
|
|
|
|
|
+ getNodeStatusFromDict: (
|
|
|
|
|
+ approvalStatus: string | number | null | undefined,
|
|
|
|
|
+ approvalStatusDictList: DictDataVO[],
|
|
|
|
|
+ workNode: WorkflowWorkNodeDO | undefined,
|
|
|
|
|
+ nodeMap: Map<string, WorkflowWorkNodeDO>
|
|
|
|
|
+ ) => 'completed' | 'in_progress' | 'pending';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const WorkflowRenderer: React.FC<WorkflowRendererProps> = ({
|
|
const WorkflowRenderer: React.FC<WorkflowRendererProps> = ({
|
|
@@ -398,6 +432,8 @@ const WorkflowRenderer: React.FC<WorkflowRendererProps> = ({
|
|
|
getExecutorInfo,
|
|
getExecutorInfo,
|
|
|
getNodeIcon,
|
|
getNodeIcon,
|
|
|
formatDate,
|
|
formatDate,
|
|
|
|
|
+ approvalStatusDictList,
|
|
|
|
|
+ getNodeStatusFromDict,
|
|
|
}) => {
|
|
}) => {
|
|
|
// 用于控制节点依次渲染的状态
|
|
// 用于控制节点依次渲染的状态
|
|
|
const [visibleNodes, setVisibleNodes] = useState<Set<string>>(new Set());
|
|
const [visibleNodes, setVisibleNodes] = useState<Set<string>>(new Set());
|
|
@@ -998,22 +1034,8 @@ const WorkflowRenderer: React.FC<WorkflowRendererProps> = ({
|
|
|
|
|
|
|
|
// 构建 Timeline 数据
|
|
// 构建 Timeline 数据
|
|
|
const timelineItems = mainFlowNodes.map((node, index) => {
|
|
const timelineItems = mainFlowNodes.map((node, index) => {
|
|
|
- // 判断节点状态
|
|
|
|
|
- let status: 'completed' | 'in_progress' | 'pending' = 'pending';
|
|
|
|
|
- if (node.approvalStatus === 'approved') {
|
|
|
|
|
- status = 'completed';
|
|
|
|
|
- } else if (node.approvalStatus === 'unaudited' || node.approvalStatus === 'pending') {
|
|
|
|
|
- if (index === 0) {
|
|
|
|
|
- status = 'completed';
|
|
|
|
|
- } else {
|
|
|
|
|
- const prevNode = mainFlowNodes[index - 1];
|
|
|
|
|
- if (prevNode && prevNode.approvalStatus === 'approved') {
|
|
|
|
|
- status = 'in_progress';
|
|
|
|
|
- } else {
|
|
|
|
|
- status = 'pending';
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 根据字典判断节点状态
|
|
|
|
|
+ const status = getNodeStatusFromDict(node.approvalStatus, approvalStatusDictList, node, nodeMap);
|
|
|
|
|
|
|
|
const executorInfo = getExecutorInfo(node, node.type || '');
|
|
const executorInfo = getExecutorInfo(node, node.type || '');
|
|
|
const time = node.createTime || node.updateTime;
|
|
const time = node.createTime || node.updateTime;
|
|
@@ -1034,24 +1056,8 @@ const WorkflowRenderer: React.FC<WorkflowRendererProps> = ({
|
|
|
const branchContent = isBranchParent && isExpanded && branchNodes.length > 0 ? (
|
|
const branchContent = isBranchParent && isExpanded && branchNodes.length > 0 ? (
|
|
|
<div className="mt-3 space-y-2" style={{ width: '100%' }}>
|
|
<div className="mt-3 space-y-2" style={{ width: '100%' }}>
|
|
|
{branchNodes.map((branchNode, branchIndex) => {
|
|
{branchNodes.map((branchNode, branchIndex) => {
|
|
|
- let branchStatus: 'completed' | 'in_progress' | 'pending' = 'pending';
|
|
|
|
|
- if (branchNode.approvalStatus === 'approved') {
|
|
|
|
|
- branchStatus = 'completed';
|
|
|
|
|
- } else if (branchNode.approvalStatus === 'unaudited' || branchNode.approvalStatus === 'pending') {
|
|
|
|
|
- // 检查主节点状态
|
|
|
|
|
- if (node.approvalStatus === 'approved') {
|
|
|
|
|
- branchStatus = 'in_progress';
|
|
|
|
|
- } else {
|
|
|
|
|
- // 检查前面的分支节点是否有已完成的
|
|
|
|
|
- const allBranchNodes = node.branchNodes || [];
|
|
|
|
|
- const currentIndex = allBranchNodes.findIndex(bn => bn.uuid === branchNode.uuid);
|
|
|
|
|
- const nodesBefore = allBranchNodes.slice(0, currentIndex);
|
|
|
|
|
- const hasCompletedBefore = nodesBefore.some(n => n.approvalStatus === 'approved');
|
|
|
|
|
- if (hasCompletedBefore) {
|
|
|
|
|
- branchStatus = 'in_progress';
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 根据字典判断分支节点状态
|
|
|
|
|
+ const branchStatus = getNodeStatusFromDict(branchNode.approvalStatus, approvalStatusDictList, branchNode, nodeMap);
|
|
|
|
|
|
|
|
const branchExecutorInfo = getExecutorInfo(branchNode, branchNode.type || '');
|
|
const branchExecutorInfo = getExecutorInfo(branchNode, branchNode.type || '');
|
|
|
const branchTime = branchNode.createTime || branchNode.updateTime;
|
|
const branchTime = branchNode.createTime || branchNode.updateTime;
|
|
@@ -1680,6 +1686,9 @@ export default function WorkJobDetail() {
|
|
|
// 作业状态字典数据
|
|
// 作业状态字典数据
|
|
|
const [jobStatusDictList, setJobStatusDictList] = useState<DictDataVO[]>([]);
|
|
const [jobStatusDictList, setJobStatusDictList] = useState<DictDataVO[]>([]);
|
|
|
|
|
|
|
|
|
|
+ // 审批状态字典数据
|
|
|
|
|
+ const [approvalStatusDictList, setApprovalStatusDictList] = useState<DictDataVO[]>([]);
|
|
|
|
|
+
|
|
|
// 流程树状态
|
|
// 流程树状态
|
|
|
const [workflowTree, setWorkflowTree] = useState<WorkflowNode[]>([]);
|
|
const [workflowTree, setWorkflowTree] = useState<WorkflowNode[]>([]);
|
|
|
const [expandedBranches, setExpandedBranches] = useState<Set<string>>(new Set());
|
|
const [expandedBranches, setExpandedBranches] = useState<Set<string>>(new Set());
|
|
@@ -1781,6 +1790,16 @@ export default function WorkJobDetail() {
|
|
|
});
|
|
});
|
|
|
const urgencyData = (urgencyRes as any)?.data || urgencyRes;
|
|
const urgencyData = (urgencyRes as any)?.data || urgencyRes;
|
|
|
setUrgencyLevelDictList(urgencyData?.list || []);
|
|
setUrgencyLevelDictList(urgencyData?.list || []);
|
|
|
|
|
+
|
|
|
|
|
+ // 加载审批状态字典
|
|
|
|
|
+ const approvalStatusRes = await dictDataApi.getDictDataPage({
|
|
|
|
|
+ pageNo: 1,
|
|
|
|
|
+ pageSize: -1,
|
|
|
|
|
+ dictType: 'approval_status',
|
|
|
|
|
+ });
|
|
|
|
|
+ const approvalStatusData = (approvalStatusRes as any)?.data || approvalStatusRes;
|
|
|
|
|
+ setApprovalStatusDictList(approvalStatusData?.list || []);
|
|
|
|
|
+ console.log('WorkJobDetail: 获取审批状态字典成功', approvalStatusData?.list || []);
|
|
|
} catch (error: any) {
|
|
} catch (error: any) {
|
|
|
console.error('加载数据失败:', error);
|
|
console.error('加载数据失败:', error);
|
|
|
}
|
|
}
|
|
@@ -1846,6 +1865,102 @@ export default function WorkJobDetail() {
|
|
|
return userMap;
|
|
return userMap;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ // 根据 approvalStatus 字典值判断节点状态
|
|
|
|
|
+ const getNodeStatusFromDict = (
|
|
|
|
|
+ approvalStatus: string | number | null | undefined,
|
|
|
|
|
+ approvalStatusDictList: DictDataVO[],
|
|
|
|
|
+ workNode: WorkflowWorkNodeDO | undefined,
|
|
|
|
|
+ nodeMap: Map<string, WorkflowWorkNodeDO>
|
|
|
|
|
+ ): 'completed' | 'in_progress' | 'pending' => {
|
|
|
|
|
+ if (!approvalStatus || !workNode) return 'pending';
|
|
|
|
|
+
|
|
|
|
|
+ // 在字典中查找对应的状态项
|
|
|
|
|
+ const statusStr = String(approvalStatus).toLowerCase();
|
|
|
|
|
+ const dictItem = approvalStatusDictList.find(item =>
|
|
|
|
|
+ String(item.value).toLowerCase() === statusStr ||
|
|
|
|
|
+ String(item.label).toLowerCase() === statusStr ||
|
|
|
|
|
+ String(item.name).toLowerCase() === statusStr
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (dictItem) {
|
|
|
|
|
+ const label = (dictItem.label || dictItem.name || '').toLowerCase();
|
|
|
|
|
+
|
|
|
|
|
+ // 根据字典 label 判断状态类型
|
|
|
|
|
+ // 已完成、已通过、已审批等 -> completed
|
|
|
|
|
+ if (label.includes('已完成') || label.includes('已通过') || label.includes('已审批') ||
|
|
|
|
|
+ label.includes('完成') || label.includes('通过') || label.includes('approved')) {
|
|
|
|
|
+ return 'completed';
|
|
|
|
|
+ }
|
|
|
|
|
+ // 进行中、执行中、待审核等 -> in_progress
|
|
|
|
|
+ if (label.includes('进行中') || label.includes('执行中') || label.includes('待审核') ||
|
|
|
|
|
+ label.includes('pending') || label.includes('running') || label.includes('in_progress')) {
|
|
|
|
|
+ return 'in_progress';
|
|
|
|
|
+ }
|
|
|
|
|
+ // 待执行、未审核、未开始等 -> 需要检查父节点
|
|
|
|
|
+ if (label.includes('待执行') || label.includes('未审核') || label.includes('未开始') ||
|
|
|
|
|
+ label.includes('待开始') || label.includes('unaudited')) {
|
|
|
|
|
+ // 检查父节点状态
|
|
|
|
|
+ if (!workNode.parentUuid || workNode.parentUuid === '') {
|
|
|
|
|
+ return 'completed'; // 没有父节点,可能是第一个节点
|
|
|
|
|
+ }
|
|
|
|
|
+ // 检查所有父节点是否都已完成
|
|
|
|
|
+ const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
|
|
|
|
|
+ const allParentsCompleted = parentUuids.every(parentUuid => {
|
|
|
|
|
+ const parentNode = nodeMap.get(parentUuid);
|
|
|
|
|
+ if (!parentNode) return false;
|
|
|
|
|
+ const parentStatus = getNodeStatusFromDict(parentNode.approvalStatus, approvalStatusDictList, parentNode, nodeMap);
|
|
|
|
|
+ return parentStatus === 'completed';
|
|
|
|
|
+ });
|
|
|
|
|
+ return allParentsCompleted ? 'in_progress' : 'pending';
|
|
|
|
|
+ }
|
|
|
|
|
+ // 已驳回 -> pending
|
|
|
|
|
+ if (label.includes('已驳回') || label.includes('rejected')) {
|
|
|
|
|
+ return 'pending';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有找到字典项,使用默认逻辑(兼容旧代码)
|
|
|
|
|
+ if (typeof approvalStatus === 'string') {
|
|
|
|
|
+ const statusStrLower = approvalStatus.toLowerCase().trim();
|
|
|
|
|
+ if (statusStrLower === 'approved') {
|
|
|
|
|
+ return 'completed';
|
|
|
|
|
+ } else if (statusStrLower === 'running' || statusStrLower === 'in_progress') {
|
|
|
|
|
+ return 'in_progress';
|
|
|
|
|
+ } else if (statusStrLower === 'pending' || statusStrLower === 'unaudited') {
|
|
|
|
|
+ if (!workNode.parentUuid || workNode.parentUuid === '') {
|
|
|
|
|
+ return 'completed';
|
|
|
|
|
+ }
|
|
|
|
|
+ const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
|
|
|
|
|
+ const allParentsCompleted = parentUuids.every(parentUuid => {
|
|
|
|
|
+ const parentNode = nodeMap.get(parentUuid);
|
|
|
|
|
+ if (!parentNode) return false;
|
|
|
|
|
+ return getNodeStatusFromDict(parentNode.approvalStatus, approvalStatusDictList, parentNode, nodeMap) === 'completed';
|
|
|
|
|
+ });
|
|
|
|
|
+ return allParentsCompleted ? 'in_progress' : 'pending';
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (typeof approvalStatus === 'number') {
|
|
|
|
|
+ // 数字类型:通常 1-待执行,2-进行中,3-已完成
|
|
|
|
|
+ if (approvalStatus === 3) {
|
|
|
|
|
+ return 'completed';
|
|
|
|
|
+ } else if (approvalStatus === 2) {
|
|
|
|
|
+ return 'in_progress';
|
|
|
|
|
+ } else if (approvalStatus === 1) {
|
|
|
|
|
+ if (!workNode.parentUuid || workNode.parentUuid === '') {
|
|
|
|
|
+ return 'completed';
|
|
|
|
|
+ }
|
|
|
|
|
+ const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
|
|
|
|
|
+ const allParentsCompleted = parentUuids.every(parentUuid => {
|
|
|
|
|
+ const parentNode = nodeMap.get(parentUuid);
|
|
|
|
|
+ if (!parentNode) return false;
|
|
|
|
|
+ return getNodeStatusFromDict(parentNode.approvalStatus, approvalStatusDictList, parentNode, nodeMap) === 'completed';
|
|
|
|
|
+ });
|
|
|
|
|
+ return allParentsCompleted ? 'in_progress' : 'pending';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return 'pending';
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
// 解析 designContent 并转换为 ReactFlow 格式
|
|
// 解析 designContent 并转换为 ReactFlow 格式
|
|
|
const loadDesignContentToReactFlow = useCallback((designContent: string, workflowWorkNodeDOList: WorkflowWorkNodeDO[]) => {
|
|
const loadDesignContentToReactFlow = useCallback((designContent: string, workflowWorkNodeDOList: WorkflowWorkNodeDO[]) => {
|
|
|
try {
|
|
try {
|
|
@@ -1872,30 +1987,64 @@ export default function WorkJobDetail() {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // 调试:打印 workflowWorkNodeDOList 数据和字典数据
|
|
|
|
|
+ console.log('=== workflowWorkNodeDOList 数据 ===');
|
|
|
|
|
+ workflowWorkNodeDOList.forEach((node) => {
|
|
|
|
|
+ console.log(`节点 ${node.uuid} (${node.nodeName}): approvalStatus = ${node.approvalStatus}`, node);
|
|
|
|
|
+ });
|
|
|
|
|
+ console.log('=== approval_status 字典数据 ===', approvalStatusDictList);
|
|
|
|
|
+
|
|
|
// 转换节点
|
|
// 转换节点
|
|
|
const convertedNodes: Node[] = jsonData.nodes.map((node: any) => {
|
|
const convertedNodes: Node[] = jsonData.nodes.map((node: any) => {
|
|
|
const nodeId = node.uuid || node.id;
|
|
const nodeId = node.uuid || node.id;
|
|
|
const workNode = nodeMap.get(nodeId);
|
|
const workNode = nodeMap.get(nodeId);
|
|
|
|
|
|
|
|
- // 获取节点状态(使用getNodeStatus函数进行更准确的判断)
|
|
|
|
|
|
|
+ // 根据 approvalStatus 字典值获取节点状态
|
|
|
let status: 'completed' | 'in_progress' | 'pending' = 'pending';
|
|
let status: 'completed' | 'in_progress' | 'pending' = 'pending';
|
|
|
if (workNode) {
|
|
if (workNode) {
|
|
|
- status = getNodeStatus(workNode, nodeMap);
|
|
|
|
|
|
|
+ status = getNodeStatusFromDict(workNode.approvalStatus, approvalStatusDictList, workNode, nodeMap);
|
|
|
|
|
+
|
|
|
|
|
+ // 调试:打印节点状态映射
|
|
|
|
|
+ const dictItem = approvalStatusDictList.find(item =>
|
|
|
|
|
+ String(item.value).toLowerCase() === String(workNode.approvalStatus).toLowerCase() ||
|
|
|
|
|
+ String(item.label).toLowerCase() === String(workNode.approvalStatus).toLowerCase()
|
|
|
|
|
+ );
|
|
|
|
|
+ console.log(`节点 ${nodeId} (${workNode.nodeName}): approvalStatus=${JSON.stringify(workNode.approvalStatus)} -> 字典项=${dictItem?.label || '未找到'} -> status=${status}`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.warn(`未找到节点 ${nodeId} 对应的 workNode`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 优先从 workflowWorkNodeDOList 中获取节点数据,如果没有则使用 designContent 中的数据作为兜底
|
|
|
const nodeData = node.data || {};
|
|
const nodeData = node.data || {};
|
|
|
|
|
+
|
|
|
|
|
+ // 节点名称:优先从 workNode 获取
|
|
|
|
|
+ const nodeName = workNode?.nodeName || nodeData.label || node.nodeName || '节点';
|
|
|
|
|
+
|
|
|
|
|
+ // 节点类型:优先从 workNode 获取
|
|
|
|
|
+ const nodeType = workNode?.type || node.type || 'createJob';
|
|
|
|
|
+
|
|
|
|
|
+ // 节点图标:优先从 workNode 获取
|
|
|
|
|
+ const nodeIcon = workNode?.nodeIcon || nodeData.icon || node.nodeIcon || '';
|
|
|
|
|
+
|
|
|
|
|
+ // 节点ID(用于显示):优先从 workNode 获取,如果没有则从 nodeData 获取
|
|
|
|
|
+ const displayNodeId = workNode?.id ? String(workNode.id) : (nodeData.nodeId || '');
|
|
|
|
|
+
|
|
|
return {
|
|
return {
|
|
|
id: nodeId,
|
|
id: nodeId,
|
|
|
- type: node.type || 'createJob',
|
|
|
|
|
- position: node.position || { x: 0, y: 0 },
|
|
|
|
|
|
|
+ type: nodeType, // 使用从 workNode 获取的类型
|
|
|
|
|
+ position: node.position || { x: 0, y: 0 }, // 位置信息从 designContent 获取
|
|
|
data: {
|
|
data: {
|
|
|
|
|
+ // 保留 designContent 中的其他数据作为兜底
|
|
|
...nodeData,
|
|
...nodeData,
|
|
|
- label: workNode?.nodeName || nodeData.label || node.nodeName || '节点',
|
|
|
|
|
- nodeName: workNode?.nodeName || nodeData.label || node.nodeName || '节点',
|
|
|
|
|
- nodeId: nodeData.nodeId || '',
|
|
|
|
|
- icon: nodeData.icon || node.nodeIcon || '',
|
|
|
|
|
- type: node.type || 'createJob',
|
|
|
|
|
|
|
+ // 但显示相关的内容优先使用 workNode 中的数据
|
|
|
|
|
+ label: nodeName,
|
|
|
|
|
+ nodeName: nodeName,
|
|
|
|
|
+ nodeId: displayNodeId,
|
|
|
|
|
+ icon: nodeIcon,
|
|
|
|
|
+ type: nodeType,
|
|
|
status: status,
|
|
status: status,
|
|
|
|
|
+ // 将 workNode 的完整数据也保存到 data 中,方便后续使用
|
|
|
|
|
+ workNode: workNode,
|
|
|
},
|
|
},
|
|
|
};
|
|
};
|
|
|
});
|
|
});
|
|
@@ -2063,7 +2212,7 @@ export default function WorkJobDetail() {
|
|
|
} catch (error: any) {
|
|
} catch (error: any) {
|
|
|
console.error('解析 designContent 失败:', error);
|
|
console.error('解析 designContent 失败:', error);
|
|
|
}
|
|
}
|
|
|
- }, [reactFlowInstance, setNodes, setEdges]);
|
|
|
|
|
|
|
+ }, [reactFlowInstance, setNodes, setEdges, approvalStatusDictList]);
|
|
|
|
|
|
|
|
const loadJobDetail = async () => {
|
|
const loadJobDetail = async () => {
|
|
|
if (!jobId) return;
|
|
if (!jobId) return;
|
|
@@ -2122,7 +2271,7 @@ export default function WorkJobDetail() {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 构建流程树
|
|
// 构建流程树
|
|
|
- const buildWorkflowTree = (nodeList: WorkflowWorkNodeDO[]) => {
|
|
|
|
|
|
|
+ const buildWorkflowTree = useCallback((nodeList: WorkflowWorkNodeDO[]) => {
|
|
|
console.log('开始构建流程树,节点列表:', nodeList);
|
|
console.log('开始构建流程树,节点列表:', nodeList);
|
|
|
|
|
|
|
|
if (!nodeList || nodeList.length === 0) {
|
|
if (!nodeList || nodeList.length === 0) {
|
|
@@ -2158,7 +2307,7 @@ export default function WorkJobDetail() {
|
|
|
|
|
|
|
|
// 递归构建节点树
|
|
// 递归构建节点树
|
|
|
const buildNode = (workNode: WorkflowWorkNodeDO): WorkflowNode => {
|
|
const buildNode = (workNode: WorkflowWorkNodeDO): WorkflowNode => {
|
|
|
- const status = getNodeStatus(workNode, nodeMap);
|
|
|
|
|
|
|
+ const status = getNodeStatusFromDict(workNode.approvalStatus, approvalStatusDictList, workNode, nodeMap);
|
|
|
const children = childrenMap.get(workNode.uuid || '') || [];
|
|
const children = childrenMap.get(workNode.uuid || '') || [];
|
|
|
|
|
|
|
|
// 判断是否有分支(多个子节点)
|
|
// 判断是否有分支(多个子节点)
|
|
@@ -2200,7 +2349,7 @@ export default function WorkJobDetail() {
|
|
|
console.warn('未找到根节点');
|
|
console.warn('未找到根节点');
|
|
|
setWorkflowTree([]);
|
|
setWorkflowTree([]);
|
|
|
}
|
|
}
|
|
|
- };
|
|
|
|
|
|
|
+ }, [approvalStatusDictList]);
|
|
|
|
|
|
|
|
// 切换分支展开/收起
|
|
// 切换分支展开/收起
|
|
|
const toggleBranch = (nodeUuid: string) => {
|
|
const toggleBranch = (nodeUuid: string) => {
|
|
@@ -2422,24 +2571,17 @@ export default function WorkJobDetail() {
|
|
|
return [];
|
|
return [];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const records: FlowRecord[] = jobDetail.workflowWorkNodeDOList.map((node: any, index: number) => {
|
|
|
|
|
- // 根据审批状态确定任务状态
|
|
|
|
|
- let taskStatus: 'completed' | 'in_progress' | 'pending' = 'pending';
|
|
|
|
|
- if (node.approvalStatus === 'approved') {
|
|
|
|
|
- taskStatus = 'completed';
|
|
|
|
|
- } else if (node.approvalStatus === 'unaudited' || node.approvalStatus === 'pending') {
|
|
|
|
|
- // 检查是否是第一个节点或者前面的节点已完成
|
|
|
|
|
- if (index === 0) {
|
|
|
|
|
- taskStatus = 'completed';
|
|
|
|
|
- } else {
|
|
|
|
|
- const prevNode = jobDetail.workflowWorkNodeDOList[index - 1];
|
|
|
|
|
- if (prevNode && prevNode.approvalStatus === 'approved') {
|
|
|
|
|
- taskStatus = 'in_progress';
|
|
|
|
|
- } else {
|
|
|
|
|
- taskStatus = 'pending';
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 构建节点映射
|
|
|
|
|
+ const nodeMap = new Map<string, WorkflowWorkNodeDO>();
|
|
|
|
|
+ jobDetail.workflowWorkNodeDOList.forEach((node: any) => {
|
|
|
|
|
+ if (node.uuid) {
|
|
|
|
|
+ nodeMap.set(node.uuid, node);
|
|
|
}
|
|
}
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const records: FlowRecord[] = jobDetail.workflowWorkNodeDOList.map((node: any, index: number) => {
|
|
|
|
|
+ // 根据字典判断任务状态
|
|
|
|
|
+ const taskStatus = getNodeStatusFromDict(node.approvalStatus, approvalStatusDictList, node, nodeMap);
|
|
|
|
|
|
|
|
// 格式化时间
|
|
// 格式化时间
|
|
|
const startTime = node.createTime ? formatDateWithFormat(node.createTime) : undefined;
|
|
const startTime = node.createTime ? formatDateWithFormat(node.createTime) : undefined;
|
|
@@ -2654,42 +2796,98 @@ export default function WorkJobDetail() {
|
|
|
</h2>
|
|
</h2>
|
|
|
</div>
|
|
</div>
|
|
|
<div className="p-4">
|
|
<div className="p-4">
|
|
|
- <Descriptions column={1} bordered size="small">
|
|
|
|
|
|
|
+ <style>{`
|
|
|
|
|
+ .job-info-descriptions .ant-descriptions-item-label {
|
|
|
|
|
+ width: 100px !important;
|
|
|
|
|
+ min-width: 100px !important;
|
|
|
|
|
+ max-width: 100px !important;
|
|
|
|
|
+ flex: 0 0 100px !important;
|
|
|
|
|
+ }
|
|
|
|
|
+ .job-info-descriptions .ant-descriptions-item-content {
|
|
|
|
|
+ width: 300px !important;
|
|
|
|
|
+ min-width: 300px !important;
|
|
|
|
|
+ max-width: 300px !important;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ }
|
|
|
|
|
+ `}</style>
|
|
|
|
|
+ <Descriptions
|
|
|
|
|
+ column={1}
|
|
|
|
|
+ bordered
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ className="job-info-descriptions"
|
|
|
|
|
+ labelStyle={{
|
|
|
|
|
+ width: '100px',
|
|
|
|
|
+ minWidth: '100px',
|
|
|
|
|
+ maxWidth: '100px',
|
|
|
|
|
+ flex: '0 0 100px'
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
<Descriptions.Item
|
|
<Descriptions.Item
|
|
|
label="流程模板"
|
|
label="流程模板"
|
|
|
>
|
|
>
|
|
|
- {(() => {
|
|
|
|
|
|
|
+ <Tooltip title={(() => {
|
|
|
const template = workflowTemplateList.find(t => t.id === jobDetail?.designId);
|
|
const template = workflowTemplateList.find(t => t.id === jobDetail?.designId);
|
|
|
return template?.name || '-';
|
|
return template?.name || '-';
|
|
|
- })()}
|
|
|
|
|
|
|
+ })()}>
|
|
|
|
|
+ <div style={{ width: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
|
|
|
+ {(() => {
|
|
|
|
|
+ const template = workflowTemplateList.find(t => t.id === jobDetail?.designId);
|
|
|
|
|
+ return template?.name || '-';
|
|
|
|
|
+ })()}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Tooltip>
|
|
|
</Descriptions.Item>
|
|
</Descriptions.Item>
|
|
|
|
|
|
|
|
<Descriptions.Item
|
|
<Descriptions.Item
|
|
|
label="作业分类"
|
|
label="作业分类"
|
|
|
>
|
|
>
|
|
|
- {(() => {
|
|
|
|
|
|
|
+ <Tooltip title={(() => {
|
|
|
const item = workTypeDictList.find(i => i.value === jobDetail?.type);
|
|
const item = workTypeDictList.find(i => i.value === jobDetail?.type);
|
|
|
return item?.label || jobDetail?.type || '-';
|
|
return item?.label || jobDetail?.type || '-';
|
|
|
- })()}
|
|
|
|
|
|
|
+ })()}>
|
|
|
|
|
+ <div style={{ width: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
|
|
|
+ {(() => {
|
|
|
|
|
+ const item = workTypeDictList.find(i => i.value === jobDetail?.type);
|
|
|
|
|
+ return item?.label || jobDetail?.type || '-';
|
|
|
|
|
+ })()}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Tooltip>
|
|
|
</Descriptions.Item>
|
|
</Descriptions.Item>
|
|
|
|
|
|
|
|
<Descriptions.Item
|
|
<Descriptions.Item
|
|
|
label="作业名称"
|
|
label="作业名称"
|
|
|
>
|
|
>
|
|
|
- {jobDetail?.name || '-'}
|
|
|
|
|
|
|
+ <Tooltip title={jobDetail?.name || '-'}>
|
|
|
|
|
+ <div style={{ width: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
|
|
|
+ {jobDetail?.name || '-'}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Tooltip>
|
|
|
</Descriptions.Item>
|
|
</Descriptions.Item>
|
|
|
|
|
|
|
|
<Descriptions.Item
|
|
<Descriptions.Item
|
|
|
label="作业内容"
|
|
label="作业内容"
|
|
|
>
|
|
>
|
|
|
- {jobDetail?.description || jobDetail?.content || '-'}
|
|
|
|
|
|
|
+ <Tooltip title={jobDetail?.description || jobDetail?.content || '-'}>
|
|
|
|
|
+ <div style={{ width: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
|
|
|
+ {jobDetail?.description || jobDetail?.content || '-'}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Tooltip>
|
|
|
</Descriptions.Item>
|
|
</Descriptions.Item>
|
|
|
|
|
|
|
|
<Descriptions.Item label="紧急程度">
|
|
<Descriptions.Item label="紧急程度">
|
|
|
- {(() => {
|
|
|
|
|
|
|
+ <Tooltip title={(() => {
|
|
|
const item = urgencyLevelDictList.find(i => String(i.value) === String(jobDetail?.urgencyLevel || ''));
|
|
const item = urgencyLevelDictList.find(i => String(i.value) === String(jobDetail?.urgencyLevel || ''));
|
|
|
return item?.label || '-';
|
|
return item?.label || '-';
|
|
|
- })()}
|
|
|
|
|
|
|
+ })()}>
|
|
|
|
|
+ <div style={{ width: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
|
|
|
+ {(() => {
|
|
|
|
|
+ const item = urgencyLevelDictList.find(i => String(i.value) === String(jobDetail?.urgencyLevel || ''));
|
|
|
|
|
+ return item?.label || '-';
|
|
|
|
|
+ })()}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Tooltip>
|
|
|
</Descriptions.Item>
|
|
</Descriptions.Item>
|
|
|
</Descriptions>
|
|
</Descriptions>
|
|
|
</div>
|
|
</div>
|