|
|
@@ -1368,16 +1368,47 @@ const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
|
|
|
const workNode = workflowWorkNodeDOList.find(n => n.uuid === node.id);
|
|
|
const nodeData = node.data || {};
|
|
|
|
|
|
+ // 如果是解除隔离节点,需要从对应的隔离方案节点获取数据
|
|
|
+ let isolationNodeData: any = null;
|
|
|
+ if (nodeData.type === 'releaseIsolation') {
|
|
|
+ // 优先使用从隔离方案节点复制的数据(在 loadDesignContentToReactFlow 中已处理)
|
|
|
+ isolationNodeData = nodeData.isolationNodeData;
|
|
|
+
|
|
|
+ // 如果没有复制的数据,尝试从 isolationNodeUuid 查找对应的隔离方案节点
|
|
|
+ if (!isolationNodeData) {
|
|
|
+ const isolationNodeUuid = workNode?.isolationNodeUuid || nodeData.isolationNodeUuid || '';
|
|
|
+ if (isolationNodeUuid) {
|
|
|
+ const isolationWorkNode = workflowWorkNodeDOList.find(n => n.uuid === isolationNodeUuid && n.type === 'isolation');
|
|
|
+ if (isolationWorkNode) {
|
|
|
+ isolationNodeData = {
|
|
|
+ isolationType: isolationWorkNode.isolationType || '',
|
|
|
+ isolationPoints: isolationWorkNode.isolationPoints
|
|
|
+ ? (typeof isolationWorkNode.isolationPoints === 'string'
|
|
|
+ ? JSON.parse(isolationWorkNode.isolationPoints)
|
|
|
+ : isolationWorkNode.isolationPoints)
|
|
|
+ : [],
|
|
|
+ lockPerson: isolationWorkNode.nodeUserList?.find((u: any) => u.type === 'locker' || u.type === 'jtlocker')?.userId || '',
|
|
|
+ coLockPersons: isolationWorkNode.nodeUserList?.filter((u: any) => u.type === 'colocker' || u.type === 'jtcolocker').map((u: any) => u.userId) || [],
|
|
|
+ formId: isolationWorkNode.formId || '',
|
|
|
+ workerUserId: isolationWorkNode.workerUserId || '',
|
|
|
+ points: isolationWorkNode.points || [],
|
|
|
+ locks: isolationWorkNode.locks || [],
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 获取节点配置信息
|
|
|
const nodeConfig = {
|
|
|
nodeName: workNode?.nodeName || nodeData.label || nodeData.nodeName || '',
|
|
|
- responsible: workNode?.workerUserId || nodeData.workerUserId || nodeData.responsible || '',
|
|
|
- formId: workNode?.formId || nodeData.formId || nodeData.submitForm || '',
|
|
|
- isolationType: workNode?.isolationType || nodeData.isolationType || '',
|
|
|
- isolationPoints: workNode?.isolationPoints ? (typeof workNode.isolationPoints === 'string' ? JSON.parse(workNode.isolationPoints) : workNode.isolationPoints) : (nodeData.isolationPoints || []),
|
|
|
+ responsible: isolationNodeData?.workerUserId || workNode?.workerUserId || nodeData.workerUserId || nodeData.responsible || '',
|
|
|
+ formId: isolationNodeData?.formId || workNode?.formId || nodeData.formId || nodeData.submitForm || '',
|
|
|
+ isolationType: isolationNodeData?.isolationType || workNode?.isolationType || nodeData.isolationType || '',
|
|
|
+ isolationPoints: isolationNodeData?.isolationPoints || (workNode?.isolationPoints ? (typeof workNode.isolationPoints === 'string' ? JSON.parse(workNode.isolationPoints) : workNode.isolationPoints) : (nodeData.isolationPoints || [])),
|
|
|
isolationNodeUuid: workNode?.isolationNodeUuid || nodeData.isolationNodeUuid || '',
|
|
|
- lockPerson: workNode?.nodeUserList?.find((u: any) => u.type === 'locker' || u.type === 'jtlocker')?.userId || nodeData.lockPerson || '',
|
|
|
- coLockPersons: workNode?.nodeUserList?.filter((u: any) => u.type === 'colocker' || u.type === 'jtcolocker').map((u: any) => u.userId) || nodeData.coLockPersons || [],
|
|
|
+ lockPerson: isolationNodeData?.lockPerson || workNode?.nodeUserList?.find((u: any) => u.type === 'locker' || u.type === 'jtlocker')?.userId || nodeData.lockPerson || '',
|
|
|
+ coLockPersons: isolationNodeData?.coLockPersons || workNode?.nodeUserList?.filter((u: any) => u.type === 'colocker' || u.type === 'jtcolocker').map((u: any) => u.userId) || nodeData.coLockPersons || [],
|
|
|
notificationMethods: nodeData.notificationMethods || {
|
|
|
sms: false,
|
|
|
message: false,
|
|
|
@@ -1387,6 +1418,9 @@ const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
|
|
|
notificationPerson: nodeData.notificationPerson || '',
|
|
|
notificationTime: workNode?.notifyTime || nodeData.notificationTime || '',
|
|
|
remark: nodeData.remark || '',
|
|
|
+ // 保存隔离方案节点的 points 和 locks 数据(用于解除隔离节点显示)
|
|
|
+ points: isolationNodeData?.points || workNode?.points || [],
|
|
|
+ locks: isolationNodeData?.locks || workNode?.locks || [],
|
|
|
};
|
|
|
|
|
|
// 获取负责人名称
|
|
|
@@ -1441,35 +1475,6 @@ const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
|
|
|
return names.join(', ');
|
|
|
};
|
|
|
|
|
|
- // 获取通知人显示文本
|
|
|
- const getNotificationPersonText = () => {
|
|
|
- const map: Record<string, string> = {
|
|
|
- 'taskResponsible': '任务负责人',
|
|
|
- 'taskParticipant': '任务参与人',
|
|
|
- 'responsibleAndParticipant': '负责人和参与人',
|
|
|
- 'specifiedPerson': '指定人',
|
|
|
- };
|
|
|
- return map[nodeConfig.notificationPerson] || nodeConfig.notificationPerson || '-';
|
|
|
- };
|
|
|
-
|
|
|
- // 获取通知时间显示文本
|
|
|
- const getNotificationTimeText = () => {
|
|
|
- const map: Record<string, string> = {
|
|
|
- 'before': '执行前(上一个节点结束后)',
|
|
|
- 'after': '执行后(该节点结束后)',
|
|
|
- 'time': '选择时间',
|
|
|
- '30min': '任务开始前30分钟',
|
|
|
- '1h': '任务开始前1小时',
|
|
|
- '2h': '任务开始前2小时',
|
|
|
- '4h': '任务开始前4小时',
|
|
|
- '5h': '任务开始前5小时',
|
|
|
- '8h': '任务开始前8小时',
|
|
|
- '12h': '任务开始前12小时',
|
|
|
- '24h': '任务开始前24小时',
|
|
|
- '48h': '任务开始前48小时',
|
|
|
- };
|
|
|
- return map[nodeConfig.notificationTime] || nodeConfig.notificationTime || '-';
|
|
|
- };
|
|
|
|
|
|
return (
|
|
|
<div className="flex-1 overflow-y-auto space-y-6">
|
|
|
@@ -1652,22 +1657,6 @@ const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div>
|
|
|
- <label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
- 通知人
|
|
|
- </label>
|
|
|
- <div className="text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded-lg border border-gray-200">
|
|
|
- {getNotificationPersonText()}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div>
|
|
|
- <label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
- 通知时间
|
|
|
- </label>
|
|
|
- <div className="text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded-lg border border-gray-200">
|
|
|
- {getNotificationTimeText()}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
)}
|
|
|
@@ -1872,38 +1861,43 @@ export default function WorkJobDetail() {
|
|
|
workNode: WorkflowWorkNodeDO | undefined,
|
|
|
nodeMap: Map<string, WorkflowWorkNodeDO>
|
|
|
): 'completed' | 'in_progress' | 'pending' => {
|
|
|
- if (!approvalStatus || !workNode) return 'pending';
|
|
|
+ if (!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
|
|
|
- );
|
|
|
+ // 如果没有 approvalStatus,检查父节点状态来决定是否可以执行
|
|
|
+ if (!approvalStatus) {
|
|
|
+ if (!workNode.parentUuid || workNode.parentUuid === '') {
|
|
|
+ return 'pending'; // 没有父节点且没有状态,默认为待执行
|
|
|
+ }
|
|
|
+ // 检查所有父节点是否都已完成
|
|
|
+ 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';
|
|
|
+ }
|
|
|
|
|
|
+ // 在字典中查找对应的状态项(优先通过 value 匹配,其次通过 label 匹配)
|
|
|
+ const statusStr = String(approvalStatus).toLowerCase().trim();
|
|
|
+ const dictItem = approvalStatusDictList.find(item => {
|
|
|
+ const itemValue = String(item.value || '').toLowerCase().trim();
|
|
|
+ const itemLabel = String(item.label || '').toLowerCase().trim();
|
|
|
+ return itemValue === statusStr || itemLabel === statusStr;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 如果找到字典项,根据字典的 value 来映射状态
|
|
|
if (dictItem) {
|
|
|
- const label = (dictItem.label || dictItem.name || '').toLowerCase();
|
|
|
+ const dictValue = String(dictItem.value || '').toLowerCase().trim();
|
|
|
|
|
|
- // 根据字典 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')) {
|
|
|
- // 检查父节点状态
|
|
|
+ // 根据字典 value 直接映射状态
|
|
|
+ // pending -> pending (待执行)
|
|
|
+ if (dictValue === 'pending') {
|
|
|
+ // 检查父节点状态,如果所有父节点都已完成,则可以进行中
|
|
|
if (!workNode.parentUuid || workNode.parentUuid === '') {
|
|
|
- return 'completed'; // 没有父节点,可能是第一个节点
|
|
|
+ return 'pending';
|
|
|
}
|
|
|
- // 检查所有父节点是否都已完成
|
|
|
const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
|
|
|
const allParentsCompleted = parentUuids.every(parentUuid => {
|
|
|
const parentNode = nodeMap.get(parentUuid);
|
|
|
@@ -1913,22 +1907,33 @@ export default function WorkJobDetail() {
|
|
|
});
|
|
|
return allParentsCompleted ? 'in_progress' : 'pending';
|
|
|
}
|
|
|
- // 已驳回 -> pending
|
|
|
- if (label.includes('已驳回') || label.includes('rejected')) {
|
|
|
- return 'pending';
|
|
|
+ // running -> in_progress (进行中)
|
|
|
+ if (dictValue === 'running') {
|
|
|
+ return 'in_progress';
|
|
|
+ }
|
|
|
+ // approved -> completed (已完成)
|
|
|
+ if (dictValue === 'approved') {
|
|
|
+ return 'completed';
|
|
|
+ }
|
|
|
+ // rejected -> completed (已完成,虽然被拒绝但也算完成状态)
|
|
|
+ if (dictValue === 'rejected') {
|
|
|
+ return 'completed';
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 如果没有找到字典项,使用默认逻辑(兼容旧代码)
|
|
|
+ // 如果没有找到字典项,直接根据 approvalStatus 的值来映射(兼容旧代码)
|
|
|
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 === 'rejected') {
|
|
|
+ return 'completed'; // rejected 也表示已完成
|
|
|
} else if (statusStrLower === 'pending' || statusStrLower === 'unaudited') {
|
|
|
+ // 检查父节点状态
|
|
|
if (!workNode.parentUuid || workNode.parentUuid === '') {
|
|
|
- return 'completed';
|
|
|
+ return 'pending';
|
|
|
}
|
|
|
const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
|
|
|
const allParentsCompleted = parentUuids.every(parentUuid => {
|
|
|
@@ -1945,8 +1950,9 @@ export default function WorkJobDetail() {
|
|
|
} else if (approvalStatus === 2) {
|
|
|
return 'in_progress';
|
|
|
} else if (approvalStatus === 1) {
|
|
|
+ // 检查父节点状态
|
|
|
if (!workNode.parentUuid || workNode.parentUuid === '') {
|
|
|
- return 'completed';
|
|
|
+ return 'pending';
|
|
|
}
|
|
|
const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
|
|
|
const allParentsCompleted = parentUuids.every(parentUuid => {
|
|
|
@@ -1958,6 +1964,7 @@ export default function WorkJobDetail() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 默认返回 pending
|
|
|
return 'pending';
|
|
|
};
|
|
|
|
|
|
@@ -2029,6 +2036,35 @@ export default function WorkJobDetail() {
|
|
|
// 节点ID(用于显示):优先从 workNode 获取,如果没有则从 nodeData 获取
|
|
|
const displayNodeId = workNode?.id ? String(workNode.id) : (nodeData.nodeId || '');
|
|
|
|
|
|
+ // 如果是解除隔离节点,需要从对应的隔离方案节点复制数据
|
|
|
+ let isolationNodeData: any = null;
|
|
|
+ if (nodeType === 'releaseIsolation') {
|
|
|
+ // 获取隔离节点UUID(从 workNode 或 nodeData 中获取)
|
|
|
+ const isolationNodeUuid = workNode?.isolationNodeUuid || nodeData.isolationNodeUuid || '';
|
|
|
+ if (isolationNodeUuid) {
|
|
|
+ // 找到对应的隔离方案节点
|
|
|
+ const isolationWorkNode = workflowWorkNodeDOList.find(n => n.uuid === isolationNodeUuid && n.type === 'isolation');
|
|
|
+ if (isolationWorkNode) {
|
|
|
+ // 复制隔离方案节点的数据
|
|
|
+ isolationNodeData = {
|
|
|
+ isolationType: isolationWorkNode.isolationType || '',
|
|
|
+ isolationPoints: isolationWorkNode.isolationPoints
|
|
|
+ ? (typeof isolationWorkNode.isolationPoints === 'string'
|
|
|
+ ? JSON.parse(isolationWorkNode.isolationPoints)
|
|
|
+ : isolationWorkNode.isolationPoints)
|
|
|
+ : [],
|
|
|
+ lockPerson: isolationWorkNode.nodeUserList?.find((u: any) => u.type === 'locker' || u.type === 'jtlocker')?.userId || '',
|
|
|
+ coLockPersons: isolationWorkNode.nodeUserList?.filter((u: any) => u.type === 'colocker' || u.type === 'jtcolocker').map((u: any) => u.userId) || [],
|
|
|
+ formId: isolationWorkNode.formId || '',
|
|
|
+ workerUserId: isolationWorkNode.workerUserId || '',
|
|
|
+ points: isolationWorkNode.points || [],
|
|
|
+ locks: isolationWorkNode.locks || [],
|
|
|
+ };
|
|
|
+ console.log(`解除隔离节点 ${nodeId} 从隔离方案节点 ${isolationNodeUuid} 复制数据:`, isolationNodeData);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return {
|
|
|
id: nodeId,
|
|
|
type: nodeType, // 使用从 workNode 获取的类型
|
|
|
@@ -2045,6 +2081,8 @@ export default function WorkJobDetail() {
|
|
|
status: status,
|
|
|
// 将 workNode 的完整数据也保存到 data 中,方便后续使用
|
|
|
workNode: workNode,
|
|
|
+ // 如果是解除隔离节点,添加从隔离方案节点复制的数据
|
|
|
+ ...(isolationNodeData ? { isolationNodeData } : {}),
|
|
|
},
|
|
|
};
|
|
|
});
|
|
|
@@ -2523,6 +2561,22 @@ export default function WorkJobDetail() {
|
|
|
}
|
|
|
|
|
|
let executorName = '';
|
|
|
+ let isolationWorkNode: WorkflowWorkNodeDO | undefined = undefined;
|
|
|
+
|
|
|
+ // 如果是解除隔离节点且没有执行人,尝试从对应的隔离方案节点获取
|
|
|
+ if (type === 'releaseIsolation' && !workNode.workerUserId && !workNode.workerUserName) {
|
|
|
+ const isolationNodeUuid = workNode.isolationNodeUuid;
|
|
|
+ if (isolationNodeUuid && jobDetail?.workflowWorkNodeDOList) {
|
|
|
+ isolationWorkNode = jobDetail.workflowWorkNodeDOList.find(
|
|
|
+ n => n.uuid === isolationNodeUuid && n.type === 'isolation'
|
|
|
+ );
|
|
|
+ if (isolationWorkNode) {
|
|
|
+ // 使用隔离方案节点的数据
|
|
|
+ workNode = isolationWorkNode;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (workNode.nodeUserList && Array.isArray(workNode.nodeUserList)) {
|
|
|
const workerUser = workNode.nodeUserList.find((user: any) => user.type === 'worker' || !user.type);
|
|
|
if (workerUser && workerUser.userName) {
|