|
|
@@ -1,4 +1,5 @@
|
|
|
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
|
|
+import { flushSync } from 'react-dom';
|
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
import { Plus, Search, Edit2, Trash2, MoreVertical, FileText, Eye, Play, CheckCircle, RefreshCw, Workflow, Hand } from 'lucide-react';
|
|
|
@@ -432,6 +433,8 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
const [workflowActiveTabKey, setWorkflowActiveTabKey] = useState<string>('info');
|
|
|
const workflowReactFlowWrapper = useRef<HTMLDivElement>(null);
|
|
|
const [workflowReactFlowInstance, setWorkflowReactFlowInstance] = useState<any>(null);
|
|
|
+ /** 当前 workflowNodeConfig 对应的节点 id,用于切换节点时避免把上一节点的 config 应用到画布 */
|
|
|
+ const workflowNodeConfigForIdRef = useRef<string | null>(null);
|
|
|
|
|
|
// 节点配置状态
|
|
|
const [workflowNodeConfig, setWorkflowNodeConfig] = useState({
|
|
|
@@ -1953,6 +1956,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
// 节点点击事件
|
|
|
const onWorkflowNodeClick = useCallback((event: React.MouseEvent, node: Node) => {
|
|
|
console.log('点击节点:', node.id);
|
|
|
+ workflowNodeConfigForIdRef.current = node.id;
|
|
|
|
|
|
// 更新节点选中状态
|
|
|
setWorkflowNodes((nds) =>
|
|
|
@@ -1963,10 +1967,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
)
|
|
|
);
|
|
|
|
|
|
- // 先设置选中的节点,确保右侧面板显示
|
|
|
- setSelectedWorkflowNode(node);
|
|
|
-
|
|
|
- // 优先从 workflowWorkNodeDOList 中查找对应节点数据(通过uuid匹配)
|
|
|
+ // 先计算并设置右侧面板的 config,再设置选中的节点,避免切换时出现「选中已变但面板仍显示上一节点」的中间状态
|
|
|
const nodeDO = workflowWorkNodeDOList.find(item => item.uuid === node.id);
|
|
|
|
|
|
console.log('点击节点:', node.id, '找到的 nodeDO:', nodeDO);
|
|
|
@@ -2030,29 +2031,39 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
}
|
|
|
|
|
|
const fromIsolationOnly = isReleaseIsolation && isolationNodeDO;
|
|
|
+ // 解除隔离且有关联隔离方案时:表单项仅从隔离方案(dataSource)取数,不兜底 source/node.data,保证清空或更换隔离方案内容后解除隔离表单始终与之一致
|
|
|
+ const isolationOnlyIsolationPoints = fromIsolationOnly && dataSource.isolationPoints
|
|
|
+ ? (() => {
|
|
|
+ try {
|
|
|
+ return typeof dataSource.isolationPoints === 'string'
|
|
|
+ ? JSON.parse(dataSource.isolationPoints)
|
|
|
+ : (Array.isArray(dataSource.isolationPoints) ? dataSource.isolationPoints : []);
|
|
|
+ } catch (_) { return []; }
|
|
|
+ })()
|
|
|
+ : (fromIsolationOnly ? [] : isolationPoints);
|
|
|
const nodeConfig = {
|
|
|
nodeName: fromIsolationOnly
|
|
|
? (nodeDO.nodeName || releaseNodeData.label || node.data?.label || config?.label || '')
|
|
|
: (dataSource.nodeName || source.label || config?.label || ''),
|
|
|
nodeIcon: (isolationNodeDO ? nodeDO.nodeIcon : dataSource.nodeIcon) || source.icon || '',
|
|
|
- responsible: (dataSource.workerUserId !== null && dataSource.workerUserId !== undefined && dataSource.workerUserId !== 0)
|
|
|
- ? (typeof dataSource.workerUserId === 'number' ? dataSource.workerUserId : Number(dataSource.workerUserId))
|
|
|
- : (source.workerUserId && source.workerUserId !== '' && source.workerUserId !== '0')
|
|
|
- ? (typeof source.workerUserId === 'number' ? source.workerUserId : Number(source.workerUserId))
|
|
|
- : undefined,
|
|
|
+ responsible: fromIsolationOnly
|
|
|
+ ? (dataSource.workerUserId !== null && dataSource.workerUserId !== undefined && dataSource.workerUserId !== 0 ? (typeof dataSource.workerUserId === 'number' ? dataSource.workerUserId : Number(dataSource.workerUserId)) : undefined)
|
|
|
+ : (dataSource.workerUserId !== null && dataSource.workerUserId !== undefined && dataSource.workerUserId !== 0)
|
|
|
+ ? (typeof dataSource.workerUserId === 'number' ? dataSource.workerUserId : Number(dataSource.workerUserId))
|
|
|
+ : (source.workerUserId && source.workerUserId !== '' && source.workerUserId !== '0' ? (typeof source.workerUserId === 'number' ? source.workerUserId : Number(source.workerUserId)) : undefined),
|
|
|
remark: source.remark || '',
|
|
|
submitForm: fromIsolationOnly
|
|
|
? (nodeDO.formId != null && nodeDO.formId !== '' ? (typeof nodeDO.formId === 'number' ? nodeDO.formId : Number(nodeDO.formId)) : (releaseNodeData.submitForm != null ? (typeof releaseNodeData.submitForm === 'number' ? releaseNodeData.submitForm : Number(releaseNodeData.submitForm)) : undefined))
|
|
|
: (dataSource.formId ? (typeof dataSource.formId === 'number' ? dataSource.formId : Number(dataSource.formId)) : (source.submitForm ? (typeof source.submitForm === 'number' ? source.submitForm : Number(source.submitForm)) : undefined)),
|
|
|
- isolationType: (dataSource.isolationType !== null && dataSource.isolationType !== undefined) ? String(dataSource.isolationType) : (source.isolationType || ''),
|
|
|
- isolationPoints,
|
|
|
+ isolationType: fromIsolationOnly ? (dataSource.isolationType != null && dataSource.isolationType !== undefined ? String(dataSource.isolationType) : '') : (dataSource.isolationType !== null && dataSource.isolationType !== undefined ? String(dataSource.isolationType) : (source.isolationType || '')),
|
|
|
+ isolationPoints: fromIsolationOnly ? isolationOnlyIsolationPoints : isolationPoints,
|
|
|
isolationNode: source.isolationNode || [],
|
|
|
isolationNodeUuid: nodeDO.isolationNodeUuid || source.isolationNodeUuid || '',
|
|
|
- lockPerson: lockPersonId || (source.lockPerson ? (typeof source.lockPerson === 'number' ? source.lockPerson : Number(source.lockPerson)) : undefined),
|
|
|
- coLockPersons: coLockPersonIds.length > 0 ? coLockPersonIds : (source.coLockPersons && Array.isArray(source.coLockPersons) ? source.coLockPersons.map((id: any) => typeof id === 'number' ? id : Number(id)) : []),
|
|
|
+ lockPerson: fromIsolationOnly ? (lockPersonId || undefined) : (lockPersonId || (source.lockPerson ? (typeof source.lockPerson === 'number' ? source.lockPerson : Number(source.lockPerson)) : undefined)),
|
|
|
+ coLockPersons: fromIsolationOnly ? coLockPersonIds : (coLockPersonIds.length > 0 ? coLockPersonIds : (source.coLockPersons && Array.isArray(source.coLockPersons) ? source.coLockPersons.map((id: any) => typeof id === 'number' ? id : Number(id)) : [])),
|
|
|
notificationMethods: source.notificationMethods || { sms: false, message: false, email: false, app: false },
|
|
|
notificationPerson: source.notificationPerson || '',
|
|
|
- notificationTime: dataSource.notifyTime || source.notificationTime || '',
|
|
|
+ notificationTime: fromIsolationOnly ? (dataSource.notifyTime ?? '') : (dataSource.notifyTime || source.notificationTime || ''),
|
|
|
smsTemplateCode: source.smsTemplateCode || 'false',
|
|
|
messageTemplateCode: source.messageTemplateCode || 'false',
|
|
|
emailTemplateCode: source.emailTemplateCode || 'false',
|
|
|
@@ -2109,6 +2120,10 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
+ // 使用 flushSync 立即提交选中节点,确保右侧面板在本轮事件中完成切换,避免「有时切换未生效」
|
|
|
+ flushSync(() => {
|
|
|
+ setSelectedWorkflowNode(node);
|
|
|
+ });
|
|
|
setWorkflowActiveTabKey('info');
|
|
|
}, [workflowNodeConfigCache, workflowWorkNodeDOList, nodeConfigs]);
|
|
|
|
|
|
@@ -2117,9 +2132,10 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
setSelectedWorkflowNode(null);
|
|
|
}, []);
|
|
|
|
|
|
- // 实时更新节点显示(只在 workflowNodeConfig 变化时更新,避免在切换节点时覆盖)
|
|
|
+ // 实时更新节点显示(仅当当前 config 属于当前选中节点时才写回画布,避免切换节点时把上一节点的 config 误写到新节点)
|
|
|
useEffect(() => {
|
|
|
- if (selectedWorkflowNode && workflowNodeConfig.nodeName) {
|
|
|
+ if (!selectedWorkflowNode || selectedWorkflowNode.id !== workflowNodeConfigForIdRef.current) return;
|
|
|
+ if (workflowNodeConfig.nodeName) {
|
|
|
const { isolationMethod, ...restData } = selectedWorkflowNode.data || {};
|
|
|
// 从缓存中读取 completed 状态,保持保存状态不变
|
|
|
const isSaved = nodeSavedStatusCache.get(selectedWorkflowNode.id) || false;
|
|
|
@@ -4535,10 +4551,10 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
)}
|
|
|
</div>
|
|
|
|
|
|
- {/* 右侧:配置面板 */}
|
|
|
+ {/* 右侧:配置面板;key 随选中节点变化以强制重挂载,避免切换节点后面板内容未刷新 */}
|
|
|
<div className="w-80 border border-gray-200 rounded-lg bg-white overflow-y-auto flex-shrink-0">
|
|
|
{selectedWorkflowNode ? (
|
|
|
- <div className="h-full flex flex-col">
|
|
|
+ <div key={selectedWorkflowNode.id} className="h-full flex flex-col">
|
|
|
{/* 头部 */}
|
|
|
<div className="px-4 py-2.5 bg-white border-b border-gray-200 flex items-center justify-between sticky top-0 z-10 shadow-sm">
|
|
|
<h3 className="text-sm font-medium text-gray-900">
|