Jelajahi Sumber

Merge branch 'develop' of http://192.168.0.253:3000/bozzysadmb/ISCS-Client-V1.0 into develop

wyn 4 bulan lalu
induk
melakukan
2c490bac66
2 mengubah file dengan 127 tambahan dan 43 penghapusan
  1. 89 5
      src/components/IsolationWork.tsx
  2. 38 38
      src/components/ProcessDesigner.tsx

+ 89 - 5
src/components/IsolationWork.tsx

@@ -134,6 +134,53 @@ const nodeConfigs = [
   },
 ];
 
+// 使用 import.meta.glob 动态导入所有图标(用于节点显示)
+const iconModulesForNode = import.meta.glob('../assets/节点图标/**/*.png', { eager: true, as: 'url' });
+
+// 根据文件名获取图标路径(用于节点显示)
+const getIconPathByFileName = (fileName: string | undefined): string | null => {
+  if (!fileName) return null;
+  
+  // 如果已经是完整路径,尝试提取文件名
+  let actualFileName = fileName;
+  if (fileName.includes('/') || fileName.includes('\\')) {
+    // 从路径中提取文件名
+    const pathParts = fileName.replace(/\\/g, '/').split('/');
+    actualFileName = pathParts[pathParts.length - 1];
+  }
+  
+  // 从文件名中提取数字和分类
+  const match = actualFileName.match(/^(\d+)\.png$/);
+  if (!match) return null;
+  
+  const iconNumber = parseInt(match[1], 10);
+  
+  // 根据数字范围判断分类
+  let category = '';
+  if (iconNumber >= 1000 && iconNumber <= 1011) category = '审核';
+  else if (iconNumber >= 2000 && iconNumber <= 2016) category = '开始';
+  else if (iconNumber >= 3000 && iconNumber <= 3028) category = '录入';
+  else if (iconNumber >= 4000 && iconNumber <= 4024) category = '确认';
+  else if (iconNumber >= 5000 && iconNumber <= 5018) category = '结束';
+  else if (iconNumber >= 6000 && iconNumber <= 6027) category = '能量隔离';
+  else if (iconNumber >= 7000 && iconNumber <= 7021) category = '解除隔离';
+  
+  if (!category) return null;
+  
+  // 查找对应的路径
+  const allKeys = Object.keys(iconModulesForNode);
+  const matchingKey = allKeys.find(k => {
+    const normalizedKey = k.replace(/\\/g, '/').toLowerCase();
+    return normalizedKey.includes(category.toLowerCase()) && normalizedKey.endsWith(actualFileName.toLowerCase());
+  });
+  
+  if (matchingKey) {
+    return iconModulesForNode[matchingKey] as string;
+  }
+  
+  return null;
+};
+
 // 自定义节点组件
 function CustomNode({ data, selected, id }: any) {
   const config = nodeConfigs.find(c => c.type === data.type);
@@ -141,6 +188,25 @@ function CustomNode({ data, selected, id }: any) {
   const nodeId = data.nodeId || (id ? String(parseInt(id.split('-').pop() || '0') % 1000).padStart(3, '0') : '001');
   const isCompleted = data.completed || false;
   
+  // 检查是否是图片文件名(如 "1000.png")
+  let iconImagePath: string | null = null;
+  // 支持多种格式:纯文件名(如 "1000.png")或完整路径
+  if (data.icon) {
+    // 如果是纯文件名格式
+    if (/^\d+\.png$/.test(data.icon)) {
+      iconImagePath = getIconPathByFileName(data.icon);
+    } 
+    // 如果已经是完整路径(兼容旧数据)
+    else if (data.icon.includes('/') || data.icon.includes('\\')) {
+      // 尝试从路径中提取文件名
+      const pathParts = data.icon.replace(/\\/g, '/').split('/');
+      const fileName = pathParts[pathParts.length - 1];
+      if (/^\d+\.png$/.test(fileName)) {
+        iconImagePath = getIconPathByFileName(fileName);
+      }
+    }
+  }
+  
   // 确定边框颜色:选中 > 已完成 > 默认
   let borderClass = 'border-gray-200 hover:border-gray-300';
   let borderStyle: React.CSSProperties = { borderWidth: '2px' };
@@ -228,10 +294,23 @@ function CustomNode({ data, selected, id }: any) {
       
       <div className="flex flex-col items-center justify-between gap-3 h-full">
         <div className={`${config?.bgColor || 'bg-gray-50'} ${config?.borderColor || 'border-gray-100'} border p-3 rounded-xl shadow-sm flex items-center justify-center flex-shrink-0`} style={{ borderRadius: '12px' }}>
-          <Icon 
-            className={`${config?.iconColor || 'text-gray-600'} text-2xl`} 
-            style={{ color: config?.iconColorCustom || undefined }}
-          />
+          {iconImagePath ? (
+            <img 
+              src={iconImagePath} 
+              alt={data.icon || '节点图标'}
+              className="w-6 h-6 object-contain"
+              onError={(e) => {
+                // 如果图片加载失败,回退到默认图标
+                console.error('节点图标加载失败:', iconImagePath);
+                (e.target as HTMLImageElement).style.display = 'none';
+              }}
+            />
+          ) : (
+            <Icon 
+              className={`${config?.iconColor || 'text-gray-600'} text-2xl`} 
+              style={{ color: config?.iconColorCustom || undefined }}
+            />
+          )}
         </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">
           {data.label || config?.label}
@@ -994,14 +1073,18 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
           const nodeData = node.data || {};
           const nodeId = node.id || node.uuid;
           
+          // 获取图标:优先使用 nodeIcon(顶层),其次使用 data.icon
+          const iconValue = node.nodeIcon || nodeData.icon || '';
+          
           return {
             id: nodeId,
             type: node.type || 'createJob',
             position: node.position || { x: 0, y: 0 },
             data: {
               ...nodeData,
-              label: nodeData.label || node.label || nodeConfigs.find(c => c.type === (node.type || nodeData.type))?.label || '节点',
+              label: nodeData.label || node.label || node.nodeName || nodeConfigs.find(c => c.type === (node.type || nodeData.type))?.label || '节点',
               type: node.type || nodeData.type || 'createJob',
+              icon: iconValue, // 确保icon字段被正确传递
             },
           };
         });
@@ -1136,6 +1219,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
         data: {
           label: node.label,
           type: node.type,
+          icon: node.icon || node.nodeIcon || '', // 确保icon字段被传递
         },
       }));
       

+ 38 - 38
src/components/ProcessDesigner.tsx

@@ -544,10 +544,10 @@ function CustomNode({ data, selected, id }: any) {
               }}
             />
           ) : (
-            <Icon 
+          <Icon 
               className={`${config?.iconColor || 'text-gray-600'} text-2xl`} 
-              style={{ color: config?.iconColorCustom || undefined }}
-            />
+            style={{ color: config?.iconColorCustom || undefined }}
+          />
           )}
         </div>
         {/* 中间:节点名称 */}
@@ -1319,9 +1319,9 @@ export default function ProcessDesigner() {
         if (hasApiContent && hasCacheContent) {
           Modal.confirm({
             title: '检测到未保存的设计',
-            content: '发现您之前设计的内容在缓存中,是否使用缓存中的内容替换当前内容?',
-            okText: '使用缓存',
-            cancelText: '使用接口内容',
+            content: '检测到您有未保存的设计稿,需要恢复继续设计吗?',
+            okText: '恢复',
+            cancelText: '不用',
             onOk: () => {
               // 使用缓存内容
               loadJsonToCanvas(cachedContent, true);
@@ -2519,7 +2519,7 @@ export default function ProcessDesigner() {
                             <Popover
                               content={
                                 <div style={{ width: '400px', maxHeight: '600px', overflowY: 'auto', padding: '12px' }}>
-                                  {Object.keys(iconCategories).map((category) => {
+                                  {['开始', '确认', '审核', '录入', '能量隔离', '解除隔离', '结束'].map((category) => {
                                     const iconPaths = generateIconPaths(category);
                                     return (
                                       <div key={category} className="mb-6">
@@ -2533,38 +2533,38 @@ export default function ProcessDesigner() {
                                           <div className="grid grid-cols-5 gap-3" style={{ gridTemplateColumns: 'repeat(5, 1fr)' }}>
                                           {iconPaths.map((iconItem) => {
                                             const isSelected = nodeConfig.nodeIcon === iconItem.fileName;
-                                            return (
-                                              <div
+                                      return (
+                                        <div
                                                 key={iconItem.id}
-                                                onClick={() => {
+                                          onClick={() => {
                                                   // 保存文件名而不是路径
                                                   setNodeConfig({ ...nodeConfig, nodeIcon: iconItem.fileName });
-                                                  setIconPickerOpen(false);
-                                                  // 立即更新节点显示
-                                                  setNodes((nds) => {
-                                                    return nds.map((node) => {
-                                                      if (node.id === selectedNode.id) {
-                                                        return {
-                                                          ...node,
-                                                          data: {
-                                                            ...node.data,
+                                            setIconPickerOpen(false);
+                                            // 立即更新节点显示
+                                            setNodes((nds) => {
+                                              return nds.map((node) => {
+                                                if (node.id === selectedNode.id) {
+                                                  return {
+                                                    ...node,
+                                                    data: {
+                                                      ...node.data,
                                                             icon: iconItem.fileName, // 保存文件名
-                                                          },
-                                                        };
-                                                      }
-                                                      return node;
-                                                    });
-                                                  });
-                                                }}
-                                                className={`
+                                                    },
+                                                  };
+                                                }
+                                                return node;
+                                              });
+                                            });
+                                          }}
+                                          className={`
                                                   rounded-lg border-2 cursor-pointer transition-all
                                                   flex items-center justify-center overflow-hidden
-                                                  hover:border-blue-400 hover:bg-blue-50
-                                                  ${isSelected ? 'border-blue-500 bg-blue-50' : 'border-gray-200 bg-white'}
-                                                `}
+                                            hover:border-blue-400 hover:bg-blue-50
+                                            ${isSelected ? 'border-blue-500 bg-blue-50' : 'border-gray-200 bg-white'}
+                                          `}
                                                 style={{ width: '25px', height: '25px' }}
                                                 title={category}
-                                              >
+                                        >
                                                 <img 
                                                   src={iconItem.path} 
                                                   alt={category}
@@ -2591,10 +2591,10 @@ export default function ProcessDesigner() {
                                                       // console.log('ProcessDesigner: 图标加载成功', iconItem.path);
                                                     }}
                                                   />
-                                                </div>
-                                              );
-                                            })}
-                                          </div>
+                                        </div>
+                                      );
+                                    })}
+                                  </div>
                                         ) : (
                                           <div className="text-sm text-gray-500 py-4 text-center">
                                             该分类暂无图标(请检查控制台日志)
@@ -2638,9 +2638,9 @@ export default function ProcessDesigner() {
                                   // 否则使用默认图标
                                   let Icon = FileTextOutlined;
                                   let config = null;
-                                  const iconType = selectedNode.data?.type;
-                                  config = nodeConfigs.find(c => c.type === iconType);
-                                  Icon = config?.icon || FileTextOutlined;
+                                    const iconType = selectedNode.data?.type;
+                                    config = nodeConfigs.find(c => c.type === iconType);
+                                    Icon = config?.icon || FileTextOutlined;
                                   
                                   return (
                                     <>