Sfoglia il codice sorgente

新增盲板和拆除功能 修复部分bug

pm 3 mesi fa
parent
commit
87853a1cef

+ 2 - 0
src/components/Dashboard.tsx

@@ -2845,6 +2845,7 @@ export default function Dashboard() {
                           setOriginalConf('');
                           taskDetailForm.resetFields();
                         }}
+                        style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500 }}
                       >
                         {t('common.cancel')}
                       </Button>
@@ -2908,6 +2909,7 @@ export default function Dashboard() {
                             setSubmitLoading(false);
                           }
                         }}
+                        style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500, boxShadow: '0 4px 14px rgba(22, 119, 255, 0.35)' }}
                       >
                         {(() => {
                           const nodeType = String(taskDetailData?.type || taskDetailData?.nodeType || '').trim();

+ 2 - 0
src/components/ExecutorDashboard.tsx

@@ -2184,6 +2184,7 @@ export default function ExecutorDashboard() {
                           setOriginalConf('');
                           taskDetailForm.resetFields();
                         }}
+                        style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500 }}
                       >
                         {t('common.cancel')}
                       </Button>
@@ -2251,6 +2252,7 @@ export default function ExecutorDashboard() {
                             setSubmitLoading(false);
                           }
                         }}
+                        style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500, boxShadow: '0 4px 14px rgba(22, 119, 255, 0.35)' }}
                       >
                         {(() => {
                           const nodeType = String(taskDetailData?.type || taskDetailData?.nodeType || '').trim();

+ 336 - 59
src/components/MyTask.tsx

@@ -1,12 +1,13 @@
 import React, { useState, useEffect } from 'react';
 import { useNavigate } from 'react-router-dom';
 import { Eye, Search, RotateCcw } from 'lucide-react';
-import { Button, Space, Table as AntdTable, Input, message, Modal, Form as AntdForm, Card, Alert, Select, DatePicker, InputNumber, Switch, Radio, Checkbox, Cascader, Upload } from 'antd';
-import { UploadOutlined, LockOutlined, KeyOutlined, WarningOutlined, CheckCircleOutlined } from '@ant-design/icons';
+import { Button, Space, Table as AntdTable, Input, message, Modal, Form as AntdForm, Card, Alert, Select, DatePicker, InputNumber, Switch, Radio, Checkbox, Cascader, Upload, Image } from 'antd';
+import { UploadOutlined, LockOutlined, KeyOutlined, WarningOutlined, CheckCircleOutlined, BarcodeOutlined, SendOutlined } from '@ant-design/icons';
 import type { ColumnsType } from 'antd/es/table';
 import { toast } from 'sonner';
 import dayjs, { Dayjs } from 'dayjs';
 import { myTaskApi, MyTaskVO, MyTaskPageParam, PageResponse, MyTaskNodeDetailVO, UpdateNodeApprovalParam } from '../api/mytask';
+import { fileApi } from '../api/file';
 import { dateFormatter } from '../utils/formatTime';
 import { DICT_TYPE, getDictLabel } from '../utils/dict';
 import { setConfAndFields2, FormCreateData } from '../utils/formCreate';
@@ -222,7 +223,10 @@ export default function MyTask() {
   const [approvalComment, setApprovalComment] = useState(''); // 审核意见
   const [approvalLoading, setApprovalLoading] = useState(false); // 审核操作loading状态
   const [submitLoading, setSubmitLoading] = useState(false); // 提交操作loading状态
-  
+  const [isolationMethodDictList, setIsolationMethodDictList] = useState<any[]>([]);
+  const [isolationDeviceNumber, setIsolationDeviceNumber] = useState('');
+  const [isolationFileList, setIsolationFileList] = useState<any[]>([]);
+  const [isolationSubmitLoading, setIsolationSubmitLoading] = useState(false);
   
   // 组件挂载时打印调试信息
   useEffect(() => {
@@ -270,9 +274,25 @@ export default function MyTask() {
     }
   };
 
+  const getIsolationMethodDictList = async () => {
+    try {
+      const { dictDataApi } = await import('../api/DictData');
+      const response = await dictDataApi.getDictDataPage({
+        pageNo: 1,
+        pageSize: -1,
+        dictType: 'isolation_method',
+      });
+      const data = (response as any)?.data || response;
+      setIsolationMethodDictList(data?.list || []);
+    } catch (error: any) {
+      console.error('获取隔离方式字典失败:', error);
+    }
+  };
+
   useEffect(() => {
     getApprovalStatusDictList();
     getUrgencyLevelDictList();
+    getIsolationMethodDictList();
   }, []);
   
   /** 查询列表 */
@@ -1041,6 +1061,15 @@ export default function MyTask() {
         'detailDataWithWorkInfo': detailDataWithWorkInfo,
       });
       
+      // 打开盲板/拆除详情时先清空设备编号和附件,后续若有 formData 再回填
+      if (isIsolation || isReleaseIsolation) {
+        const isolationType = String(detailDataWithWorkInfo.isolationType ?? '').trim();
+        if (isolationType === '0' || isolationType === '2') {
+          setIsolationDeviceNumber('');
+          setIsolationFileList([]);
+        }
+      }
+      
       // 如果任务状态为"已通过"且有 formData,则从 formData 中解析表单结构
       if (isApproved && hasFormData) {
         console.log('MyTask: ✅ 任务已通过,从 formData 中解析表单结构');
@@ -1149,6 +1178,23 @@ export default function MyTask() {
               detailForm.setFieldsValue(convertedFormValues);
               console.log('MyTask: 表单数据已回填(从 formData)', convertedFormValues);
             }, 100);
+          } else if (
+            parsedFormData.deviceNumber !== undefined ||
+            (Array.isArray(parsedFormData.attachments) && parsedFormData.attachments.length > 0)
+          ) {
+            // 盲板/拆除 表单回显:deviceNumber + attachments
+            setIsolationDeviceNumber(parsedFormData.deviceNumber ?? '');
+            const list = Array.isArray(parsedFormData.attachments) ? parsedFormData.attachments : [];
+            setIsolationFileList(
+              list.map((item: any, idx: number) => ({
+                uid: `echo-${idx}-${item.url || item.name || idx}`,
+                name: item.name || `文件${idx + 1}`,
+                url: item.url || item.response,
+                status: 'done',
+                response: item.url || item.response,
+              }))
+            );
+            console.log('MyTask: 盲板/拆除 formData 已回显', { deviceNumber: parsedFormData.deviceNumber, attachmentsCount: list.length });
           } else {
             console.warn('MyTask: formData 中缺少 conf 或 fields', { conf: !!conf, fields: !!fields });
             message.warning('表单数据不完整');
@@ -1813,6 +1859,8 @@ export default function MyTask() {
           setOriginalConf('');
           detailForm.resetFields();
           setApprovalComment('');
+          setIsolationDeviceNumber('');
+          setIsolationFileList([]);
         }}
         footer={null}
         width={1000}
@@ -1907,54 +1955,275 @@ export default function MyTask() {
 
                 // 隔离/方案节点、解除隔离节点和还锁节点
                 if (isIsolation || isReleaseIsolation || isReturnLock) {
-                  console.log('MyTask: ✅ 进入隔离节点渲染分支', { isIsolation, isReleaseIsolation, isReturnLock, nodeType });
+                  const isolationType = String(detailData?.isolationType ?? '').trim();
+                  const isLockoutTagout = isolationType === '1';
+                  const isBlindPlate = isolationType === '0';
+                  const isDismantle = isolationType === '2';
+                  const showLockCabinet = isReturnLock || (isLockoutTagout && (isIsolation || isReleaseIsolation));
+                  const showDeviceForm = (isIsolation || isReleaseIsolation) && (isBlindPlate || isDismantle);
+
+                  if (showDeviceForm) {
+                    const deviceLabel = isReleaseIsolation
+                      ? (isBlindPlate ? t('form.releaseBlindPlateDeviceNo') : t('form.restoreDeviceNo'))
+                      : (isBlindPlate ? t('form.blindPlateDeviceNo') : t('form.dismantleDeviceNo'));
+                    return (
+                      <div key="isolation-device-form" style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
+                        <div style={{ flex: 1, minHeight: 0, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
+                          <Card
+                            style={{
+                              width: '100%',
+                              maxWidth: 500,
+                              boxShadow: '0 8px 32px rgba(22, 119, 255, 0.12), 0 2px 8px rgba(0,0,0,0.04)',
+                              borderRadius: 16,
+                              border: '1px solid rgba(22, 119, 255, 0.15)',
+                              overflow: 'hidden',
+                              background: 'linear-gradient(180deg, #ffffff 0%, #fafcff 100%)',
+                            }}
+                            bodyStyle={{ padding: 0 }}
+                          >
+                            <div style={{ padding: '24px 32px 28px' }}>
+                              <div style={{ marginBottom: 26 }}>
+                                <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 14, fontWeight: 600, color: '#1f2937', marginBottom: 12 }}>
+                                  <span style={{ width: 28, height: 28, borderRadius: 8, background: '#e6f4ff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
+                                    <BarcodeOutlined style={{ fontSize: 14, color: '#1677ff' }} />
+                                  </span>
+                                  {deviceLabel}
+                                </label>
+                                <Input
+                                  value={isolationDeviceNumber}
+                                  onChange={(e) => !isApproved && setIsolationDeviceNumber(e.target.value)}
+                                  placeholder={`请输入${deviceLabel}`}
+                                  maxLength={100}
+                                  size="large"
+                                  disabled={isApproved}
+                                  prefix={<BarcodeOutlined style={{ color: '#91caff', marginRight: 8 }} />}
+                                  style={{ borderRadius: 10, borderColor: '#d9e8ff', fontSize: 15 }}
+                                />
+                              </div>
+                              <div>
+                                <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 14, fontWeight: 600, color: '#1f2937', marginBottom: 12 }}>
+                                  <span style={{ width: 28, height: 28, borderRadius: 8, background: '#e6f4ff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
+                                    <UploadOutlined style={{ fontSize: 14, color: '#1677ff' }} />
+                                  </span>
+                                  附件上传
+                                  {!isApproved && <span style={{ marginLeft: 8, fontSize: 12, fontWeight: 400, color: '#69b1ff', background: '#e6f4ff', padding: '2px 8px', borderRadius: 6 }}>支持拖拽</span>}
+                                </label>
+                                {isApproved && isolationFileList.length > 0 ? (
+                                  <div
+                                    style={{
+                                      borderRadius: 12,
+                                      padding: '32px 20px',
+                                      background: 'linear-gradient(180deg, #f5faff 0%, #e8f4ff 50%, #e0efff 100%)',
+                                      border: '2px solid #91caff',
+                                      minHeight: 180,
+                                      display: 'flex',
+                                      flexWrap: 'wrap',
+                                      gap: 16,
+                                      alignItems: 'center',
+                                      justifyContent: 'center',
+                                      alignContent: 'center',
+                                    }}
+                                  >
+                                    <Image.PreviewGroup>
+                                      {isolationFileList.filter((f: any) => {
+                                        const u = f.url || f.response;
+                                        return /\.(jpg|jpeg|png|gif|webp|bmp)(\?|$)/i.test(String(u || '')) || /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(String(f.name || ''));
+                                      }).map((file: any) => (
+                                        <Image
+                                          key={file.uid}
+                                          width={160}
+                                          height={160}
+                                          src={file.url || file.response}
+                                          style={{ objectFit: 'cover', borderRadius: 10, cursor: 'pointer', flexShrink: 0 }}
+                                          alt={file.name}
+                                        />
+                                      ))}
+                                    </Image.PreviewGroup>
+                                    {isolationFileList.filter((f: any) => {
+                                      const u = f.url || f.response;
+                                      return !/\.(jpg|jpeg|png|gif|webp|bmp)(\?|$)/i.test(String(u || '')) && !/\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(String(f.name || ''));
+                                    }).map((file: any) => (
+                                      <a
+                                        key={file.uid}
+                                        href={file.url || file.response}
+                                        target="_blank"
+                                        rel="noopener noreferrer"
+                                        style={{ display: 'block', padding: '12px 16px', background: '#fff', borderRadius: 10, color: '#1677ff', border: '1px solid #91caff', maxWidth: 240, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
+                                      >
+                                        {file.name}
+                                      </a>
+                                    ))}
+                                  </div>
+                                ) : isApproved ? (
+                                  <div style={{ padding: 24, textAlign: 'center', color: '#8c8c8c', background: '#fafafa', borderRadius: 12 }}>暂无附件</div>
+                                ) : (
+                                  <Upload.Dragger
+                                    fileList={isolationFileList}
+                                    onChange={({ fileList }) => setIsolationFileList(fileList.slice(-5))}
+                                    multiple
+                                    showUploadList={{ showRemoveIcon: true }}
+                                    customRequest={async ({ file, onSuccess, onError }) => {
+                                      try {
+                                        const url = await fileApi.upload(file as File);
+                                        onSuccess?.(url);
+                                      } catch (e: any) {
+                                        onError?.(e);
+                                        message.error(e?.message || '文件上传失败');
+                                      }
+                                    }}
+                                    style={{
+                                      borderRadius: 12,
+                                      padding: '32px 20px',
+                                      background: 'linear-gradient(180deg, #f5faff 0%, #e8f4ff 50%, #e0efff 100%)',
+                                      border: '2px dashed #91caff',
+                                    }}
+                                  >
+                                    <p className="ant-upload-drag-icon" style={{ marginBottom: 12 }}>
+                                      <span style={{ width: 64, height: 64, borderRadius: 16, background: 'linear-gradient(135deg, rgba(22,119,255,0.15) 0%, rgba(64,150,255,0.1) 100%)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
+                                        <UploadOutlined style={{ fontSize: 32, color: '#1677ff' }} />
+                                      </span>
+                                    </p>
+                                    <p className="ant-upload-text" style={{ margin: 0, color: '#0958d9', fontSize: 15, fontWeight: 500 }}>点击或拖拽文件到此区域上传</p>
+                                    <p className="ant-upload-hint" style={{ margin: '8px 0 0', color: '#69b1ff', fontSize: 13 }}>最多 5 个文件,支持多选</p>
+                                  </Upload.Dragger>
+                                )}
+                              </div>
+                            </div>
+                          </Card>
+                        </div>
+                        <div className="flex justify-end gap-3" style={{ padding: '20px 24px', borderTop: '1px solid rgba(22, 119, 255, 0.1)', flexShrink: 0, background: 'linear-gradient(0deg, #f0f7ff 0%, #fafcff 60%, #fff 100%)' }}>
+                          {!isApproved ? (
+                            <Button
+                              type="primary"
+                              loading={isolationSubmitLoading}
+                              onClick={async () => {
+                                const nodeId = detailData.nodeId || detailData.id;
+                                if (!nodeId) {
+                                  message.error('节点ID不存在');
+                                  return;
+                                }
+                                const uploadedFiles = isolationFileList.filter((f: any) => f.status === 'done' && (f.response != null));
+                                const uploadingCount = isolationFileList.filter((f: any) => f.status === 'uploading').length;
+                                if (uploadingCount > 0) {
+                                  message.warning('请等待文件上传完成');
+                                  return;
+                                }
+                                if (isolationFileList.length > 0 && uploadedFiles.length === 0) {
+                                  message.warning('请等待文件上传完成或移除未上传成功的文件');
+                                  return;
+                                }
+                                setIsolationSubmitLoading(true);
+                                try {
+                                  const attachmentUrls = uploadedFiles.map((f: any) => {
+                                    const url = typeof f.response === 'string' ? f.response : (f.response?.url ?? '');
+                                    return { name: f.name, url };
+                                  });
+                                  const payload = {
+                                    deviceNumber: isolationDeviceNumber,
+                                    attachments: attachmentUrls,
+                                  };
+                                  const formDataString = JSON.stringify(payload);
+                                  await myTaskApi.updateNodeApproval({
+                                    nodeId: typeof nodeId === 'number' ? nodeId : Number(nodeId),
+                                    approvalStatus: 'approved',
+                                    formData: formDataString,
+                                  });
+                                  message.success(t('common.submit') + t('common.success'));
+                                  setDetailVisible(false);
+                                  setDetailData(null);
+                                  setIsolationDeviceNumber('');
+                                  setIsolationFileList([]);
+                                  getList();
+                                } catch (error: any) {
+                                  message.error(error?.message || '提交失败');
+                                } finally {
+                                  setIsolationSubmitLoading(false);
+                                }
+                              }}
+                              icon={<SendOutlined />}
+                              style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500, boxShadow: '0 4px 14px rgba(22, 119, 255, 0.35)' }}
+                            >
+                              {t('common.submit')}
+                            </Button>
+                          ) : (
+                            <Button
+                              onClick={() => {
+                                setDetailVisible(false);
+                                setDetailData(null);
+                                setIsolationDeviceNumber('');
+                                setIsolationFileList([]);
+                              }}
+                              style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500 }}
+                            >
+                              {t('common.cancel')}
+                            </Button>
+                          )}
+                        </div>
+                      </div>
+                    );
+                  }
+
+                  console.log('MyTask: ✅ 进入隔离节点渲染分支', { isIsolation, isReleaseIsolation, isReturnLock, nodeType, isolationType });
                   const isolationContent = (
-                    <div 
+                    <div
                       key="isolation-content"
                       style={{
+                        flex: 1,
+                        minHeight: 0,
+                        overflow: 'hidden',
                         display: 'flex',
-                        justifyContent: 'center',
                         alignItems: 'center',
-                        minHeight: '400px',
-                        padding: '40px 20px'
+                        justifyContent: 'center',
+                        padding: '24px',
+                        background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)',
                       }}
                     >
                       <Card
                         style={{
                           width: '100%',
-                          maxWidth: '500px',
+                          maxWidth: 500,
                           textAlign: 'center',
-                          borderRadius: '12px',
-                          boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
-                          border: '1px solid #e8e8e8'
-                        }}
-                        bodyStyle={{
-                          padding: '40px 30px'
+                          borderRadius: 16,
+                          boxShadow: '0 8px 32px rgba(22, 119, 255, 0.12), 0 2px 8px rgba(0,0,0,0.04)',
+                          border: '1px solid rgba(22, 119, 255, 0.15)',
+                          background: 'linear-gradient(180deg, #ffffff 0%, #fafcff 100%)',
                         }}
+                        bodyStyle={{ padding: '48px 40px' }}
                       >
-                        <div style={{ marginBottom: '24px' }}>
-                          <LockOutlined 
-                            style={{ 
-                              fontSize: '64px', 
-                              color: '#1890ff',
-                              display: 'block'
-                            }} 
-                          />
+                        <div style={{ marginBottom: 28 }}>
+                          <span
+                            style={{
+                              width: 88,
+                              height: 88,
+                              borderRadius: 20,
+                              background: 'linear-gradient(135deg, #1677ff 0%, #4096ff 50%, #69b1ff 100%)',
+                              display: 'inline-flex',
+                              alignItems: 'center',
+                              justifyContent: 'center',
+                              boxShadow: '0 8px 24px rgba(22, 119, 255, 0.35)',
+                            }}
+                          >
+                            <LockOutlined style={{ fontSize: 42, color: '#fff' }} />
+                          </span>
                         </div>
                         <div
                           style={{
-                            fontSize: '20px',
-                            fontWeight: 500,
-                            color: '#333',
-                            lineHeight: '1.6'
+                            fontSize: 18,
+                            fontWeight: 600,
+                            color: '#1f2937',
+                            lineHeight: 1.6,
+                            marginBottom: 8,
                           }}
                         >
                           {t('form.lockCabinetTip')}
                         </div>
+                        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 13, color: '#69b1ff' }}>
+                          <KeyOutlined />
+                          <span>完成取锁、取钥匙后,任务将自动推进</span>
+                        </div>
                       </Card>
                     </div>
                   );
-                  console.log('MyTask: 隔离节点内容已创建', isolationContent);
                   return isolationContent;
                 }
 
@@ -2373,59 +2642,65 @@ export default function MyTask() {
                         })()}
                       </div>
                     ) : (
-                      <div 
+                      <div
                         style={{
+                          flex: 1,
+                          minHeight: 0,
+                          overflow: 'hidden',
                           display: 'flex',
-                          justifyContent: 'center',
                           alignItems: 'center',
-                          minHeight: '400px',
-                          padding: '40px 20px'
+                          justifyContent: 'center',
+                          padding: '24px',
+                          background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)',
                         }}
                       >
                         <Card
                           style={{
                             width: '100%',
-                            maxWidth: '500px',
+                            maxWidth: 500,
                             textAlign: 'center',
-                            borderRadius: '12px',
-                            boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
-                            border: '1px solid #e8e8e8'
-                          }}
-                          bodyStyle={{
-                            padding: '40px 30px'
+                            borderRadius: 16,
+                            boxShadow: '0 8px 32px rgba(22, 119, 255, 0.12), 0 2px 8px rgba(0,0,0,0.04)',
+                            border: '1px solid rgba(22, 119, 255, 0.15)',
+                            background: 'linear-gradient(180deg, #ffffff 0%, #fafcff 100%)',
                           }}
+                          bodyStyle={{ padding: '48px 40px' }}
                         >
-                          <div style={{ marginBottom: '24px' }}>
-                            <CheckCircleOutlined 
-                              style={{ 
-                                fontSize: '64px', 
-                                color: '#1890ff',
-                                display: 'block'
-                              }} 
-                            />
+                          <div style={{ marginBottom: 24 }}>
+                            <span
+                              style={{
+                                width: 88,
+                                height: 88,
+                                borderRadius: 20,
+                                background: 'linear-gradient(135deg, #52c41a 0%, #73d13d 50%, #95de64 100%)',
+                                display: 'inline-flex',
+                                alignItems: 'center',
+                                justifyContent: 'center',
+                                boxShadow: '0 8px 24px rgba(82, 196, 26, 0.35)',
+                              }}
+                            >
+                              <CheckCircleOutlined style={{ fontSize: 42, color: '#fff' }} />
+                            </span>
                           </div>
-                          <div
-                            style={{
-                              fontSize: '20px',
-                              fontWeight: 500,
-                              color: '#333',
-                              lineHeight: '1.6'
-                            }}
-                          >
+                          <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6 }}>
                             无需填写表单,直接点击底部按钮提交即可
                           </div>
+                          <div style={{ marginTop: 12, fontSize: 13, color: '#69b1ff' }}>
+                            确认无误后点击「完成」结束本节点
+                          </div>
                         </Card>
                       </div>
                     )}
 
                     </div>
                     {/* 底部按钮 */}
-                    <div 
+                    <div
                       className="flex justify-end gap-3"
                       style={{
-                        padding: '16px 24px',
-                        borderTop: '1px solid #f0f0f0',
-                        flexShrink: 0
+                        padding: '20px 24px',
+                        borderTop: '1px solid rgba(22, 119, 255, 0.1)',
+                        flexShrink: 0,
+                        background: 'linear-gradient(0deg, #f0f7ff 0%, #fafcff 60%, #fff 100%)',
                       }}
                     >
                       <Button
@@ -2437,6 +2712,7 @@ export default function MyTask() {
                           setOriginalConf('');
                           detailForm.resetFields();
                         }}
+                        style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500 }}
                       >
                         {t('common.cancel')}
                       </Button>
@@ -2531,6 +2807,7 @@ export default function MyTask() {
                             setSubmitLoading(false);
                           }
                         }}
+                        style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500, boxShadow: '0 4px 14px rgba(22, 119, 255, 0.35)' }}
                       >
                         {(() => {
                           // 从 detailData.type 或 detailData.nodeType 获取节点类型

+ 3 - 1
src/components/ProcessDesigner.tsx

@@ -2554,8 +2554,10 @@ export default function ProcessDesigner() {
   };
 
   // 返回(仅在有未保存修改时弹框:保存 或 放弃并返回)
+  // 同时用 checkUnsavedChanges() 实时检测,避免「导入 JSON 后只改了节点配置/未点保存」等路径未设 hasUnsavedChanges 导致不提示
   const handleBack = () => {
-    if (hasUnsavedChanges) {
+    const hasChange = hasUnsavedChanges || checkUnsavedChanges();
+    if (hasChange) {
       Modal.confirm({
         title: '未保存的更改',
         content: '检测到您有未保存的更改,请先保存后再返回,或放弃更改并返回。',

+ 340 - 59
src/components/TaskManagement.tsx

@@ -1,12 +1,13 @@
 import React, { useState, useEffect } from 'react';
 import { useNavigate } from 'react-router-dom';
 import { Eye, Search, RotateCcw } from 'lucide-react';
-import { Button, Space, Table as AntdTable, Input, message, Modal, Form as AntdForm, Card, Alert, Select, DatePicker, InputNumber, Switch, Radio, Checkbox, Cascader, Upload } from 'antd';
-import { UploadOutlined, LockOutlined, KeyOutlined, WarningOutlined, CheckCircleOutlined } from '@ant-design/icons';
+import { Button, Space, Table as AntdTable, Input, message, Modal, Form as AntdForm, Card, Alert, Select, DatePicker, InputNumber, Switch, Radio, Checkbox, Cascader, Upload, Image } from 'antd';
+import { UploadOutlined, LockOutlined, KeyOutlined, WarningOutlined, CheckCircleOutlined, BarcodeOutlined, SendOutlined } from '@ant-design/icons';
 import type { ColumnsType } from 'antd/es/table';
 import { toast } from 'sonner';
 import dayjs, { Dayjs } from 'dayjs';
 import { taskManagementApi, MyTaskVO, MyTaskPageParam, PageResponse, MyTaskNodeDetailVO, UpdateNodeApprovalParam } from '../api/mytask';
+import { fileApi } from '../api/file';
 import { dateFormatter } from '../utils/formatTime';
 import { DICT_TYPE, getDictLabel } from '../utils/dict';
 import { setConfAndFields2, FormCreateData } from '../utils/formCreate';
@@ -229,7 +230,12 @@ export default function TaskManagement() {
   const [approvalComment, setApprovalComment] = useState(''); // 审核意见
   const [approvalLoading, setApprovalLoading] = useState(false); // 审核操作loading状态
   const [submitLoading, setSubmitLoading] = useState(false); // 提交操作loading状态
-  
+  // 隔离方式字典(isolation_method:0=盲板,1=上锁挂牌,2=拆除)
+  const [isolationMethodDictList, setIsolationMethodDictList] = useState<any[]>([]);
+  // 盲板/拆除类型时的设备编号与附件(仅隔离/方案、解除隔离且隔离方式为盲板或拆除时使用)
+  const [isolationDeviceNumber, setIsolationDeviceNumber] = useState('');
+  const [isolationFileList, setIsolationFileList] = useState<any[]>([]);
+  const [isolationSubmitLoading, setIsolationSubmitLoading] = useState(false);
   
   // 组件挂载时打印调试信息,并从 sessionStorage 读取 status
   useEffect(() => {
@@ -288,9 +294,27 @@ export default function TaskManagement() {
     }
   };
 
+  // 获取隔离方式字典(isolation_method)
+  const getIsolationMethodDictList = async () => {
+    try {
+      const { dictDataApi } = await import('../api/DictData');
+      const response = await dictDataApi.getDictDataPage({
+        pageNo: 1,
+        pageSize: -1,
+        dictType: 'isolation_method',
+      });
+      const data = (response as any)?.data || response;
+      const dictList = data?.list || [];
+      setIsolationMethodDictList(dictList);
+    } catch (error: any) {
+      console.error('获取隔离方式字典失败:', error);
+    }
+  };
+
   useEffect(() => {
     getApprovalStatusDictList();
     getUrgencyLevelDictList();
+    getIsolationMethodDictList();
   }, []);
   
   /** 查询列表 */
@@ -1050,6 +1074,15 @@ export default function TaskManagement() {
         'detailDataWithWorkInfo': detailDataWithWorkInfo,
       });
       
+      // 打开盲板/拆除详情时先清空设备编号和附件,后续若有 formData 再回填
+      if (isIsolation || isReleaseIsolation) {
+        const isolationType = String(detailDataWithWorkInfo.isolationType ?? '').trim();
+        if (isolationType === '0' || isolationType === '2') {
+          setIsolationDeviceNumber('');
+          setIsolationFileList([]);
+        }
+      }
+      
       // 如果任务状态为"已通过"且有 formData,则从 formData 中解析表单结构
       if (isApproved && hasFormData) {
         console.log('TaskManagement: ✅ 任务已通过,从 formData 中解析表单结构');
@@ -1158,6 +1191,23 @@ export default function TaskManagement() {
               detailForm.setFieldsValue(convertedFormValues);
               console.log('TaskManagement: 表单数据已回填(从 formData)', convertedFormValues);
             }, 100);
+          } else if (
+            parsedFormData.deviceNumber !== undefined ||
+            (Array.isArray(parsedFormData.attachments) && parsedFormData.attachments.length > 0)
+          ) {
+            // 盲板/拆除 表单回显:deviceNumber + attachments
+            setIsolationDeviceNumber(parsedFormData.deviceNumber ?? '');
+            const list = Array.isArray(parsedFormData.attachments) ? parsedFormData.attachments : [];
+            setIsolationFileList(
+              list.map((item: any, idx: number) => ({
+                uid: `echo-${idx}-${item.url || item.name || idx}`,
+                name: item.name || `文件${idx + 1}`,
+                url: item.url || item.response,
+                status: 'done',
+                response: item.url || item.response,
+              }))
+            );
+            console.log('TaskManagement: 盲板/拆除 formData 已回显', { deviceNumber: parsedFormData.deviceNumber, attachmentsCount: list.length });
           } else {
             console.warn('TaskManagement: formData 中缺少 conf 或 fields', { conf: !!conf, fields: !!fields });
             message.warning('表单数据不完整');
@@ -1822,6 +1872,8 @@ export default function TaskManagement() {
           setOriginalConf('');
           detailForm.resetFields();
           setApprovalComment('');
+          setIsolationDeviceNumber('');
+          setIsolationFileList([]);
         }}
         footer={null}
         width={1000}
@@ -1916,54 +1968,275 @@ export default function TaskManagement() {
 
                 // 隔离/方案节点、解除隔离节点和还锁节点
                 if (isIsolation || isReleaseIsolation || isReturnLock) {
-                  console.log('TaskManagement: ✅ 进入隔离节点渲染分支', { isIsolation, isReleaseIsolation, isReturnLock, nodeType });
+                  const isolationType = String(detailData?.isolationType ?? '').trim();
+                  const isLockoutTagout = isolationType === '1'; // 上锁挂牌
+                  const isBlindPlate = isolationType === '0';   // 盲板
+                  const isDismantle = isolationType === '2';    // 拆除
+                  const showLockCabinet = isReturnLock || (isLockoutTagout && (isIsolation || isReleaseIsolation));
+                  const showDeviceForm = (isIsolation || isReleaseIsolation) && (isBlindPlate || isDismantle);
+
+                  if (showDeviceForm) {
+                    const deviceLabel = isReleaseIsolation
+                      ? (isBlindPlate ? t('form.releaseBlindPlateDeviceNo') : t('form.restoreDeviceNo'))
+                      : (isBlindPlate ? t('form.blindPlateDeviceNo') : t('form.dismantleDeviceNo'));
+                    return (
+                      <div key="isolation-device-form" style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
+                        <div style={{ flex: 1, minHeight: 0, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
+                          <Card
+                            style={{
+                              width: '100%',
+                              maxWidth: 500,
+                              boxShadow: '0 8px 32px rgba(22, 119, 255, 0.12), 0 2px 8px rgba(0,0,0,0.04)',
+                              borderRadius: 16,
+                              border: '1px solid rgba(22, 119, 255, 0.15)',
+                              overflow: 'hidden',
+                              background: 'linear-gradient(180deg, #ffffff 0%, #fafcff 100%)',
+                            }}
+                            bodyStyle={{ padding: 0 }}
+                          >
+                            <div style={{ padding: '24px 32px 28px' }}>
+                              <div style={{ marginBottom: 26 }}>
+                                <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 14, fontWeight: 600, color: '#1f2937', marginBottom: 12 }}>
+                                  <span style={{ width: 28, height: 28, borderRadius: 8, background: '#e6f4ff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
+                                    <BarcodeOutlined style={{ fontSize: 14, color: '#1677ff' }} />
+                                  </span>
+                                  {deviceLabel}
+                                </label>
+                                <Input
+                                  value={isolationDeviceNumber}
+                                  onChange={(e) => !isApproved && setIsolationDeviceNumber(e.target.value)}
+                                  placeholder={`请输入${deviceLabel}`}
+                                  maxLength={100}
+                                  size="large"
+                                  disabled={isApproved}
+                                  prefix={<BarcodeOutlined style={{ color: '#91caff', marginRight: 8 }} />}
+                                  style={{ borderRadius: 10, borderColor: '#d9e8ff', fontSize: 15 }}
+                                />
+                              </div>
+                              <div>
+                                <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 14, fontWeight: 600, color: '#1f2937', marginBottom: 12 }}>
+                                  <span style={{ width: 28, height: 28, borderRadius: 8, background: '#e6f4ff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
+                                    <UploadOutlined style={{ fontSize: 14, color: '#1677ff' }} />
+                                  </span>
+                                  附件上传
+                                  {!isApproved && <span style={{ marginLeft: 8, fontSize: 12, fontWeight: 400, color: '#69b1ff', background: '#e6f4ff', padding: '2px 8px', borderRadius: 6 }}>支持拖拽</span>}
+                                </label>
+                                {isApproved && isolationFileList.length > 0 ? (
+                                  <div
+                                    style={{
+                                      borderRadius: 12,
+                                      padding: '32px 20px',
+                                      background: 'linear-gradient(180deg, #f5faff 0%, #e8f4ff 50%, #e0efff 100%)',
+                                      border: '2px solid #91caff',
+                                      minHeight: 180,
+                                      display: 'flex',
+                                      flexWrap: 'wrap',
+                                      gap: 16,
+                                      alignItems: 'center',
+                                      justifyContent: 'center',
+                                      alignContent: 'center',
+                                    }}
+                                  >
+                                    <Image.PreviewGroup>
+                                      {isolationFileList.filter((f: any) => {
+                                        const u = f.url || f.response;
+                                        return /\.(jpg|jpeg|png|gif|webp|bmp)(\?|$)/i.test(String(u || '')) || /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(String(f.name || ''));
+                                      }).map((file: any) => (
+                                        <Image
+                                          key={file.uid}
+                                          width={160}
+                                          height={160}
+                                          src={file.url || file.response}
+                                          style={{ objectFit: 'cover', borderRadius: 10, cursor: 'pointer', flexShrink: 0 }}
+                                          alt={file.name}
+                                        />
+                                      ))}
+                                    </Image.PreviewGroup>
+                                    {isolationFileList.filter((f: any) => {
+                                      const u = f.url || f.response;
+                                      return !/\.(jpg|jpeg|png|gif|webp|bmp)(\?|$)/i.test(String(u || '')) && !/\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(String(f.name || ''));
+                                    }).map((file: any) => (
+                                      <a
+                                        key={file.uid}
+                                        href={file.url || file.response}
+                                        target="_blank"
+                                        rel="noopener noreferrer"
+                                        style={{ display: 'block', padding: '12px 16px', background: '#fff', borderRadius: 10, color: '#1677ff', border: '1px solid #91caff', maxWidth: 240, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
+                                      >
+                                        {file.name}
+                                      </a>
+                                    ))}
+                                  </div>
+                                ) : isApproved ? (
+                                  <div style={{ padding: 24, textAlign: 'center', color: '#8c8c8c', background: '#fafafa', borderRadius: 12 }}>暂无附件</div>
+                                ) : (
+                                  <Upload.Dragger
+                                    fileList={isolationFileList}
+                                    onChange={({ fileList }) => setIsolationFileList(fileList.slice(-5))}
+                                    multiple
+                                    showUploadList={{ showRemoveIcon: true }}
+                                    customRequest={async ({ file, onSuccess, onError }) => {
+                                      try {
+                                        const url = await fileApi.upload(file as File);
+                                        onSuccess?.(url);
+                                      } catch (e: any) {
+                                        onError?.(e);
+                                        message.error(e?.message || '文件上传失败');
+                                      }
+                                    }}
+                                    style={{
+                                      borderRadius: 12,
+                                      padding: '32px 20px',
+                                      background: 'linear-gradient(180deg, #f5faff 0%, #e8f4ff 50%, #e0efff 100%)',
+                                      border: '2px dashed #91caff',
+                                    }}
+                                  >
+                                    <p className="ant-upload-drag-icon" style={{ marginBottom: 12 }}>
+                                      <span style={{ width: 64, height: 64, borderRadius: 16, background: 'linear-gradient(135deg, rgba(22,119,255,0.15) 0%, rgba(64,150,255,0.1) 100%)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
+                                        <UploadOutlined style={{ fontSize: 32, color: '#1677ff' }} />
+                                      </span>
+                                    </p>
+                                    <p className="ant-upload-text" style={{ margin: 0, color: '#0958d9', fontSize: 15, fontWeight: 500 }}>点击或拖拽文件到此区域上传</p>
+                                    <p className="ant-upload-hint" style={{ margin: '8px 0 0', color: '#69b1ff', fontSize: 13 }}>最多 5 个文件,支持多选</p>
+                                  </Upload.Dragger>
+                                )}
+                              </div>
+                            </div>
+                          </Card>
+                        </div>
+                        <div className="flex justify-end gap-3" style={{ padding: '20px 24px', borderTop: '1px solid rgba(22, 119, 255, 0.1)', flexShrink: 0, background: 'linear-gradient(0deg, #f0f7ff 0%, #fafcff 60%, #fff 100%)' }}>
+                          {!isApproved ? (
+                            <Button
+                              type="primary"
+                              loading={isolationSubmitLoading}
+                              onClick={async () => {
+                                const nodeId = detailData.nodeId || detailData.id;
+                                if (!nodeId) {
+                                  message.error('节点ID不存在');
+                                  return;
+                                }
+                                const uploadedFiles = isolationFileList.filter((f: any) => f.status === 'done' && (f.response != null));
+                                const uploadingCount = isolationFileList.filter((f: any) => f.status === 'uploading').length;
+                                if (uploadingCount > 0) {
+                                  message.warning('请等待文件上传完成');
+                                  return;
+                                }
+                                if (isolationFileList.length > 0 && uploadedFiles.length === 0) {
+                                  message.warning('请等待文件上传完成或移除未上传成功的文件');
+                                  return;
+                                }
+                                setIsolationSubmitLoading(true);
+                                try {
+                                  const attachmentUrls = uploadedFiles.map((f: any) => {
+                                    const url = typeof f.response === 'string' ? f.response : (f.response?.url ?? '');
+                                    return { name: f.name, url };
+                                  });
+                                  const payload = {
+                                    deviceNumber: isolationDeviceNumber,
+                                    attachments: attachmentUrls,
+                                  };
+                                  const formDataString = JSON.stringify(payload);
+                                  await taskManagementApi.updateNodeApproval({
+                                    nodeId: typeof nodeId === 'number' ? nodeId : Number(nodeId),
+                                    approvalStatus: 'approved',
+                                    formData: formDataString,
+                                  });
+                                  message.success(t('common.submit') + t('common.success'));
+                                  setDetailVisible(false);
+                                  setDetailData(null);
+                                  setIsolationDeviceNumber('');
+                                  setIsolationFileList([]);
+                                  getList();
+                                } catch (error: any) {
+                                  message.error(error?.message || '提交失败');
+                                } finally {
+                                  setIsolationSubmitLoading(false);
+                                }
+                              }}
+                              icon={<SendOutlined />}
+                              style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500, boxShadow: '0 4px 14px rgba(22, 119, 255, 0.35)' }}
+                            >
+                              {t('common.submit')}
+                            </Button>
+                          ) : (
+                            <Button
+                              onClick={() => {
+                                setDetailVisible(false);
+                                setDetailData(null);
+                                setIsolationDeviceNumber('');
+                                setIsolationFileList([]);
+                              }}
+                              style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500 }}
+                            >
+                              {t('common.cancel')}
+                            </Button>
+                          )}
+                        </div>
+                      </div>
+                    );
+                  }
+
+                  console.log('TaskManagement: ✅ 进入隔离节点渲染分支', { isIsolation, isReleaseIsolation, isReturnLock, nodeType, isolationType });
                   const isolationContent = (
-                    <div 
+                    <div
                       key="isolation-content"
                       style={{
+                        flex: 1,
+                        minHeight: 0,
+                        overflow: 'hidden',
                         display: 'flex',
-                        justifyContent: 'center',
                         alignItems: 'center',
-                        minHeight: '400px',
-                        padding: '40px 20px'
+                        justifyContent: 'center',
+                        padding: '24px',
+                        background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)',
                       }}
                     >
                       <Card
                         style={{
                           width: '100%',
-                          maxWidth: '500px',
+                          maxWidth: 500,
                           textAlign: 'center',
-                          borderRadius: '12px',
-                          boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
-                          border: '1px solid #e8e8e8'
-                        }}
-                        bodyStyle={{
-                          padding: '40px 30px'
+                          borderRadius: 16,
+                          boxShadow: '0 8px 32px rgba(22, 119, 255, 0.12), 0 2px 8px rgba(0,0,0,0.04)',
+                          border: '1px solid rgba(22, 119, 255, 0.15)',
+                          background: 'linear-gradient(180deg, #ffffff 0%, #fafcff 100%)',
                         }}
+                        bodyStyle={{ padding: '48px 40px' }}
                       >
-                        <div style={{ marginBottom: '24px' }}>
-                          <LockOutlined 
-                            style={{ 
-                              fontSize: '64px', 
-                              color: '#1890ff',
-                              display: 'block'
-                            }} 
-                          />
+                        <div style={{ marginBottom: 28 }}>
+                          <span
+                            style={{
+                              width: 88,
+                              height: 88,
+                              borderRadius: 20,
+                              background: 'linear-gradient(135deg, #1677ff 0%, #4096ff 50%, #69b1ff 100%)',
+                              display: 'inline-flex',
+                              alignItems: 'center',
+                              justifyContent: 'center',
+                              boxShadow: '0 8px 24px rgba(22, 119, 255, 0.35)',
+                            }}
+                          >
+                            <LockOutlined style={{ fontSize: 42, color: '#fff' }} />
+                          </span>
                         </div>
                         <div
                           style={{
-                            fontSize: '20px',
-                            fontWeight: 500,
-                            color: '#333',
-                            lineHeight: '1.6'
+                            fontSize: 18,
+                            fontWeight: 600,
+                            color: '#1f2937',
+                            lineHeight: 1.6,
+                            marginBottom: 8,
                           }}
                         >
                           {t('form.lockCabinetTip')}
                         </div>
+                        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 13, color: '#69b1ff' }}>
+                          <KeyOutlined />
+                          <span>完成取锁、取钥匙后,任务将自动推进</span>
+                        </div>
                       </Card>
                     </div>
                   );
-                  console.log('TaskManagement: 隔离节点内容已创建', isolationContent);
                   return isolationContent;
                 }
 
@@ -2382,59 +2655,65 @@ export default function TaskManagement() {
                         })()}
                       </div>
                     ) : (
-                      <div 
+                      <div
                         style={{
+                          flex: 1,
+                          minHeight: 0,
+                          overflow: 'hidden',
                           display: 'flex',
-                          justifyContent: 'center',
                           alignItems: 'center',
-                          minHeight: '400px',
-                          padding: '40px 20px'
+                          justifyContent: 'center',
+                          padding: '24px',
+                          background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)',
                         }}
                       >
                         <Card
                           style={{
                             width: '100%',
-                            maxWidth: '500px',
+                            maxWidth: 500,
                             textAlign: 'center',
-                            borderRadius: '12px',
-                            boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
-                            border: '1px solid #e8e8e8'
-                          }}
-                          bodyStyle={{
-                            padding: '40px 30px'
+                            borderRadius: 16,
+                            boxShadow: '0 8px 32px rgba(22, 119, 255, 0.12), 0 2px 8px rgba(0,0,0,0.04)',
+                            border: '1px solid rgba(22, 119, 255, 0.15)',
+                            background: 'linear-gradient(180deg, #ffffff 0%, #fafcff 100%)',
                           }}
+                          bodyStyle={{ padding: '48px 40px' }}
                         >
-                          <div style={{ marginBottom: '24px' }}>
-                            <CheckCircleOutlined 
-                              style={{ 
-                                fontSize: '64px', 
-                                color: '#1890ff',
-                                display: 'block'
-                              }} 
-                            />
+                          <div style={{ marginBottom: 24 }}>
+                            <span
+                              style={{
+                                width: 88,
+                                height: 88,
+                                borderRadius: 20,
+                                background: 'linear-gradient(135deg, #52c41a 0%, #73d13d 50%, #95de64 100%)',
+                                display: 'inline-flex',
+                                alignItems: 'center',
+                                justifyContent: 'center',
+                                boxShadow: '0 8px 24px rgba(82, 196, 26, 0.35)',
+                              }}
+                            >
+                              <CheckCircleOutlined style={{ fontSize: 42, color: '#fff' }} />
+                            </span>
                           </div>
-                          <div
-                            style={{
-                              fontSize: '20px',
-                              fontWeight: 500,
-                              color: '#333',
-                              lineHeight: '1.6'
-                            }}
-                          >
+                          <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6 }}>
                             无需填写表单,直接点击底部按钮提交即可
                           </div>
+                          <div style={{ marginTop: 12, fontSize: 13, color: '#69b1ff' }}>
+                            确认无误后点击「完成」结束本节点
+                          </div>
                         </Card>
                       </div>
                     )}
 
                     </div>
                     {/* 底部按钮 */}
-                    <div 
+                    <div
                       className="flex justify-end gap-3"
                       style={{
-                        padding: '16px 24px',
-                        borderTop: '1px solid #f0f0f0',
-                        flexShrink: 0
+                        padding: '20px 24px',
+                        borderTop: '1px solid rgba(22, 119, 255, 0.1)',
+                        flexShrink: 0,
+                        background: 'linear-gradient(0deg, #f0f7ff 0%, #fafcff 60%, #fff 100%)',
                       }}
                     >
                       <Button
@@ -2446,6 +2725,7 @@ export default function TaskManagement() {
                           setOriginalConf('');
                           detailForm.resetFields();
                         }}
+                        style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500 }}
                       >
                         {t('common.cancel')}
                       </Button>
@@ -2540,6 +2820,7 @@ export default function TaskManagement() {
                             setSubmitLoading(false);
                           }
                         }}
+                        style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500, boxShadow: '0 4px 14px rgba(22, 119, 255, 0.35)' }}
                       >
                         {(() => {
                           // 从 detailData.type 或 detailData.nodeType 获取节点类型

+ 4 - 0
src/locales/en.json

@@ -786,6 +786,10 @@
     "formLoading": "Form loading...",
     "noFormContent": "No form content",
     "lockCabinetTip": "Please go to the lock cabinet to get the lock and key",
+    "blindPlateDeviceNo": "Blind plate device no.",
+    "dismantleDeviceNo": "Dismantle device no.",
+    "releaseBlindPlateDeviceNo": "Release blind plate device no.",
+    "restoreDeviceNo": "Restore device no.",
     "loading": "Loading...",
     "completeSuccess": "Completed successfully",
     "complete": "Complete",

+ 4 - 0
src/locales/zh.json

@@ -788,6 +788,10 @@
     "formLoading": "表单加载中...",
     "noFormContent": "暂无表单内容",
     "lockCabinetTip": "请前往锁控柜进行取锁,取钥匙操作",
+    "blindPlateDeviceNo": "盲板设备编号",
+    "dismantleDeviceNo": "拆除设备编号",
+    "releaseBlindPlateDeviceNo": "解除盲板设备编号",
+    "restoreDeviceNo": "恢复设备编号",
     "loading": "加载中...",
     "completeSuccess": "完成成功",
     "complete": "完成",