|
@@ -36,6 +36,7 @@ import {
|
|
|
SafetyOutlined,
|
|
SafetyOutlined,
|
|
|
UnlockOutlined,
|
|
UnlockOutlined,
|
|
|
LockOutlined,
|
|
LockOutlined,
|
|
|
|
|
+ KeyOutlined,
|
|
|
CheckSquareOutlined,
|
|
CheckSquareOutlined,
|
|
|
CloseOutlined,
|
|
CloseOutlined,
|
|
|
MenuFoldOutlined,
|
|
MenuFoldOutlined,
|
|
@@ -148,6 +149,15 @@ import { getFormPage, getForm, FormVO } from '../api/bpm/form';
|
|
|
import { setConfAndFields2, FormCreateData } from '../utils/formCreate';
|
|
import { setConfAndFields2, FormCreateData } from '../utils/formCreate';
|
|
|
import { dictDataApi, DictDataVO } from '../api/DictData';
|
|
import { dictDataApi, DictDataVO } from '../api/DictData';
|
|
|
import FormUploadField from './FormUploadField';
|
|
import FormUploadField from './FormUploadField';
|
|
|
|
|
+import {
|
|
|
|
|
+ isWorkflowCoLockType,
|
|
|
|
|
+ isWorkflowIsolationSchemePanelType,
|
|
|
|
|
+ isWorkflowLockSchemeType,
|
|
|
|
|
+ isWorkflowUnlockCoLockType,
|
|
|
|
|
+ isWorkflowUnlockParentMatch,
|
|
|
|
|
+ isWorkflowUnlockSchemeType,
|
|
|
|
|
+ resolveWorkflowPaletteType,
|
|
|
|
|
+} from '../utils/workflowNodeTypes';
|
|
|
|
|
|
|
|
/** 抄送部门/人员 ID:支持数组、逗号分隔字符串、JSON 数组字符串,兼容旧 cc* 单选 */
|
|
/** 抄送部门/人员 ID:支持数组、逗号分隔字符串、JSON 数组字符串,兼容旧 cc* 单选 */
|
|
|
function parseWorkflowCopyIds(multi: unknown, legacySingle: unknown): string[] {
|
|
function parseWorkflowCopyIds(multi: unknown, legacySingle: unknown): string[] {
|
|
@@ -236,9 +246,9 @@ const nodeConfigs = [
|
|
|
borderColor: 'border-purple-100',
|
|
borderColor: 'border-purple-100',
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- type: 'isolation',
|
|
|
|
|
- label: '隔离/方案',
|
|
|
|
|
- icon: SafetyOutlined,
|
|
|
|
|
|
|
+ type: 'lock',
|
|
|
|
|
+ label: '上锁',
|
|
|
|
|
+ icon: LockOutlined,
|
|
|
bgColor: 'bg-white',
|
|
bgColor: 'bg-white',
|
|
|
bgColorCustom: '#ffffff',
|
|
bgColorCustom: '#ffffff',
|
|
|
iconColor: 'text-red-600',
|
|
iconColor: 'text-red-600',
|
|
@@ -246,8 +256,8 @@ const nodeConfigs = [
|
|
|
borderColor: 'border-red-100',
|
|
borderColor: 'border-red-100',
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- type: 'releaseIsolation',
|
|
|
|
|
- label: '解除隔离',
|
|
|
|
|
|
|
+ type: 'unlock',
|
|
|
|
|
+ label: '解锁',
|
|
|
icon: UnlockOutlined,
|
|
icon: UnlockOutlined,
|
|
|
bgColor: 'bg-white',
|
|
bgColor: 'bg-white',
|
|
|
bgColorCustom: '#ffffff',
|
|
bgColorCustom: '#ffffff',
|
|
@@ -255,6 +265,26 @@ const nodeConfigs = [
|
|
|
iconColorCustom: '#38bdf8',
|
|
iconColorCustom: '#38bdf8',
|
|
|
borderColor: 'border-yellow-100',
|
|
borderColor: 'border-yellow-100',
|
|
|
},
|
|
},
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'coLock',
|
|
|
|
|
+ label: '共锁',
|
|
|
|
|
+ icon: TeamOutlined,
|
|
|
|
|
+ bgColor: 'bg-white',
|
|
|
|
|
+ bgColorCustom: '#ffffff',
|
|
|
|
|
+ iconColor: 'text-red-600',
|
|
|
|
|
+ iconColorCustom: '#f87272',
|
|
|
|
|
+ borderColor: 'border-red-100',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'unlockCoLock',
|
|
|
|
|
+ label: '解除共锁',
|
|
|
|
|
+ icon: KeyOutlined,
|
|
|
|
|
+ bgColor: 'bg-white',
|
|
|
|
|
+ bgColorCustom: '#ffffff',
|
|
|
|
|
+ iconColor: 'text-yellow-600',
|
|
|
|
|
+ iconColorCustom: '#38bdf8',
|
|
|
|
|
+ borderColor: 'border-yellow-100',
|
|
|
|
|
+ },
|
|
|
{
|
|
{
|
|
|
type: 'returnLock',
|
|
type: 'returnLock',
|
|
|
label: '还锁',
|
|
label: '还锁',
|
|
@@ -286,6 +316,7 @@ const availableIcons = [
|
|
|
{ name: 'SafetyOutlined', component: SafetyOutlined, label: '安全' },
|
|
{ name: 'SafetyOutlined', component: SafetyOutlined, label: '安全' },
|
|
|
{ name: 'UnlockOutlined', component: UnlockOutlined, label: '解锁' },
|
|
{ name: 'UnlockOutlined', component: UnlockOutlined, label: '解锁' },
|
|
|
{ name: 'LockOutlined', component: LockOutlined, label: '锁定' },
|
|
{ name: 'LockOutlined', component: LockOutlined, label: '锁定' },
|
|
|
|
|
+ { name: 'KeyOutlined', component: KeyOutlined, label: '钥匙' },
|
|
|
{ name: 'CheckSquareOutlined', component: CheckSquareOutlined, label: '完成' },
|
|
{ name: 'CheckSquareOutlined', component: CheckSquareOutlined, label: '完成' },
|
|
|
{ name: 'HomeOutlined', component: HomeOutlined, label: '首页' },
|
|
{ name: 'HomeOutlined', component: HomeOutlined, label: '首页' },
|
|
|
{ name: 'UserOutlined', component: UserOutlined, label: '用户' },
|
|
{ name: 'UserOutlined', component: UserOutlined, label: '用户' },
|
|
@@ -384,6 +415,7 @@ const iconNameMap: Record<string, React.ComponentType<any>> = {
|
|
|
SafetyOutlined,
|
|
SafetyOutlined,
|
|
|
UnlockOutlined,
|
|
UnlockOutlined,
|
|
|
LockOutlined,
|
|
LockOutlined,
|
|
|
|
|
+ KeyOutlined,
|
|
|
CheckSquareOutlined,
|
|
CheckSquareOutlined,
|
|
|
HomeOutlined,
|
|
HomeOutlined,
|
|
|
UserOutlined,
|
|
UserOutlined,
|
|
@@ -479,13 +511,14 @@ function CustomNode({ data, selected, id }: any) {
|
|
|
let Icon = FileTextOutlined;
|
|
let Icon = FileTextOutlined;
|
|
|
let config = null;
|
|
let config = null;
|
|
|
let iconImagePath: string | null = null;
|
|
let iconImagePath: string | null = null;
|
|
|
|
|
+ const paletteType = resolveWorkflowPaletteType(data.type);
|
|
|
|
|
|
|
|
// 检查是否是图片文件名(如 "1000.png")
|
|
// 检查是否是图片文件名(如 "1000.png")
|
|
|
if (data.icon && /^\d+\.png$/.test(data.icon)) {
|
|
if (data.icon && /^\d+\.png$/.test(data.icon)) {
|
|
|
// 是图片文件名,获取对应的图片路径
|
|
// 是图片文件名,获取对应的图片路径
|
|
|
iconImagePath = getIconPathByFileName(data.icon);
|
|
iconImagePath = getIconPathByFileName(data.icon);
|
|
|
// 尝试找到对应的配置(保持颜色等样式)
|
|
// 尝试找到对应的配置(保持颜色等样式)
|
|
|
- config = nodeConfigs.find(c => c.type === data.type) || nodeConfigs[0];
|
|
|
|
|
|
|
+ config = nodeConfigs.find(c => c.type === paletteType) || nodeConfigs[0];
|
|
|
} else if (data.icon && iconNameMap[data.icon]) {
|
|
} else if (data.icon && iconNameMap[data.icon]) {
|
|
|
// 使用自定义图标组件
|
|
// 使用自定义图标组件
|
|
|
Icon = iconNameMap[data.icon];
|
|
Icon = iconNameMap[data.icon];
|
|
@@ -493,11 +526,11 @@ function CustomNode({ data, selected, id }: any) {
|
|
|
config = nodeConfigs.find(c => c.icon === Icon);
|
|
config = nodeConfigs.find(c => c.icon === Icon);
|
|
|
if (!config) {
|
|
if (!config) {
|
|
|
// 如果找不到配置,使用默认配置
|
|
// 如果找不到配置,使用默认配置
|
|
|
- config = nodeConfigs.find(c => c.type === data.type) || nodeConfigs[0];
|
|
|
|
|
|
|
+ config = nodeConfigs.find(c => c.type === paletteType) || nodeConfigs[0];
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
// 使用节点类型对应的图标
|
|
// 使用节点类型对应的图标
|
|
|
- config = nodeConfigs.find(c => c.type === data.type);
|
|
|
|
|
|
|
+ config = nodeConfigs.find(c => c.type === paletteType);
|
|
|
Icon = config?.icon || FileTextOutlined;
|
|
Icon = config?.icon || FileTextOutlined;
|
|
|
}
|
|
}
|
|
|
// 从节点ID中提取序号,或使用data中的nodeId
|
|
// 从节点ID中提取序号,或使用data中的nodeId
|
|
@@ -661,6 +694,14 @@ function ReleaseIsolationNode(props: any) {
|
|
|
return <CustomNode {...props} />;
|
|
return <CustomNode {...props} />;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function CoLockNode(props: any) {
|
|
|
|
|
+ return <CustomNode {...props} />;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function UnlockCoLockNode(props: any) {
|
|
|
|
|
+ return <CustomNode {...props} />;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function ReturnLockNode(props: any) {
|
|
function ReturnLockNode(props: any) {
|
|
|
return <CustomNode {...props} />;
|
|
return <CustomNode {...props} />;
|
|
|
}
|
|
}
|
|
@@ -675,8 +716,12 @@ const nodeTypes = {
|
|
|
confirm: ConfirmNode,
|
|
confirm: ConfirmNode,
|
|
|
review: ReviewNode,
|
|
review: ReviewNode,
|
|
|
inputInfo: InputInfoNode,
|
|
inputInfo: InputInfoNode,
|
|
|
|
|
+ lock: IsolationNode,
|
|
|
isolation: IsolationNode,
|
|
isolation: IsolationNode,
|
|
|
|
|
+ unlock: ReleaseIsolationNode,
|
|
|
releaseIsolation: ReleaseIsolationNode,
|
|
releaseIsolation: ReleaseIsolationNode,
|
|
|
|
|
+ coLock: CoLockNode,
|
|
|
|
|
+ unlockCoLock: UnlockCoLockNode,
|
|
|
returnLock: ReturnLockNode,
|
|
returnLock: ReturnLockNode,
|
|
|
complete: CompleteNode,
|
|
complete: CompleteNode,
|
|
|
};
|
|
};
|
|
@@ -824,6 +869,10 @@ const getIconCategoryByNodeType = (nodeType: string): string => {
|
|
|
'confirm': '确认',
|
|
'confirm': '确认',
|
|
|
'review': '审核',
|
|
'review': '审核',
|
|
|
'inputInfo': '录入',
|
|
'inputInfo': '录入',
|
|
|
|
|
+ 'lock': '能量隔离',
|
|
|
|
|
+ 'unlock': '解除隔离',
|
|
|
|
|
+ 'coLock': '能量隔离',
|
|
|
|
|
+ 'unlockCoLock': '解除隔离',
|
|
|
'isolation': '能量隔离',
|
|
'isolation': '能量隔离',
|
|
|
'releaseIsolation': '解除隔离',
|
|
'releaseIsolation': '解除隔离',
|
|
|
'returnLock': '结束',
|
|
'returnLock': '结束',
|
|
@@ -1521,8 +1570,10 @@ export default function ProcessDesigner() {
|
|
|
};
|
|
};
|
|
|
// 缓存配置
|
|
// 缓存配置
|
|
|
saveNodeCache(selectedNode.id, updatedData);
|
|
saveNodeCache(selectedNode.id, updatedData);
|
|
|
- const isIsolationNode = selectedNode.data?.type === 'isolation';
|
|
|
|
|
- const isolationSyncPayload = isIsolationNode
|
|
|
|
|
|
|
+ const isLockSchemeSource = isWorkflowLockSchemeType(selectedNode.data?.type);
|
|
|
|
|
+ const isCoLockSchemeSource = isWorkflowCoLockType(selectedNode.data?.type);
|
|
|
|
|
+ const isolationSyncPayload =
|
|
|
|
|
+ isLockSchemeSource || isCoLockSchemeSource
|
|
|
? {
|
|
? {
|
|
|
isolationType: nodeConfig.isolationType,
|
|
isolationType: nodeConfig.isolationType,
|
|
|
isolationPoints: nodeConfig.isolationPoints,
|
|
isolationPoints: nodeConfig.isolationPoints,
|
|
@@ -1532,7 +1583,17 @@ export default function ProcessDesigner() {
|
|
|
workerUserId: responsibleValue,
|
|
workerUserId: responsibleValue,
|
|
|
}
|
|
}
|
|
|
: null;
|
|
: null;
|
|
|
- // 实时更新节点显示;若当前为隔离/方案节点,则把修改内容同步到所有已关联的解除隔离节点
|
|
|
|
|
|
|
+ /** 上锁节点变更时同步到「共锁」节点:不覆盖共锁节点自身的共锁人 */
|
|
|
|
|
+ const lockSchemeToCoLockPayload = isLockSchemeSource
|
|
|
|
|
+ ? {
|
|
|
|
|
+ isolationType: nodeConfig.isolationType,
|
|
|
|
|
+ isolationPoints: nodeConfig.isolationPoints,
|
|
|
|
|
+ lockPerson: nodeConfig.lockPerson,
|
|
|
|
|
+ responsible: responsibleValue,
|
|
|
|
|
+ workerUserId: responsibleValue,
|
|
|
|
|
+ }
|
|
|
|
|
+ : null;
|
|
|
|
|
+ // 实时更新节点显示;若当前为上锁/共锁节点,则把修改内容同步到已关联的解锁/解除共锁节点
|
|
|
setNodes((nds) =>
|
|
setNodes((nds) =>
|
|
|
nds.map((node) => {
|
|
nds.map((node) => {
|
|
|
if (node.id === selectedNode.id) {
|
|
if (node.id === selectedNode.id) {
|
|
@@ -1540,7 +1601,8 @@ export default function ProcessDesigner() {
|
|
|
}
|
|
}
|
|
|
if (
|
|
if (
|
|
|
isolationSyncPayload &&
|
|
isolationSyncPayload &&
|
|
|
- node.data?.type === 'releaseIsolation' &&
|
|
|
|
|
|
|
+ isWorkflowUnlockSchemeType(node.data?.type) &&
|
|
|
|
|
+ isLockSchemeSource &&
|
|
|
String(node.data?.isolationNodeUuid) === String(selectedNode.id)
|
|
String(node.data?.isolationNodeUuid) === String(selectedNode.id)
|
|
|
) {
|
|
) {
|
|
|
const releaseData = {
|
|
const releaseData = {
|
|
@@ -1550,6 +1612,34 @@ export default function ProcessDesigner() {
|
|
|
saveNodeCache(node.id, releaseData);
|
|
saveNodeCache(node.id, releaseData);
|
|
|
return { ...node, data: releaseData };
|
|
return { ...node, data: releaseData };
|
|
|
}
|
|
}
|
|
|
|
|
+ if (
|
|
|
|
|
+ isolationSyncPayload &&
|
|
|
|
|
+ isWorkflowUnlockCoLockType(node.data?.type) &&
|
|
|
|
|
+ isCoLockSchemeSource &&
|
|
|
|
|
+ String(node.data?.isolationNodeUuid) === String(selectedNode.id)
|
|
|
|
|
+ ) {
|
|
|
|
|
+ const releaseData = {
|
|
|
|
|
+ ...node.data,
|
|
|
|
|
+ ...isolationSyncPayload,
|
|
|
|
|
+ };
|
|
|
|
|
+ saveNodeCache(node.id, releaseData);
|
|
|
|
|
+ return { ...node, data: releaseData };
|
|
|
|
|
+ }
|
|
|
|
|
+ if (
|
|
|
|
|
+ lockSchemeToCoLockPayload &&
|
|
|
|
|
+ isWorkflowCoLockType(node.data?.type) &&
|
|
|
|
|
+ isLockSchemeSource &&
|
|
|
|
|
+ String(node.data?.isolationNodeUuid) === String(selectedNode.id)
|
|
|
|
|
+ ) {
|
|
|
|
|
+ const prevCo = Array.isArray(node.data?.coLockPersons) ? node.data.coLockPersons : [];
|
|
|
|
|
+ const releaseData = {
|
|
|
|
|
+ ...node.data,
|
|
|
|
|
+ ...lockSchemeToCoLockPayload,
|
|
|
|
|
+ coLockPersons: prevCo,
|
|
|
|
|
+ };
|
|
|
|
|
+ saveNodeCache(node.id, releaseData);
|
|
|
|
|
+ return { ...node, data: releaseData };
|
|
|
|
|
+ }
|
|
|
return node;
|
|
return node;
|
|
|
})
|
|
})
|
|
|
);
|
|
);
|
|
@@ -1997,9 +2087,9 @@ export default function ProcessDesigner() {
|
|
|
const nodeData = node.data || {};
|
|
const nodeData = node.data || {};
|
|
|
const cache = loadNodeCache(node.id);
|
|
const cache = loadNodeCache(node.id);
|
|
|
const source = cache || nodeData;
|
|
const source = cache || nodeData;
|
|
|
- const config = nodeConfigs.find(c => c.type === source.type);
|
|
|
|
|
|
|
+ const config = nodeConfigs.find(c => c.type === resolveWorkflowPaletteType(source.type));
|
|
|
|
|
|
|
|
- // 如果是解除隔离节点且已选择了隔离节点,则从隔离节点获取配置
|
|
|
|
|
|
|
+ // 如果是解锁/解除共锁节点且已选择了关联方案节点,则从关联节点获取配置
|
|
|
let isolationType = source.isolationType || '';
|
|
let isolationType = source.isolationType || '';
|
|
|
let isolationPointsData = source.isolationPoints || [];
|
|
let isolationPointsData = source.isolationPoints || [];
|
|
|
let lockPerson = source.lockPerson || '';
|
|
let lockPerson = source.lockPerson || '';
|
|
@@ -2011,8 +2101,15 @@ export default function ProcessDesigner() {
|
|
|
? String(source.responsible)
|
|
? String(source.responsible)
|
|
|
: '';
|
|
: '';
|
|
|
|
|
|
|
|
- if (source.type === 'releaseIsolation' && source.isolationNodeUuid) {
|
|
|
|
|
- const targetNode = nodes.find(n => n.id === source.isolationNodeUuid && n.data?.type === 'isolation');
|
|
|
|
|
|
|
+ if (
|
|
|
|
|
+ (isWorkflowUnlockSchemeType(source.type) || isWorkflowUnlockCoLockType(source.type)) &&
|
|
|
|
|
+ source.isolationNodeUuid
|
|
|
|
|
+ ) {
|
|
|
|
|
+ const targetNode = nodes.find(
|
|
|
|
|
+ (n) =>
|
|
|
|
|
+ n.id === source.isolationNodeUuid &&
|
|
|
|
|
+ isWorkflowUnlockParentMatch(source.type, n.data?.type)
|
|
|
|
|
+ );
|
|
|
if (targetNode) {
|
|
if (targetNode) {
|
|
|
// 优先从缓存中读取,缓存中没有则从 node.data 读取
|
|
// 优先从缓存中读取,缓存中没有则从 node.data 读取
|
|
|
const targetCache = loadNodeCache(targetNode.id);
|
|
const targetCache = loadNodeCache(targetNode.id);
|
|
@@ -2028,6 +2125,24 @@ export default function ProcessDesigner() {
|
|
|
? String(targetSource.responsible)
|
|
? String(targetSource.responsible)
|
|
|
: '';
|
|
: '';
|
|
|
}
|
|
}
|
|
|
|
|
+ } else if (isWorkflowCoLockType(source.type) && source.isolationNodeUuid) {
|
|
|
|
|
+ const targetNode = nodes.find(
|
|
|
|
|
+ (n) =>
|
|
|
|
|
+ n.id === source.isolationNodeUuid && isWorkflowLockSchemeType(n.data?.type)
|
|
|
|
|
+ );
|
|
|
|
|
+ if (targetNode) {
|
|
|
|
|
+ const targetCache = loadNodeCache(targetNode.id);
|
|
|
|
|
+ const targetSource = targetCache || targetNode.data || {};
|
|
|
|
|
+ isolationType = targetSource.isolationType || '';
|
|
|
|
|
+ isolationPointsData = targetSource.isolationPoints || [];
|
|
|
|
|
+ lockPerson = targetSource.lockPerson || '';
|
|
|
|
|
+ responsible = (targetSource.workerUserId !== undefined && targetSource.workerUserId !== null && targetSource.workerUserId !== '')
|
|
|
|
|
+ ? String(targetSource.workerUserId)
|
|
|
|
|
+ : (targetSource.responsible !== undefined && targetSource.responsible !== null && targetSource.responsible !== '')
|
|
|
|
|
+ ? String(targetSource.responsible)
|
|
|
|
|
+ : '';
|
|
|
|
|
+ coLockPersons = Array.isArray(source.coLockPersons) ? source.coLockPersons : [];
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
setNodeConfig({
|
|
setNodeConfig({
|
|
@@ -3117,7 +3232,7 @@ export default function ProcessDesigner() {
|
|
|
if (updatedNode) {
|
|
if (updatedNode) {
|
|
|
const cache = loadNodeCache(updatedNode.id);
|
|
const cache = loadNodeCache(updatedNode.id);
|
|
|
const source = cache || updatedNode.data || {};
|
|
const source = cache || updatedNode.data || {};
|
|
|
- const config = nodeConfigs.find(c => c.type === source.type);
|
|
|
|
|
|
|
+ const config = nodeConfigs.find(c => c.type === resolveWorkflowPaletteType(source.type));
|
|
|
setNodeConfig({
|
|
setNodeConfig({
|
|
|
nodeName: source.label || config?.label || '',
|
|
nodeName: source.label || config?.label || '',
|
|
|
nodeIcon: source.icon || source.type || '',
|
|
nodeIcon: source.icon || source.type || '',
|
|
@@ -3175,6 +3290,10 @@ export default function ProcessDesigner() {
|
|
|
confirm: '该节点为作业确认,为"上一节点"操作分配确认模式及确认人员权限',
|
|
confirm: '该节点为作业确认,为"上一节点"操作分配确认模式及确认人员权限',
|
|
|
review: '该节点为作业审核,为"上一节点"操作分配审核模式及审核人员权限',
|
|
review: '该节点为作业审核,为"上一节点"操作分配审核模式及审核人员权限',
|
|
|
inputInfo: '该节点为作业录入提交,可提交信息或图片,主要为"信息确认"',
|
|
inputInfo: '该节点为作业录入提交,可提交信息或图片,主要为"信息确认"',
|
|
|
|
|
+ lock: '该节点为作业隔离类型选择,主要包括盲板,上锁挂牌,拆除等。',
|
|
|
|
|
+ unlock: '该节点在解锁时与所选上锁节点保持隔离方式等信息一致。',
|
|
|
|
|
+ coLock: '该节点需选择流程中的上锁任务并配置共锁人;隔离信息随所选上锁任务一致。',
|
|
|
|
|
+ unlockCoLock: '该节点在解除共锁时与所选共锁节点保持隔离方式等信息一致。',
|
|
|
isolation: '该节点为作业隔离类型选择,主要包括盲板,上锁挂牌,拆除等。',
|
|
isolation: '该节点为作业隔离类型选择,主要包括盲板,上锁挂牌,拆除等。',
|
|
|
releaseIsolation: '该节点为作业隔离类型选择,主要包括盲板,上锁挂牌,拆除等。',
|
|
releaseIsolation: '该节点为作业隔离类型选择,主要包括盲板,上锁挂牌,拆除等。',
|
|
|
returnLock: '该节点为还锁操作,归还钥匙,确认隔离操作完成',
|
|
returnLock: '该节点为还锁操作,归还钥匙,确认隔离操作完成',
|
|
@@ -3183,6 +3302,11 @@ export default function ProcessDesigner() {
|
|
|
return descriptions[type] || '该节点的功能描述。';
|
|
return descriptions[type] || '该节点的功能描述。';
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ const selectedDataType = selectedNode?.data?.type || '';
|
|
|
|
|
+ const isUnlockDesignerReadOnly =
|
|
|
|
|
+ isWorkflowUnlockSchemeType(selectedDataType) || isWorkflowUnlockCoLockType(selectedDataType);
|
|
|
|
|
+ const isSchemeDesignerPanel = isWorkflowIsolationSchemePanelType(selectedDataType);
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<div className="h-screen w-screen flex flex-col bg-gray-50">
|
|
<div className="h-screen w-screen flex flex-col bg-gray-50">
|
|
|
{/* 顶部工具栏 */}
|
|
{/* 顶部工具栏 */}
|
|
@@ -3344,7 +3468,9 @@ export default function ProcessDesigner() {
|
|
|
<Background variant={BackgroundVariant.Lines} gap={16} size={1} color="#e5e7eb" />
|
|
<Background variant={BackgroundVariant.Lines} gap={16} size={1} color="#e5e7eb" />
|
|
|
<MiniMap
|
|
<MiniMap
|
|
|
nodeColor={(node) => {
|
|
nodeColor={(node) => {
|
|
|
- const config = nodeConfigs.find(c => c.type === node.type);
|
|
|
|
|
|
|
+ const config = nodeConfigs.find(
|
|
|
|
|
+ (c) => c.type === resolveWorkflowPaletteType(node.type as string)
|
|
|
|
|
+ );
|
|
|
if (config?.color === 'bg-blue-500') return '#3b82f6';
|
|
if (config?.color === 'bg-blue-500') return '#3b82f6';
|
|
|
if (config?.color === 'bg-green-500') return '#10b981';
|
|
if (config?.color === 'bg-green-500') return '#10b981';
|
|
|
if (config?.color === 'bg-orange-500') return '#f97316';
|
|
if (config?.color === 'bg-orange-500') return '#f97316';
|
|
@@ -3413,8 +3539,8 @@ export default function ProcessDesigner() {
|
|
|
<div className="space-y-5">
|
|
<div className="space-y-5">
|
|
|
{/* 节点名称 */}
|
|
{/* 节点名称 */}
|
|
|
<div>
|
|
<div>
|
|
|
- {/* 描述 - 创建作业、确认、审核、录入信息、隔离方案和解除隔离节点显示在节点名称顶部 */}
|
|
|
|
|
- {(selectedNode.data?.type === 'createJob' || selectedNode.data?.type === 'confirm' || selectedNode.data?.type === 'review' || selectedNode.data?.type === 'inputInfo' || selectedNode.data?.type === 'isolation' || selectedNode.data?.type === 'releaseIsolation' || selectedNode.data?.type === 'returnLock' || selectedNode.data?.type === 'complete') && (
|
|
|
|
|
|
|
+ {/* 描述 - 创建作业、确认、审核、录入信息、上锁/解锁/共锁类节点、还锁与结束节点显示在节点名称顶部 */}
|
|
|
|
|
+ {(selectedNode.data?.type === 'createJob' || selectedNode.data?.type === 'confirm' || selectedNode.data?.type === 'review' || selectedNode.data?.type === 'inputInfo' || isSchemeDesignerPanel || selectedNode.data?.type === 'returnLock' || selectedNode.data?.type === 'complete') && (
|
|
|
<div className="text-xs text-gray-500 leading-relaxed mb-2">
|
|
<div className="text-xs text-gray-500 leading-relaxed mb-2">
|
|
|
{getNodeDescription(selectedNode.data?.type || '')}
|
|
{getNodeDescription(selectedNode.data?.type || '')}
|
|
|
</div>
|
|
</div>
|
|
@@ -3427,12 +3553,12 @@ export default function ProcessDesigner() {
|
|
|
</label>
|
|
</label>
|
|
|
{selectedNode.data?.type !== 'createJob' && selectedNode.data?.type !== 'confirm' && (
|
|
{selectedNode.data?.type !== 'createJob' && selectedNode.data?.type !== 'confirm' && (
|
|
|
<p className="text-xs text-gray-500 mb-2.5 leading-relaxed">
|
|
<p className="text-xs text-gray-500 mb-2.5 leading-relaxed">
|
|
|
- (默认名称: {nodeConfigs.find(c => c.type === selectedNode.data?.type)?.label || '节点名称'},可根据需求调整)
|
|
|
|
|
|
|
+ (默认名称: {nodeConfigs.find(c => c.type === resolveWorkflowPaletteType(selectedNode.data?.type))?.label || '节点名称'},可根据需求调整)
|
|
|
</p>
|
|
</p>
|
|
|
)}
|
|
)}
|
|
|
{selectedNode.data?.type === 'createJob' && (
|
|
{selectedNode.data?.type === 'createJob' && (
|
|
|
<p className="text-xs text-gray-500 mb-2.5 leading-relaxed">
|
|
<p className="text-xs text-gray-500 mb-2.5 leading-relaxed">
|
|
|
- (默认名称: {nodeConfigs.find(c => c.type === selectedNode.data?.type)?.label || '节点名称'},可根据需求调整)
|
|
|
|
|
|
|
+ (默认名称: {nodeConfigs.find(c => c.type === resolveWorkflowPaletteType(selectedNode.data?.type))?.label || '节点名称'},可根据需求调整)
|
|
|
</p>
|
|
</p>
|
|
|
)}
|
|
)}
|
|
|
<Input
|
|
<Input
|
|
@@ -3575,7 +3701,7 @@ export default function ProcessDesigner() {
|
|
|
let Icon = FileTextOutlined;
|
|
let Icon = FileTextOutlined;
|
|
|
let config = null;
|
|
let config = null;
|
|
|
const iconType = selectedNode.data?.type;
|
|
const iconType = selectedNode.data?.type;
|
|
|
- config = nodeConfigs.find(c => c.type === iconType);
|
|
|
|
|
|
|
+ config = nodeConfigs.find(c => c.type === resolveWorkflowPaletteType(iconType));
|
|
|
Icon = config?.icon || FileTextOutlined;
|
|
Icon = config?.icon || FileTextOutlined;
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
@@ -3644,8 +3770,8 @@ export default function ProcessDesigner() {
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
- {/* 负责人 - 创建作业、隔离方案和解除隔离节点不显示 */}
|
|
|
|
|
- {selectedNode.data?.type !== 'createJob' && selectedNode.data?.type !== 'confirm' && selectedNode.data?.type !== 'review' && selectedNode.data?.type !== 'isolation' && selectedNode.data?.type !== 'releaseIsolation' && (
|
|
|
|
|
|
|
+ {/* 负责人 - 创建作业与上锁/解锁/共锁类方案节点不显示 */}
|
|
|
|
|
+ {selectedNode.data?.type !== 'createJob' && selectedNode.data?.type !== 'confirm' && selectedNode.data?.type !== 'review' && !isSchemeDesignerPanel && (
|
|
|
<div>
|
|
<div>
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
负责人
|
|
负责人
|
|
@@ -3669,8 +3795,8 @@ export default function ProcessDesigner() {
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
- {/* 备注 - 创建作业、确认、审核、录入信息、隔离方案、解除隔离、还锁和完成节点不显示 */}
|
|
|
|
|
- {selectedNode.data?.type !== 'createJob' && selectedNode.data?.type !== 'confirm' && selectedNode.data?.type !== 'review' && selectedNode.data?.type !== 'inputInfo' && selectedNode.data?.type !== 'isolation' && selectedNode.data?.type !== 'releaseIsolation' && selectedNode.data?.type !== 'returnLock' && selectedNode.data?.type !== 'complete' && (
|
|
|
|
|
|
|
+ {/* 备注 - 创建作业、确认、审核、录入信息、上锁/解锁/共锁类、还锁和完成节点不显示 */}
|
|
|
|
|
+ {selectedNode.data?.type !== 'createJob' && selectedNode.data?.type !== 'confirm' && selectedNode.data?.type !== 'review' && selectedNode.data?.type !== 'inputInfo' && !isSchemeDesignerPanel && selectedNode.data?.type !== 'returnLock' && selectedNode.data?.type !== 'complete' && (
|
|
|
<div>
|
|
<div>
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
备注
|
|
备注
|
|
@@ -3753,22 +3879,27 @@ export default function ProcessDesigner() {
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- {/* 隔离/方案 和 解除隔离 节点特有的字段 */}
|
|
|
|
|
- {(selectedNode.data?.type === 'isolation' || selectedNode.data?.type === 'releaseIsolation') && (
|
|
|
|
|
|
|
+ {/* 上锁/解锁/共锁/解除共锁 节点特有的字段 */}
|
|
|
|
|
+ {isSchemeDesignerPanel && (
|
|
|
<>
|
|
<>
|
|
|
- {/* 解除隔离节点:选择隔离节点 */}
|
|
|
|
|
- {selectedNode.data?.type === 'releaseIsolation' && (
|
|
|
|
|
|
|
+ {(isWorkflowUnlockSchemeType(selectedDataType) ||
|
|
|
|
|
+ isWorkflowUnlockCoLockType(selectedDataType)) && (
|
|
|
<div>
|
|
<div>
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
- 选择隔离节点
|
|
|
|
|
|
|
+ {isWorkflowUnlockCoLockType(selectedDataType)
|
|
|
|
|
+ ? '选择共锁任务'
|
|
|
|
|
+ : '选择上锁任务'}
|
|
|
</label>
|
|
</label>
|
|
|
<Select
|
|
<Select
|
|
|
value={nodeConfig.isolationNodeUuid || undefined}
|
|
value={nodeConfig.isolationNodeUuid || undefined}
|
|
|
onChange={(value) => {
|
|
onChange={(value) => {
|
|
|
setNodeConfig((prev) => {
|
|
setNodeConfig((prev) => {
|
|
|
- const target = nodes.find(n => n.id === value && n.data?.type === 'isolation');
|
|
|
|
|
|
|
+ const target = nodes.find(
|
|
|
|
|
+ (n) =>
|
|
|
|
|
+ n.id === value &&
|
|
|
|
|
+ isWorkflowUnlockParentMatch(selectedDataType, n.data?.type)
|
|
|
|
|
+ );
|
|
|
if (target) {
|
|
if (target) {
|
|
|
- // 优先从缓存中读取,缓存中没有则从 node.data 读取
|
|
|
|
|
const cache = loadNodeCache(target.id);
|
|
const cache = loadNodeCache(target.id);
|
|
|
const source = cache || target.data || {};
|
|
const source = cache || target.data || {};
|
|
|
return {
|
|
return {
|
|
@@ -3787,13 +3918,16 @@ export default function ProcessDesigner() {
|
|
|
return { ...prev, isolationNodeUuid: value || '' };
|
|
return { ...prev, isolationNodeUuid: value || '' };
|
|
|
});
|
|
});
|
|
|
}}
|
|
}}
|
|
|
- placeholder="请选择隔离节点"
|
|
|
|
|
|
|
+ placeholder={
|
|
|
|
|
+ isWorkflowUnlockCoLockType(selectedDataType)
|
|
|
|
|
+ ? '请选择共锁任务'
|
|
|
|
|
+ : '请选择上锁任务'
|
|
|
|
|
+ }
|
|
|
className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
|
|
className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
|
|
|
allowClear
|
|
allowClear
|
|
|
>
|
|
>
|
|
|
{(() => {
|
|
{(() => {
|
|
|
- // 从当前「解除隔离」节点向前(沿连线反向)查找所有上游节点,再从中筛出「隔离/方案」节点
|
|
|
|
|
- // 这样即使中间隔了多个节点(如 隔离 -> A -> B -> 解除隔离),下拉也能出现对应隔离节点
|
|
|
|
|
|
|
+ const wantCoLockParent = isWorkflowUnlockCoLockType(selectedDataType);
|
|
|
const currentNodeId = selectedNode?.id;
|
|
const currentNodeId = selectedNode?.id;
|
|
|
if (!currentNodeId) return [];
|
|
if (!currentNodeId) return [];
|
|
|
|
|
|
|
@@ -3803,7 +3937,7 @@ export default function ProcessDesigner() {
|
|
|
while (queue.length > 0) {
|
|
while (queue.length > 0) {
|
|
|
const nextQueue: string[] = [];
|
|
const nextQueue: string[] = [];
|
|
|
for (const id of queue) {
|
|
for (const id of queue) {
|
|
|
- edges.forEach(edge => {
|
|
|
|
|
|
|
+ edges.forEach((edge) => {
|
|
|
if (String(edge.target) !== id) return;
|
|
if (String(edge.target) !== id) return;
|
|
|
const src = String(edge.source);
|
|
const src = String(edge.source);
|
|
|
if (upstreamIds.has(src)) return;
|
|
if (upstreamIds.has(src)) return;
|
|
@@ -3814,13 +3948,23 @@ export default function ProcessDesigner() {
|
|
|
queue = nextQueue;
|
|
queue = nextQueue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const isolationNodes = nodes.filter(n => n.data?.type === 'isolation');
|
|
|
|
|
- const nodesToShow = isolationNodes.filter(n => upstreamIds.has(String(n.id)));
|
|
|
|
|
- const nodesToShowFinal = nodesToShow.length > 0 ? nodesToShow : isolationNodes;
|
|
|
|
|
|
|
+ const schemeNodes = nodes.filter((n) =>
|
|
|
|
|
+ wantCoLockParent
|
|
|
|
|
+ ? isWorkflowCoLockType(n.data?.type)
|
|
|
|
|
+ : isWorkflowLockSchemeType(n.data?.type)
|
|
|
|
|
+ );
|
|
|
|
|
+ const nodesToShow = schemeNodes.filter((n) =>
|
|
|
|
|
+ upstreamIds.has(String(n.id))
|
|
|
|
|
+ );
|
|
|
|
|
+ const nodesToShowFinal =
|
|
|
|
|
+ nodesToShow.length > 0 ? nodesToShow : schemeNodes;
|
|
|
|
|
+ const defaultLabel = wantCoLockParent
|
|
|
|
|
+ ? nodeConfigs.find((c) => c.type === 'coLock')?.label || '共锁'
|
|
|
|
|
+ : nodeConfigs.find((c) => c.type === 'lock')?.label || '上锁';
|
|
|
|
|
|
|
|
- return nodesToShowFinal.map(node => (
|
|
|
|
|
|
|
+ return nodesToShowFinal.map((node) => (
|
|
|
<Select.Option key={node.id} value={node.id}>
|
|
<Select.Option key={node.id} value={node.id}>
|
|
|
- {node.data?.label || nodeConfigs.find(c => c.type === 'isolation')?.label || '隔离/方案'}
|
|
|
|
|
|
|
+ {node.data?.label || defaultLabel}
|
|
|
</Select.Option>
|
|
</Select.Option>
|
|
|
));
|
|
));
|
|
|
})()}
|
|
})()}
|
|
@@ -3828,122 +3972,270 @@ export default function ProcessDesigner() {
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
- {/* 隔离方式 - 隔离方案节点可编辑,解除隔离节点只读(根据选择的隔离节点自动填充) */}
|
|
|
|
|
- <div>
|
|
|
|
|
- <label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
- 隔离方式
|
|
|
|
|
- </label>
|
|
|
|
|
- <Select
|
|
|
|
|
- value={nodeConfig.isolationType || undefined}
|
|
|
|
|
- onChange={(value) =>
|
|
|
|
|
- setNodeConfig({ ...nodeConfig, isolationType: value || '' })
|
|
|
|
|
- }
|
|
|
|
|
- placeholder="请选择隔离方式"
|
|
|
|
|
- className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
|
|
|
|
|
- allowClear
|
|
|
|
|
- disabled={selectedNode.data?.type === 'releaseIsolation'}
|
|
|
|
|
- >
|
|
|
|
|
- {isolationTypeDictList.map((item) => (
|
|
|
|
|
- <Select.Option key={item.id} value={item.value}>
|
|
|
|
|
- {item.label}
|
|
|
|
|
- </Select.Option>
|
|
|
|
|
- ))}
|
|
|
|
|
- </Select>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- {/* 隔离点选择(可多选)- 隔离方案节点显示,解除隔离只读展示 */}
|
|
|
|
|
- {(selectedNode.data?.type === 'isolation' || selectedNode.data?.type === 'releaseIsolation') && (
|
|
|
|
|
|
|
+ {isWorkflowUnlockCoLockType(selectedDataType) &&
|
|
|
|
|
+ !!nodeConfig.isolationNodeUuid && (
|
|
|
<div>
|
|
<div>
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
- 隔离点选择(可多选)
|
|
|
|
|
|
|
+ 共锁人(可多选)
|
|
|
</label>
|
|
</label>
|
|
|
<Select
|
|
<Select
|
|
|
mode="multiple"
|
|
mode="multiple"
|
|
|
- value={nodeConfig.isolationPoints}
|
|
|
|
|
|
|
+ value={nodeConfig.coLockPersons}
|
|
|
onChange={(value) =>
|
|
onChange={(value) =>
|
|
|
- setNodeConfig({ ...nodeConfig, isolationPoints: value })
|
|
|
|
|
|
|
+ setNodeConfig({ ...nodeConfig, coLockPersons: value })
|
|
|
}
|
|
}
|
|
|
- placeholder="请选择隔离点"
|
|
|
|
|
|
|
+ placeholder="请选择共锁人"
|
|
|
className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!min-h-10"
|
|
className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!min-h-10"
|
|
|
allowClear
|
|
allowClear
|
|
|
- disabled={selectedNode.data?.type === 'releaseIsolation'}
|
|
|
|
|
|
|
+ disabled={isUnlockDesignerReadOnly}
|
|
|
>
|
|
>
|
|
|
- {isolationPoints.map((point: any, index) => (
|
|
|
|
|
- <Select.Option key={point.pointId || point.id || `point-${index}`} value={point.pointId || point.id}>{point.pointName}</Select.Option>
|
|
|
|
|
|
|
+ {colockerUsers.map((user) => (
|
|
|
|
|
+ <Select.Option key={user.id} value={user.id}>
|
|
|
|
|
+ {user.nickname || user.username}
|
|
|
|
|
+ </Select.Option>
|
|
|
))}
|
|
))}
|
|
|
</Select>
|
|
</Select>
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
- {/* 盲板和拆除:显示负责人 */}
|
|
|
|
|
- {/* 字典值:0=盲板,1=上锁挂牌,2=拆除 */}
|
|
|
|
|
- {(nodeConfig.isolationType === '0' || nodeConfig.isolationType === '2') && (
|
|
|
|
|
- <div>
|
|
|
|
|
- <label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
- 负责人
|
|
|
|
|
- </label>
|
|
|
|
|
- <Select
|
|
|
|
|
- value={nodeConfig.responsible || undefined}
|
|
|
|
|
- onChange={(value) =>
|
|
|
|
|
- setNodeConfig({ ...nodeConfig, responsible: value || '' })
|
|
|
|
|
- }
|
|
|
|
|
- placeholder="请选择负责人"
|
|
|
|
|
- className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
|
|
|
|
|
- allowClear
|
|
|
|
|
- disabled={selectedNode.data?.type === 'releaseIsolation'}
|
|
|
|
|
- >
|
|
|
|
|
- {drawerUsers.map(user => (
|
|
|
|
|
- <Select.Option key={user.id} value={user.id}>{user.nickname || user.username}</Select.Option>
|
|
|
|
|
- ))}
|
|
|
|
|
- </Select>
|
|
|
|
|
- <p className="text-xs text-gray-500 mt-1.5 leading-relaxed">
|
|
|
|
|
- 对该任务或步骤节点进行处理的人员,若不选则需要在创建作业时进行选择。
|
|
|
|
|
- </p>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ {isWorkflowCoLockType(selectedDataType) && (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
+ 选择上锁任务{' '}
|
|
|
|
|
+ <span className="text-red-500" style={{ color: '#ef4444' }}>*</span>
|
|
|
|
|
+ </label>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ value={nodeConfig.isolationNodeUuid || undefined}
|
|
|
|
|
+ onChange={(value) => {
|
|
|
|
|
+ setNodeConfig((prev) => {
|
|
|
|
|
+ const target = nodes.find(
|
|
|
|
|
+ (n) =>
|
|
|
|
|
+ n.id === value && isWorkflowLockSchemeType(n.data?.type)
|
|
|
|
|
+ );
|
|
|
|
|
+ if (target) {
|
|
|
|
|
+ const cache = loadNodeCache(target.id);
|
|
|
|
|
+ const source = cache || target.data || {};
|
|
|
|
|
+ const resp =
|
|
|
|
|
+ source.workerUserId !== undefined &&
|
|
|
|
|
+ source.workerUserId !== null &&
|
|
|
|
|
+ source.workerUserId !== ''
|
|
|
|
|
+ ? String(source.workerUserId)
|
|
|
|
|
+ : source.responsible || '';
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...prev,
|
|
|
|
|
+ isolationNodeUuid: value || '',
|
|
|
|
|
+ isolationType: source.isolationType || '',
|
|
|
|
|
+ isolationPoints: source.isolationPoints || [],
|
|
|
|
|
+ isolationNode: Array.isArray(source.isolationNode)
|
|
|
|
|
+ ? source.isolationNode
|
|
|
|
|
+ : source.isolationNode
|
|
|
|
|
+ ? [source.isolationNode]
|
|
|
|
|
+ : [],
|
|
|
|
|
+ responsible: resp,
|
|
|
|
|
+ lockPerson: source.lockPerson || '',
|
|
|
|
|
+ coLockPersons: prev.coLockPersons,
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ return { ...prev, isolationNodeUuid: value || '' };
|
|
|
|
|
+ });
|
|
|
|
|
+ }}
|
|
|
|
|
+ placeholder="请选择上锁任务"
|
|
|
|
|
+ className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
|
|
|
|
|
+ allowClear
|
|
|
|
|
+ >
|
|
|
|
|
+ {(() => {
|
|
|
|
|
+ const currentNodeId = selectedNode?.id;
|
|
|
|
|
+ if (!currentNodeId) return [];
|
|
|
|
|
+
|
|
|
|
|
+ let queue = [String(currentNodeId)];
|
|
|
|
|
+ const upstreamIds = new Set<string>();
|
|
|
|
|
+
|
|
|
|
|
+ while (queue.length > 0) {
|
|
|
|
|
+ const nextQueue: string[] = [];
|
|
|
|
|
+ for (const id of queue) {
|
|
|
|
|
+ edges.forEach((edge) => {
|
|
|
|
|
+ if (String(edge.target) !== id) return;
|
|
|
|
|
+ const src = String(edge.source);
|
|
|
|
|
+ if (upstreamIds.has(src)) return;
|
|
|
|
|
+ upstreamIds.add(src);
|
|
|
|
|
+ nextQueue.push(src);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ queue = nextQueue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const schemeNodes = nodes.filter((n) =>
|
|
|
|
|
+ isWorkflowLockSchemeType(n.data?.type)
|
|
|
|
|
+ );
|
|
|
|
|
+ const nodesToShow = schemeNodes.filter((n) =>
|
|
|
|
|
+ upstreamIds.has(String(n.id))
|
|
|
|
|
+ );
|
|
|
|
|
+ const nodesToShowFinal =
|
|
|
|
|
+ nodesToShow.length > 0 ? nodesToShow : schemeNodes;
|
|
|
|
|
+ const defaultLabel =
|
|
|
|
|
+ nodeConfigs.find((c) => c.type === 'lock')?.label || '上锁';
|
|
|
|
|
+
|
|
|
|
|
+ return nodesToShowFinal.map((node) => (
|
|
|
|
|
+ <Select.Option key={node.id} value={node.id}>
|
|
|
|
|
+ {node.data?.label || defaultLabel}
|
|
|
|
|
+ </Select.Option>
|
|
|
|
|
+ ));
|
|
|
|
|
+ })()}
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
+ 共锁人(可多选)
|
|
|
|
|
+ </label>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ mode="multiple"
|
|
|
|
|
+ value={nodeConfig.coLockPersons}
|
|
|
|
|
+ onChange={(value) =>
|
|
|
|
|
+ setNodeConfig({ ...nodeConfig, coLockPersons: value })
|
|
|
|
|
+ }
|
|
|
|
|
+ placeholder="请选择共锁人"
|
|
|
|
|
+ className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!min-h-10"
|
|
|
|
|
+ allowClear
|
|
|
|
|
+ >
|
|
|
|
|
+ {colockerUsers.map((user) => (
|
|
|
|
|
+ <Select.Option key={user.id} value={user.id}>
|
|
|
|
|
+ {user.nickname || user.username}
|
|
|
|
|
+ </Select.Option>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
- {/* 上锁挂牌:显示上锁人和共锁人 */}
|
|
|
|
|
- {nodeConfig.isolationType === '1' && (
|
|
|
|
|
|
|
+ {!isWorkflowCoLockType(selectedDataType) &&
|
|
|
|
|
+ !isWorkflowUnlockCoLockType(selectedDataType) && (
|
|
|
<>
|
|
<>
|
|
|
<div>
|
|
<div>
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
- 上锁人
|
|
|
|
|
|
|
+ 隔离方式
|
|
|
</label>
|
|
</label>
|
|
|
<Select
|
|
<Select
|
|
|
- value={nodeConfig.lockPerson || undefined}
|
|
|
|
|
|
|
+ value={nodeConfig.isolationType || undefined}
|
|
|
onChange={(value) =>
|
|
onChange={(value) =>
|
|
|
- setNodeConfig({ ...nodeConfig, lockPerson: value || '' })
|
|
|
|
|
|
|
+ setNodeConfig({ ...nodeConfig, isolationType: value || '' })
|
|
|
}
|
|
}
|
|
|
- placeholder="请选择上锁人"
|
|
|
|
|
|
|
+ placeholder="请选择隔离方式"
|
|
|
className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
|
|
className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
|
|
|
allowClear
|
|
allowClear
|
|
|
- disabled={selectedNode.data?.type === 'releaseIsolation'}
|
|
|
|
|
|
|
+ disabled={isUnlockDesignerReadOnly}
|
|
|
>
|
|
>
|
|
|
- {lockerUsers.map(user => (
|
|
|
|
|
- <Select.Option key={user.id} value={user.id}>{user.nickname || user.username}</Select.Option>
|
|
|
|
|
|
|
+ {isolationTypeDictList.map((item) => (
|
|
|
|
|
+ <Select.Option key={item.id} value={item.value}>
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </Select.Option>
|
|
|
))}
|
|
))}
|
|
|
</Select>
|
|
</Select>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
<div>
|
|
<div>
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
- 共锁人(可多选)
|
|
|
|
|
|
|
+ 隔离点选择(可多选)
|
|
|
</label>
|
|
</label>
|
|
|
<Select
|
|
<Select
|
|
|
mode="multiple"
|
|
mode="multiple"
|
|
|
- value={nodeConfig.coLockPersons}
|
|
|
|
|
|
|
+ value={nodeConfig.isolationPoints}
|
|
|
onChange={(value) =>
|
|
onChange={(value) =>
|
|
|
- setNodeConfig({ ...nodeConfig, coLockPersons: value })
|
|
|
|
|
|
|
+ setNodeConfig({ ...nodeConfig, isolationPoints: value })
|
|
|
}
|
|
}
|
|
|
- placeholder="请选择共锁人"
|
|
|
|
|
|
|
+ placeholder="请选择隔离点"
|
|
|
className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!min-h-10"
|
|
className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!min-h-10"
|
|
|
allowClear
|
|
allowClear
|
|
|
- disabled={selectedNode.data?.type === 'releaseIsolation'}
|
|
|
|
|
|
|
+ disabled={isUnlockDesignerReadOnly}
|
|
|
>
|
|
>
|
|
|
- {colockerUsers.map(user => (
|
|
|
|
|
- <Select.Option key={user.id} value={user.id}>{user.nickname || user.username}</Select.Option>
|
|
|
|
|
|
|
+ {isolationPoints.map((point: any, index) => (
|
|
|
|
|
+ <Select.Option
|
|
|
|
|
+ key={point.pointId || point.id || `point-${index}`}
|
|
|
|
|
+ value={point.pointId || point.id}
|
|
|
|
|
+ >
|
|
|
|
|
+ {point.pointName}
|
|
|
|
|
+ </Select.Option>
|
|
|
))}
|
|
))}
|
|
|
</Select>
|
|
</Select>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+ {(nodeConfig.isolationType === '0' || nodeConfig.isolationType === '2') && (
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
+ 负责人
|
|
|
|
|
+ </label>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ value={nodeConfig.responsible || undefined}
|
|
|
|
|
+ onChange={(value) =>
|
|
|
|
|
+ setNodeConfig({ ...nodeConfig, responsible: value || '' })
|
|
|
|
|
+ }
|
|
|
|
|
+ placeholder="请选择负责人"
|
|
|
|
|
+ className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
|
|
|
|
|
+ allowClear
|
|
|
|
|
+ disabled={isUnlockDesignerReadOnly}
|
|
|
|
|
+ >
|
|
|
|
|
+ {drawerUsers.map((user) => (
|
|
|
|
|
+ <Select.Option key={user.id} value={user.id}>
|
|
|
|
|
+ {user.nickname || user.username}
|
|
|
|
|
+ </Select.Option>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ <p className="text-xs text-gray-500 mt-1.5 leading-relaxed">
|
|
|
|
|
+ 对该任务或步骤节点进行处理的人员,若不选则需要在创建作业时进行选择。
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+
|
|
|
|
|
+ {nodeConfig.isolationType === '1' && (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
+ 上锁人
|
|
|
|
|
+ </label>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ value={nodeConfig.lockPerson || undefined}
|
|
|
|
|
+ onChange={(value) =>
|
|
|
|
|
+ setNodeConfig({ ...nodeConfig, lockPerson: value || '' })
|
|
|
|
|
+ }
|
|
|
|
|
+ placeholder="请选择上锁人"
|
|
|
|
|
+ className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
|
|
|
|
|
+ allowClear
|
|
|
|
|
+ disabled={isUnlockDesignerReadOnly}
|
|
|
|
|
+ >
|
|
|
|
|
+ {lockerUsers.map((user) => (
|
|
|
|
|
+ <Select.Option key={user.id} value={user.id}>
|
|
|
|
|
+ {user.nickname || user.username}
|
|
|
|
|
+ </Select.Option>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {!isWorkflowLockSchemeType(selectedDataType) &&
|
|
|
|
|
+ !isWorkflowUnlockSchemeType(selectedDataType) && (
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <label className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
+ 共锁人(可多选)
|
|
|
|
|
+ </label>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ mode="multiple"
|
|
|
|
|
+ value={nodeConfig.coLockPersons}
|
|
|
|
|
+ onChange={(value) =>
|
|
|
|
|
+ setNodeConfig({ ...nodeConfig, coLockPersons: value })
|
|
|
|
|
+ }
|
|
|
|
|
+ placeholder="请选择共锁人"
|
|
|
|
|
+ className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!min-h-10"
|
|
|
|
|
+ allowClear
|
|
|
|
|
+ disabled={isUnlockDesignerReadOnly}
|
|
|
|
|
+ >
|
|
|
|
|
+ {colockerUsers.map((user) => (
|
|
|
|
|
+ <Select.Option key={user.id} value={user.id}>
|
|
|
|
|
+ {user.nickname || user.username}
|
|
|
|
|
+ </Select.Option>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </>
|
|
|
|
|
+ )}
|
|
|
</>
|
|
</>
|
|
|
)}
|
|
)}
|
|
|
</>
|
|
</>
|