Przeglądaj źródła

修复作业管理保存时的传递参数与节点渲染数据

pm 3 miesięcy temu
rodzic
commit
bedc7034ca

+ 4 - 0
src/api/WorkJob.ts

@@ -150,6 +150,10 @@ export interface UpdateWorkflowWorkNodeParam {
   isolationPoints?: string; // 隔离点
   isolationNodeUuid?: string; // 隔离节点UUID(解除隔离节点使用)
   nodeUserDOList?: WorkflowWorkNodeUserDO[]; // 节点设置人员信息列表(上锁人、共锁人)
+  /** 上锁人 userId(字符串),从 nodeUserDOList 中 type===jtlocker 的项取 */
+  lockPerson?: string;
+  /** 共锁人 userId 列表(字符串),从 nodeUserDOList 中 type===jtcolocker 的项取,如 "[181,182]" */
+  colockPersons?: string;
   [key: string]: any;
 }
 

+ 138 - 131
src/components/Dashboard.tsx

@@ -1210,6 +1210,8 @@ export default function Dashboard() {
       const isIsolation = nodeType === 'isolation' || nodeType === '隔离' || nodeType === '隔离/方案';
       const isReleaseIsolation = nodeType === 'releaseIsolation' || nodeType === '解除隔离';
       const isReturnLock = nodeType === 'returnLock';
+      const isolationType = String(detailDataWithWorkInfo?.isolationType ?? '').trim();
+      const isBlindOrDismantle = isolationType === '0' || isolationType === '2'; // 0=盲板 2=拆除
       const isApproved = detailDataWithWorkInfo.approvalStatus === 'approved';
       const formId = detailDataWithWorkInfo.formId;
       const hasFormId = formId !== undefined && formId !== null && (
@@ -1342,9 +1344,12 @@ export default function Dashboard() {
           setFormLoading(false);
         }
       }
-      // 如果不是隔离类型,且有表单ID,且不是从 formData 获取,则从接口获取表单配置
-      else if (!isIsolation && !isReleaseIsolation && !isReturnLock && hasFormId) {
-        console.log('Dashboard: ✅ 满足条件,开始获取表单', { formId, nodeType });
+      // 有 formId 时从接口获取表单:① 审核等非隔离节点 ② 盲板/拆除节点(隔离或解除隔离且隔离方式为盲板或拆除)
+      else if (hasFormId && (
+        (!isIsolation && !isReleaseIsolation && !isReturnLock) ||
+        ((isIsolation || isReleaseIsolation) && isBlindOrDismantle)
+      )) {
+        console.log('Dashboard: ✅ 满足条件,开始获取表单', { formId, nodeType, isBlindOrDismantle });
         setFormData({ rule: [], option: {} });
         setOriginalFields([]);
         setOriginalConf('');
@@ -2444,13 +2449,18 @@ export default function Dashboard() {
                     const deviceLabel = isReleaseIsolation
                       ? (isBlindPlate ? t('form.releaseBlindPlateDeviceNo') : t('form.restoreDeviceNo'))
                       : (isBlindPlate ? t('form.blindPlateDeviceNo') : t('form.dismantleDeviceNo'));
+                    const formIdVal = taskDetailData?.formId;
+                    const hasFormIdForBlindDismantle = formIdVal !== undefined && formIdVal !== null && (
+                      typeof formIdVal === 'number' || (typeof formIdVal === 'string' && String(formIdVal).trim() !== '')
+                    );
                     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%)' }}>
+                        <div style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
                           <Card
                             style={{
                               width: '100%',
                               maxWidth: 500,
+                              margin: '0 auto',
                               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)',
@@ -2526,6 +2536,46 @@ export default function Dashboard() {
                                   </Upload.Dragger>
                                 )}
                               </div>
+                              {hasFormIdForBlindDismantle && (
+                                <div style={{ marginTop: 24, paddingTop: 24, borderTop: '1px solid rgba(22, 119, 255, 0.12)' }}>
+                                  {formLoading ? (
+                                    <div className="py-6 text-center text-gray-500">表单加载中...</div>
+                                  ) : formData.rule && formData.rule.length > 0 ? (
+                                    (() => {
+                                      const formConfig = formData.option?.formConfig || defaultFormConfig;
+                                      const layoutColumns = formConfig.layoutColumns || 1;
+                                      const gridStyle = layoutColumns > 1 ? {
+                                        display: 'grid',
+                                        gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
+                                        gap: '12px',
+                                        rowGap: '16px',
+                                      } : undefined;
+                                      return (
+                                        <AntdForm
+                                          form={taskDetailForm}
+                                          layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
+                                          size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
+                                          requiredMark={formConfig.hideRequiredMark ? false : undefined}
+                                          labelCol={formConfig.labelWidth ? {
+                                            style: {
+                                              width: `${formConfig.labelWidth}px`,
+                                              textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
+                                            }
+                                          } : undefined}
+                                        >
+                                          <div style={gridStyle} className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}>
+                                            <style>{`.form-detail-grid .ant-form-item { margin-bottom: 12px; }`}</style>
+                                            {(formData.rule || []).map((field: any) => {
+                                              const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
+                                              return renderFieldPreview(fieldWithDisabled);
+                                            })}
+                                          </div>
+                                        </AntdForm>
+                                      );
+                                    })()
+                                  ) : null}
+                                </div>
+                              )}
                             </div>
                           </Card>
                         </div>
@@ -2550,17 +2600,51 @@ export default function Dashboard() {
                                   message.warning('请等待文件上传完成或移除未上传成功的文件');
                                   return;
                                 }
+                                if (hasFormIdForBlindDismantle && formData.rule && formData.rule.length > 0) {
+                                  try {
+                                    await taskDetailForm.validateFields();
+                                  } catch (err: any) {
+                                    if (err?.errorFields?.[0]?.errors?.[0]) {
+                                      message.error(err.errorFields[0].errors[0]);
+                                    } else {
+                                      message.error('请完善表单内容');
+                                    }
+                                    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 };
                                   });
+                                  let formDataString: string | undefined = undefined;
+                                  if (hasFormIdForBlindDismantle && formData.rule && formData.rule.length > 0 && originalFields.length > 0) {
+                                    try {
+                                      const values = taskDetailForm.getFieldsValue();
+                                      const fieldsWithValues = originalFields.map((fieldString: string) => {
+                                        try {
+                                          const fieldObj = JSON.parse(fieldString);
+                                          const fieldName = fieldObj.name || fieldObj.field;
+                                          const fieldValue = values[fieldName];
+                                          fieldObj.value = fieldValue !== undefined && fieldValue !== null ? fieldValue : '';
+                                          return JSON.stringify(fieldObj);
+                                        } catch {
+                                          return fieldString;
+                                        }
+                                      });
+                                      const submitData = { conf: formData.option, fields: fieldsWithValues };
+                                      formDataString = JSON.stringify(submitData);
+                                    } catch (e) {
+                                      console.error('Dashboard: 盲板/拆除 表单数据序列化失败', e);
+                                    }
+                                  }
                                   await taskManagementApi.updateNodeApproval({
                                     nodeId: typeof nodeId === 'number' ? nodeId : Number(nodeId),
                                     approvalStatus: 'approved',
                                     deviceNumber: isolationDeviceNumber,
                                     attachments: JSON.stringify(attachmentUrls),
+                                    ...(formDataString != null ? { formData: formDataString } : {}),
                                   });
                                   message.success(t('common.submit') + t('common.success'));
                                   setTaskDetailVisible(false);
@@ -2607,141 +2691,64 @@ export default function Dashboard() {
                   );
                 }
 
-                // 审核类型节点 (review)
+                // 审核类型节点 (review) - 样式与盲板/拆除、确认节点统一
                 if (isReview) {
                   return (
                     <div style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
-                      <div className="space-y-6" style={{ padding: '0 24px', flex: 1, overflowY: 'auto', minHeight: 0 }}>
-                        {/* 自定义表单 */}
-                        {formLoading ? (
-                          <div className="py-8 text-center text-gray-500">表单加载中...</div>
-                        ) : formData.rule && formData.rule.length > 0 ? (
-                          <div>
-                            {(() => {
-                              const formConfig = formData.option?.formConfig || defaultFormConfig;
-                              const layoutColumns = formConfig.layoutColumns || 1;
-                              const gridStyle = layoutColumns > 1 ? {
-                                display: 'grid',
-                                gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
-                                gap: '12px',
-                                rowGap: '16px',
-                              } : undefined;
-                              
-                              return (
-                                <AntdForm
-                                  form={taskDetailForm}
-                                  layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
-                                  size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
-                                  requiredMark={formConfig.hideRequiredMark ? false : undefined}
-                                  labelCol={formConfig.labelWidth ? { 
-                                    style: { 
-                                      width: `${formConfig.labelWidth}px`,
-                                      textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
-                                    } 
-                                  } : undefined}
-                                >
-                                  <div 
-                                    style={gridStyle} 
-                                    className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}
-                                  >
-                                    <style>{`
-                                      .form-detail-grid .ant-form-item {
-                                        margin-bottom: 12px;
-                                      }
-                                    `}</style>
-                                    {(formData.rule || []).map((field: any) => {
-                                      const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
-                                      return renderFieldPreview(fieldWithDisabled);
-                                    })}
-                                  </div>
-                                </AntdForm>
-                              );
-                            })()}
-                          </div>
-                        ) : (
-                          <div
-                            style={{
-                              display: 'flex',
-                              justifyContent: 'center',
-                              alignItems: 'center',
-                              minHeight: '400px',
-                              padding: '24px',
-                              background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)',
-                            }}
-                          >
-                            <Card
-                              style={{
-                                width: '100%',
-                                maxWidth: 500,
-                                textAlign: 'center',
-                                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: 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 style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
+                        <Card
+                          style={{
+                            width: '100%',
+                            maxWidth: 500,
+                            margin: '0 auto',
+                            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' }}>
+                            {formLoading ? (
+                              <div className="py-8 text-center text-gray-500">表单加载中...</div>
+                            ) : formData.rule && formData.rule.length > 0 ? (
+                              <div className="space-y-4">
+                                {(() => {
+                                  const formConfig = formData.option?.formConfig || defaultFormConfig;
+                                  const layoutColumns = formConfig.layoutColumns || 1;
+                                  const gridStyle = layoutColumns > 1 ? { display: 'grid', gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`, gap: '12px', rowGap: '16px' } : undefined;
+                                  return (
+                                    <AntdForm form={taskDetailForm} layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'} size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize} requiredMark={formConfig.hideRequiredMark ? false : undefined} labelCol={formConfig.labelWidth ? { style: { width: `${formConfig.labelWidth}px`, textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left' } } : undefined}>
+                                      <div style={gridStyle} className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}>
+                                        <style>{`.form-detail-grid .ant-form-item { margin-bottom: 12px; }`}</style>
+                                        {(formData.rule || []).map((field: any) => {
+                                          const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
+                                          return renderFieldPreview(fieldWithDisabled);
+                                        })}
+                                      </div>
+                                    </AntdForm>
+                                  );
+                                })()}
                               </div>
-                              <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6 }}>
-                                无需填写表单,直接点击底部按钮提交即可
+                            ) : (
+                              <div style={{ textAlign: 'center', padding: '32px 24px' }}>
+                                <span style={{ width: 72, height: 72, borderRadius: 18, 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)', marginBottom: 20 }}>
+                                  <CheckCircleOutlined style={{ fontSize: 36, color: '#fff' }} />
+                                </span>
+                                <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6 }}>无需填写表单,直接点击底部按钮提交即可</div>
                               </div>
-                              <div style={{ marginTop: 12, fontSize: 13, color: '#69b1ff' }}>
-                                确认无误后点击「完成」结束本节点
+                            )}
+                            <div className="flex items-start" style={{ gap: 16, marginTop: 24, paddingTop: 24, borderTop: '1px solid rgba(22, 119, 255, 0.12)', backgroundColor: 'rgba(22, 119, 255, 0.04)', padding: '16px', borderRadius: 12, border: '1px solid rgba(22, 119, 255, 0.1)' }}>
+                              <label className="text-sm font-medium text-gray-700 whitespace-nowrap pt-2" style={{ width: defaultFormConfig.labelWidth ? `${defaultFormConfig.labelWidth - 20}px` : '80px', textAlign: defaultFormConfig.labelPosition === 'left' ? 'left' : defaultFormConfig.labelPosition === 'right' ? 'right' : 'left', flexShrink: 0 }}>{t('form.reviewComment')}</label>
+                              <div className="flex-1">
+                                <Input.TextArea rows={4} value={approvalComment} onChange={(e) => setApprovalComment(e.target.value)} placeholder={t('form.reviewCommentPlaceholder')} maxLength={500} showCount disabled={isApproved} readOnly={isApproved} style={{ borderRadius: 10, borderColor: '#d9e8ff' }} />
                               </div>
-                            </Card>
-                          </div>
-                        )}
-
-                        {/* 审核意见 */}
-                        <div className="flex items-start" style={{ gap: '16px', backgroundColor: '#f5f5f5', padding: '12px 12px 22px 12px', borderRadius: '12px', border: '1px solid #e0e0e0', marginBottom: '12px' }}>
-                          <label 
-                            className="text-sm font-medium text-gray-700 whitespace-nowrap pt-2"
-                            style={{ 
-                              width: defaultFormConfig.labelWidth ? `${defaultFormConfig.labelWidth - 20}px` : '80px',
-                              textAlign: defaultFormConfig.labelPosition === 'left' ? 'left' : defaultFormConfig.labelPosition === 'right' ? 'right' : 'left',
-                              flexShrink: 0
-                            }}
-                          >
-                            {t('form.reviewComment')}
-                          </label>
-                          <div className="flex-1">
-                            <Input.TextArea
-                              rows={4}
-                              value={approvalComment}
-                              onChange={(e) => setApprovalComment(e.target.value)}
-                              placeholder={t('form.reviewCommentPlaceholder')}
-                              maxLength={500}
-                              showCount
-                              disabled={isApproved}
-                              readOnly={isApproved}
-                            />
+                            </div>
                           </div>
-                        </div>
+                        </Card>
                       </div>
-                      {/* 底部按钮 - 审核通过/不通过 */}
-                      <div 
-                        className="flex justify-end gap-3"
-                        style={{
-                          padding: '16px 24px',
-                          borderTop: '1px solid #f0f0f0',
-                          flexShrink: 0
-                        }}
-                      >
+                      <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%)' }}>
                         <Button
                           danger
                           loading={approvalLoading}

+ 138 - 131
src/components/ExecutorDashboard.tsx

@@ -1056,6 +1056,8 @@ export default function ExecutorDashboard() {
       const isIsolation = nodeType === 'isolation' || nodeType === '隔离' || nodeType === '隔离/方案';
       const isReleaseIsolation = nodeType === 'releaseIsolation' || nodeType === '解除隔离';
       const isReturnLock = nodeType === 'returnLock';
+      const isolationType = String(detailDataWithWorkInfo?.isolationType ?? '').trim();
+      const isBlindOrDismantle = isolationType === '0' || isolationType === '2'; // 0=盲板 2=拆除
       const isApproved = detailDataWithWorkInfo.approvalStatus === 'approved' || detailDataWithWorkInfo.approvalStatus === 3;
       const formId = detailDataWithWorkInfo.formId;
       const hasFormId = formId !== undefined && formId !== null && (
@@ -1188,9 +1190,12 @@ export default function ExecutorDashboard() {
           setFormLoading(false);
         }
       }
-      // 如果不是隔离类型,且有表单ID,且不是从 formData 获取,则从接口获取表单配置
-      else if (!isIsolation && !isReleaseIsolation && !isReturnLock && hasFormId) {
-        console.log('ExecutorDashboard: ✅ 满足条件,开始获取表单', { formId, nodeType });
+      // 有 formId 时从接口获取表单:① 审核等非隔离节点 ② 盲板/拆除节点(隔离或解除隔离且隔离方式为盲板或拆除)
+      else if (hasFormId && (
+        (!isIsolation && !isReleaseIsolation && !isReturnLock) ||
+        ((isIsolation || isReleaseIsolation) && isBlindOrDismantle)
+      )) {
+        console.log('ExecutorDashboard: ✅ 满足条件,开始获取表单', { formId, nodeType, isBlindOrDismantle });
         setFormData({ rule: [], option: {} });
         setOriginalFields([]);
         setOriginalConf('');
@@ -1775,13 +1780,18 @@ export default function ExecutorDashboard() {
                     const deviceLabel = isReleaseIsolation
                       ? (isBlindPlate ? t('form.releaseBlindPlateDeviceNo') : t('form.restoreDeviceNo'))
                       : (isBlindPlate ? t('form.blindPlateDeviceNo') : t('form.dismantleDeviceNo'));
+                    const formIdVal = taskDetailData?.formId;
+                    const hasFormIdForBlindDismantle = formIdVal !== undefined && formIdVal !== null && (
+                      typeof formIdVal === 'number' || (typeof formIdVal === 'string' && String(formIdVal).trim() !== '')
+                    );
                     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%)' }}>
+                        <div style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
                           <Card
                             style={{
                               width: '100%',
                               maxWidth: 500,
+                              margin: '0 auto',
                               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)',
@@ -1857,6 +1867,46 @@ export default function ExecutorDashboard() {
                                   </Upload.Dragger>
                                 )}
                               </div>
+                              {hasFormIdForBlindDismantle && (
+                                <div style={{ marginTop: 24, paddingTop: 24, borderTop: '1px solid rgba(22, 119, 255, 0.12)' }}>
+                                  {formLoading ? (
+                                    <div className="py-6 text-center text-gray-500">{t('cockpit.formLoading')}</div>
+                                  ) : formData.rule && formData.rule.length > 0 ? (
+                                    (() => {
+                                      const formConfig = formData.option?.formConfig || defaultFormConfig;
+                                      const layoutColumns = formConfig.layoutColumns || 1;
+                                      const gridStyle = layoutColumns > 1 ? {
+                                        display: 'grid',
+                                        gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
+                                        gap: '12px',
+                                        rowGap: '16px',
+                                      } : undefined;
+                                      return (
+                                        <AntdForm
+                                          form={taskDetailForm}
+                                          layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
+                                          size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
+                                          requiredMark={formConfig.hideRequiredMark ? false : undefined}
+                                          labelCol={formConfig.labelWidth ? {
+                                            style: {
+                                              width: `${formConfig.labelWidth}px`,
+                                              textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
+                                            }
+                                          } : undefined}
+                                        >
+                                          <div style={gridStyle} className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}>
+                                            <style>{`.form-detail-grid .ant-form-item { margin-bottom: 12px; }`}</style>
+                                            {(formData.rule || []).map((field: any) => {
+                                              const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
+                                              return renderFieldPreview(fieldWithDisabled);
+                                            })}
+                                          </div>
+                                        </AntdForm>
+                                      );
+                                    })()
+                                  ) : null}
+                                </div>
+                              )}
                             </div>
                           </Card>
                         </div>
@@ -1881,17 +1931,51 @@ export default function ExecutorDashboard() {
                                   message.warning('请等待文件上传完成或移除未上传成功的文件');
                                   return;
                                 }
+                                if (hasFormIdForBlindDismantle && formData.rule && formData.rule.length > 0) {
+                                  try {
+                                    await taskDetailForm.validateFields();
+                                  } catch (err: any) {
+                                    if (err?.errorFields?.[0]?.errors?.[0]) {
+                                      message.error(err.errorFields[0].errors[0]);
+                                    } else {
+                                      message.error('请完善表单内容');
+                                    }
+                                    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 };
                                   });
+                                  let formDataString: string | undefined = undefined;
+                                  if (hasFormIdForBlindDismantle && formData.rule && formData.rule.length > 0 && originalFields.length > 0) {
+                                    try {
+                                      const values = taskDetailForm.getFieldsValue();
+                                      const fieldsWithValues = originalFields.map((fieldString: string) => {
+                                        try {
+                                          const fieldObj = JSON.parse(fieldString);
+                                          const fieldName = fieldObj.name || fieldObj.field;
+                                          const fieldValue = values[fieldName];
+                                          fieldObj.value = fieldValue !== undefined && fieldValue !== null ? fieldValue : '';
+                                          return JSON.stringify(fieldObj);
+                                        } catch {
+                                          return fieldString;
+                                        }
+                                      });
+                                      const submitData = { conf: formData.option, fields: fieldsWithValues };
+                                      formDataString = JSON.stringify(submitData);
+                                    } catch (e) {
+                                      console.error('ExecutorDashboard: 盲板/拆除 表单数据序列化失败', e);
+                                    }
+                                  }
                                   await myTaskApi.updateNodeApproval({
                                     nodeId: typeof nodeId === 'number' ? nodeId : Number(nodeId),
                                     approvalStatus: 'approved',
                                     deviceNumber: isolationDeviceNumber,
                                     attachments: JSON.stringify(attachmentUrls),
+                                    ...(formDataString != null ? { formData: formDataString } : {}),
                                   });
                                   message.success(t('common.submit') + t('common.success'));
                                   setTaskDetailVisible(false);
@@ -1938,141 +2022,64 @@ export default function ExecutorDashboard() {
                   );
                 }
 
-                // 审核类型节点 (review)
+                // 审核类型节点 (review) - 样式与盲板/拆除、确认节点统一
                 if (isReview) {
                   return (
                     <div style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
-                      <div className="space-y-6" style={{ padding: '0 24px', flex: 1, overflowY: 'auto', minHeight: 0 }}>
-                        {/* 自定义表单 */}
-                        {formLoading ? (
-                          <div className="py-8 text-center text-gray-500">{t('cockpit.formLoading')}</div>
-                        ) : formData.rule && formData.rule.length > 0 ? (
-                          <div>
-                            {(() => {
-                              const formConfig = formData.option?.formConfig || defaultFormConfig;
-                              const layoutColumns = formConfig.layoutColumns || 1;
-                              const gridStyle = layoutColumns > 1 ? {
-                                display: 'grid',
-                                gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
-                                gap: '12px',
-                                rowGap: '16px',
-                              } : undefined;
-                              
-                              return (
-                                <AntdForm
-                                  form={taskDetailForm}
-                                  layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
-                                  size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
-                                  requiredMark={formConfig.hideRequiredMark ? false : undefined}
-                                  labelCol={formConfig.labelWidth ? { 
-                                    style: { 
-                                      width: `${formConfig.labelWidth}px`,
-                                      textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
-                                    } 
-                                  } : undefined}
-                                >
-                                  <div 
-                                    style={gridStyle} 
-                                    className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}
-                                  >
-                                    <style>{`
-                                      .form-detail-grid .ant-form-item {
-                                        margin-bottom: 12px;
-                                      }
-                                    `}</style>
-                                    {(formData.rule || []).map((field: any) => {
-                                      const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
-                                      return renderFieldPreview(fieldWithDisabled);
-                                    })}
-                                  </div>
-                                </AntdForm>
-                              );
-                            })()}
-                          </div>
-                        ) : (
-                          <div
-                            style={{
-                              display: 'flex',
-                              justifyContent: 'center',
-                              alignItems: 'center',
-                              minHeight: '400px',
-                              padding: '24px',
-                              background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)',
-                            }}
-                          >
-                            <Card
-                              style={{
-                                width: '100%',
-                                maxWidth: 500,
-                                textAlign: 'center',
-                                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: 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 style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
+                        <Card
+                          style={{
+                            width: '100%',
+                            maxWidth: 500,
+                            margin: '0 auto',
+                            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' }}>
+                            {formLoading ? (
+                              <div className="py-8 text-center text-gray-500">{t('cockpit.formLoading')}</div>
+                            ) : formData.rule && formData.rule.length > 0 ? (
+                              <div className="space-y-4">
+                                {(() => {
+                                  const formConfig = formData.option?.formConfig || defaultFormConfig;
+                                  const layoutColumns = formConfig.layoutColumns || 1;
+                                  const gridStyle = layoutColumns > 1 ? { display: 'grid', gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`, gap: '12px', rowGap: '16px' } : undefined;
+                                  return (
+                                    <AntdForm form={taskDetailForm} layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'} size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize} requiredMark={formConfig.hideRequiredMark ? false : undefined} labelCol={formConfig.labelWidth ? { style: { width: `${formConfig.labelWidth}px`, textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left' } } : undefined}>
+                                      <div style={gridStyle} className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}>
+                                        <style>{`.form-detail-grid .ant-form-item { margin-bottom: 12px; }`}</style>
+                                        {(formData.rule || []).map((field: any) => {
+                                          const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
+                                          return renderFieldPreview(fieldWithDisabled);
+                                        })}
+                                      </div>
+                                    </AntdForm>
+                                  );
+                                })()}
                               </div>
-                              <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6 }}>
-                                无需填写表单,直接点击底部按钮提交即可
+                            ) : (
+                              <div style={{ textAlign: 'center', padding: '32px 24px' }}>
+                                <span style={{ width: 72, height: 72, borderRadius: 18, 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)', marginBottom: 20 }}>
+                                  <CheckCircleOutlined style={{ fontSize: 36, color: '#fff' }} />
+                                </span>
+                                <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6 }}>无需填写表单,直接点击底部按钮提交即可</div>
                               </div>
-                              <div style={{ marginTop: 12, fontSize: 13, color: '#69b1ff' }}>
-                                确认无误后点击「完成」结束本节点
+                            )}
+                            <div className="flex items-start" style={{ gap: 16, marginTop: 24, paddingTop: 24, borderTop: '1px solid rgba(22, 119, 255, 0.12)', backgroundColor: 'rgba(22, 119, 255, 0.04)', padding: '16px', borderRadius: 12, border: '1px solid rgba(22, 119, 255, 0.1)' }}>
+                              <label className="text-sm font-medium text-gray-700 whitespace-nowrap pt-2" style={{ width: defaultFormConfig.labelWidth ? `${defaultFormConfig.labelWidth - 20}px` : '80px', textAlign: defaultFormConfig.labelPosition === 'left' ? 'left' : defaultFormConfig.labelPosition === 'right' ? 'right' : 'left', flexShrink: 0 }}>{t('form.reviewComment')}</label>
+                              <div className="flex-1">
+                                <Input.TextArea rows={4} value={approvalComment} onChange={(e) => setApprovalComment(e.target.value)} placeholder={t('form.reviewCommentPlaceholder')} maxLength={500} showCount disabled={isApproved} readOnly={isApproved} style={{ borderRadius: 10, borderColor: '#d9e8ff' }} />
                               </div>
-                            </Card>
-                          </div>
-                        )}
-
-                        {/* 审核意见 */}
-                        <div className="flex items-start" style={{ gap: '16px', backgroundColor: '#f5f5f5', padding: '12px 12px 22px 12px', borderRadius: '12px', border: '1px solid #e0e0e0', marginBottom: '12px' }}>
-                          <label 
-                            className="text-sm font-medium text-gray-700 whitespace-nowrap pt-2"
-                            style={{ 
-                              width: defaultFormConfig.labelWidth ? `${defaultFormConfig.labelWidth - 20}px` : '80px',
-                              textAlign: defaultFormConfig.labelPosition === 'left' ? 'left' : defaultFormConfig.labelPosition === 'right' ? 'right' : 'left',
-                              flexShrink: 0
-                            }}
-                          >
-                            {t('form.reviewComment')}
-                          </label>
-                          <div className="flex-1">
-                            <Input.TextArea
-                              rows={4}
-                              value={approvalComment}
-                              onChange={(e) => setApprovalComment(e.target.value)}
-                              placeholder={t('form.reviewCommentPlaceholder')}
-                              maxLength={500}
-                              showCount
-                              disabled={isApproved}
-                              readOnly={isApproved}
-                            />
+                            </div>
                           </div>
-                        </div>
+                        </Card>
                       </div>
-                      {/* 底部按钮 - 审核通过/不通过 */}
-                      <div 
-                        className="flex justify-end gap-3"
-                        style={{
-                          padding: '16px 24px',
-                          borderTop: '1px solid #f0f0f0',
-                          flexShrink: 0
-                        }}
-                      >
+                      <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%)' }}>
                         <Button
                           danger
                           loading={approvalLoading}

+ 256 - 104
src/components/IsolationWork.tsx

@@ -1973,21 +1973,31 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
     console.log('当前 workflowWorkNodeDOList:', workflowWorkNodeDOList);
     
     if (nodeDO) {
-      // 如果找到节点数据,解析data字段并回显
+      const isReleaseIsolation = nodeDO.type === 'releaseIsolation' || nodeDO.type === '解除隔离';
+      const isolationNodeUuid = nodeDO.isolationNodeUuid || '';
+      // 解除隔离节点:从对应的隔离/方案节点抓取表单信息填充,保证两节点内容一致
+      const isolationNodeDO = isReleaseIsolation && isolationNodeUuid
+        ? workflowWorkNodeDOList.find(
+            (item: WorkflowWorkNodeDO) =>
+              item.uuid === isolationNodeUuid &&
+              (item.type === 'isolation' || item.type === '隔离' || item.type === '隔离/方案')
+          )
+        : null;
+      const dataSource = isolationNodeDO ?? nodeDO;
       let nodeData: any = {};
-      if (nodeDO.data) {
+      if (dataSource.data) {
         try {
-          nodeData = typeof nodeDO.data === 'string' ? JSON.parse(nodeDO.data) : nodeDO.data;
+          nodeData = typeof dataSource.data === 'string' ? JSON.parse(dataSource.data) : dataSource.data;
         } catch (e) {
           console.error('解析节点data失败:', e);
         }
       }
+      const source = nodeData || node.data || {};
       
-      // 从 nodeUserList 中提取上锁人和共锁人
       let lockPersonId: string | number = '';
       const coLockPersonIds: (string | number)[] = [];
-      if (nodeDO.nodeUserList && Array.isArray(nodeDO.nodeUserList)) {
-        nodeDO.nodeUserList.forEach((user: any) => {
+      if (dataSource.nodeUserList && Array.isArray(dataSource.nodeUserList)) {
+        dataSource.nodeUserList.forEach((user: any) => {
           if (user.type === 'jtlocker' && user.userId) {
             lockPersonId = typeof user.userId === 'number' ? user.userId : Number(user.userId);
           } else if (user.type === 'jtcolocker' && user.userId) {
@@ -1996,76 +2006,57 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
         });
       }
       
-      // 优先使用 nodeDO 中的数据(编辑时从后端获取的真实数据)
-      // 如果 nodeDO 存在且有 id,说明是已保存的节点,优先使用 nodeDO 的数据
-      const source = nodeData || node.data || {};
       const config = nodeConfigs.find(c => c.type === (nodeDO.type || source.type));
       
-      // 解析隔离点
       let isolationPoints: string[] = [];
-      if (nodeDO.isolationPoints) {
+      if (dataSource.isolationPoints) {
         try {
-          isolationPoints = typeof nodeDO.isolationPoints === 'string' 
-            ? JSON.parse(nodeDO.isolationPoints) 
-            : (Array.isArray(nodeDO.isolationPoints) ? nodeDO.isolationPoints : []);
+          isolationPoints = typeof dataSource.isolationPoints === 'string'
+            ? JSON.parse(dataSource.isolationPoints)
+            : (Array.isArray(dataSource.isolationPoints) ? dataSource.isolationPoints : []);
         } catch (e) {
-          console.error('解析隔离点失败:', e);
           isolationPoints = source.isolationPoints || [];
         }
       } else {
         isolationPoints = source.isolationPoints || [];
       }
       
-      // 构建节点配置,优先使用 nodeDO 中的数据
-      console.log('构建节点配置 - nodeDO.workerUserId:', nodeDO.workerUserId, 'source.workerUserId:', source.workerUserId);
       const nodeConfig = {
-        nodeName: nodeDO.nodeName || source.label || config?.label || '',
-        nodeIcon: nodeDO.nodeIcon || source.icon || '',
-        responsible: (nodeDO.workerUserId !== null && nodeDO.workerUserId !== undefined && nodeDO.workerUserId !== 0) 
-          ? (typeof nodeDO.workerUserId === 'number' ? nodeDO.workerUserId : Number(nodeDO.workerUserId)) 
-          : (source.workerUserId && source.workerUserId !== '' && source.workerUserId !== '0') 
-            ? (typeof source.workerUserId === 'number' ? source.workerUserId : Number(source.workerUserId)) 
+        nodeName: dataSource.nodeName || source.label || config?.label || '',
+        nodeIcon: (isolationNodeDO ? nodeDO.nodeIcon : dataSource.nodeIcon) || source.icon || '',
+        responsible: (dataSource.workerUserId !== null && dataSource.workerUserId !== undefined && dataSource.workerUserId !== 0)
+          ? (typeof dataSource.workerUserId === 'number' ? dataSource.workerUserId : Number(dataSource.workerUserId))
+          : (source.workerUserId && source.workerUserId !== '' && source.workerUserId !== '0')
+            ? (typeof source.workerUserId === 'number' ? source.workerUserId : Number(source.workerUserId))
             : undefined,
         remark: source.remark || '',
-        submitForm: nodeDO.formId ? (typeof nodeDO.formId === 'number' ? nodeDO.formId : Number(nodeDO.formId)) : (source.submitForm ? (typeof source.submitForm === 'number' ? source.submitForm : Number(source.submitForm)) : undefined),
-        isolationType: (nodeDO.isolationType !== null && nodeDO.isolationType !== undefined) ? String(nodeDO.isolationType) : (source.isolationType || ''),
-        isolationPoints: isolationPoints,
+        submitForm: dataSource.formId ? (typeof dataSource.formId === 'number' ? dataSource.formId : Number(dataSource.formId)) : (source.submitForm ? (typeof source.submitForm === 'number' ? source.submitForm : Number(source.submitForm)) : undefined),
+        isolationType: (dataSource.isolationType !== null && dataSource.isolationType !== undefined) ? String(dataSource.isolationType) : (source.isolationType || ''),
+        isolationPoints,
         isolationNode: source.isolationNode || [],
         isolationNodeUuid: nodeDO.isolationNodeUuid || source.isolationNodeUuid || '',
         lockPerson: lockPersonId || (source.lockPerson ? (typeof source.lockPerson === 'number' ? source.lockPerson : Number(source.lockPerson)) : undefined),
         coLockPersons: coLockPersonIds.length > 0 ? coLockPersonIds : (source.coLockPersons && Array.isArray(source.coLockPersons) ? source.coLockPersons.map((id: any) => typeof id === 'number' ? id : Number(id)) : []),
-        notificationMethods: source.notificationMethods || {
-          sms: false,
-          message: false,
-          email: false,
-          app: false,
-        },
+        notificationMethods: source.notificationMethods || { sms: false, message: false, email: false, app: false },
         notificationPerson: source.notificationPerson || '',
-        notificationTime: nodeDO.notifyTime || source.notificationTime || '',
+        notificationTime: dataSource.notifyTime || source.notificationTime || '',
         smsTemplateCode: source.smsTemplateCode || 'false',
         messageTemplateCode: source.messageTemplateCode || 'false',
         emailTemplateCode: source.emailTemplateCode || 'false',
         appTemplateCode: source.appTemplateCode || 'false',
       };
       
-      // 优先使用缓存中的配置(如果有),这样可以保持用户未保存的修改
-      // 如果缓存中没有,再使用 nodeDO 的数据
-      console.log('设置节点配置 - nodeConfig.responsible:', nodeConfig.responsible, 'node.id:', node.id);
-      const cachedConfig = workflowNodeConfigCache.get(node.id);
-      console.log('缓存中的配置:', cachedConfig);
-      if (cachedConfig) {
-        // 如果缓存中有配置,使用缓存(可能包含用户未保存的修改)
-        console.log('使用缓存配置');
-        // 使用函数式更新确保状态正确更新
-        setWorkflowNodeConfig(() => ({ ...cachedConfig }));
-      } else if (nodeDO.id) {
-        // 如果缓存中没有,但节点已保存,使用 nodeDO 的数据
-        console.log('使用 nodeDO 配置');
+      if (isReleaseIsolation && isolationNodeDO) {
         setWorkflowNodeConfig(() => ({ ...nodeConfig }));
       } else {
-        // 如果既没有缓存也没有保存,使用节点数据
-        console.log('使用节点数据配置');
-        setWorkflowNodeConfig(() => ({ ...nodeConfig }));
+        const cachedConfig = workflowNodeConfigCache.get(node.id);
+        if (cachedConfig) {
+          setWorkflowNodeConfig(() => ({ ...cachedConfig }));
+        } else if (nodeDO.id) {
+          setWorkflowNodeConfig(() => ({ ...nodeConfig }));
+        } else {
+          setWorkflowNodeConfig(() => ({ ...nodeConfig }));
+        }
       }
     } else {
       // 如果没有找到节点数据,使用原有逻辑
@@ -4637,7 +4628,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                     placeholder="请选择提交表单"
                                     className="w-full [&_.ant-select-selector]:!rounded-lg [&_.ant-select-selector]:!h-10"
                                     allowClear
-                                    disabled={isViewMode}
+                                    disabled={isViewMode || selectedWorkflowNode.data?.type === 'releaseIsolation'}
                                   >
                                     {workflowFormList.map(form => (
                                       <Select.Option key={form.id} value={form.id}>{form.name}</Select.Option>
@@ -4954,7 +4945,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                             smsTemplateCode: e.target.checked ? 'true' : 'false',
                                           })
                                         }
-                                        disabled={isViewMode}
+                                        disabled={isViewMode || selectedWorkflowNode.data?.type === 'releaseIsolation'}
                                       >
                                         短信
                                       </Checkbox>
@@ -4972,7 +4963,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                             messageTemplateCode: e.target.checked ? 'true' : 'false',
                                           })
                                         }
-                                        disabled={isViewMode}
+                                        disabled={isViewMode || selectedWorkflowNode.data?.type === 'releaseIsolation'}
                                       >
                                         站内信
                                       </Checkbox>
@@ -4990,7 +4981,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                             emailTemplateCode: e.target.checked ? 'true' : 'false',
                                           })
                                         }
-                                        disabled={isViewMode}
+                                        disabled={isViewMode || selectedWorkflowNode.data?.type === 'releaseIsolation'}
                                       >
                                         邮件
                                       </Checkbox>
@@ -5008,7 +4999,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                             appTemplateCode: e.target.checked ? 'true' : 'false',
                                           })
                                         }
-                                        disabled={isViewMode}
+                                        disabled={isViewMode || selectedWorkflowNode.data?.type === 'releaseIsolation'}
                                       >
                                         APP通知
                                       </Checkbox>
@@ -5093,6 +5084,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                     
                                     // 在外部作用域定义,确保在自动切换节点时可以使用
                                     let mergedWorkflowWorkNodeDOList: WorkflowWorkNodeDO[] = [];
+                                    let didRefreshFlowNodes = false; // 是否已用合并列表刷新过画布所有节点
                                     
                                     if (nodeDO && nodeDO.id && workflowWorkId) {
                                       // 构建节点用户信息列表
@@ -5164,6 +5156,14 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                         }
                                       }
                                       
+                                      // 从 nodeUserDOList 派生 lockPerson(jtlocker 的 userId 字符串)、colockPersons(jtcolocker 的 userId 字符串,如 "[181,182]")
+                                      const lockPersonEntry = nodeUserDOList.find((u: any) => u.type === 'jtlocker');
+                                      const lockPerson = lockPersonEntry?.userId != null ? String(lockPersonEntry.userId) : undefined;
+                                      const colockPersonIds = nodeUserDOList
+                                        .filter((u: any) => u.type === 'jtcolocker' && u.userId != null)
+                                        .map((u: any) => Number(u.userId));
+                                      const colockPersons = colockPersonIds.length > 0 ? JSON.stringify(colockPersonIds) : undefined;
+
                                       const updateParam: UpdateWorkflowWorkNodeParam = {
                                         nodeId: nodeDO.id,
                                         nodeName: workflowNodeConfig.nodeName,
@@ -5178,6 +5178,8 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                           ? workflowNodeConfig.isolationNodeUuid 
                                           : undefined,
                                         nodeUserDOList: nodeUserDOList.length > 0 ? nodeUserDOList : undefined,
+                                        lockPerson,
+                                        colockPersons: colockPersons.length > 0 ? colockPersons : undefined,
                                         // 如果获取到了表单数据,将整个对象内容转换成字符串传递给 formData 字段
                                         formData: formData,
                                         // 根据选中值传递模板代码参数(字符串类型的 'true' 或 'false')
@@ -5189,19 +5191,99 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                       
                                       await workJobApi.updateWorkflowWorkNode(updateParam);
                                       
-                                      // 更新成功后,立即调用详情接口获取最新数据
-                                      let mergedWorkflowWorkNodeDOList: WorkflowWorkNodeDO[] = [];
+                                      // 若是隔离/方案节点且含上锁人/共锁人:先本地复刻到对应解除隔离节点并刷新画布,保证切换节点时立即看到一致数据(不依赖详情接口)
+                                      const isIsolationWithLock = (selectedWorkflowNode.data?.type === 'isolation' || selectedWorkflowNode.data?.type === '隔离' || selectedWorkflowNode.data?.type === '隔离/方案') &&
+                                        workflowNodeConfig.isolationType === '1' && nodeUserDOList.length > 0;
+                                      if (isIsolationWithLock) {
+                                        const listForSync = workflowWorkNodeDOList.map((item: WorkflowWorkNodeDO) => {
+                                          if ((item.type === 'releaseIsolation' || item.type === '解除隔离') && item.isolationNodeUuid === selectedWorkflowNode.id) {
+                                            return {
+                                              ...item,
+                                              nodeUserList: nodeUserDOList.map((u: any) => ({ userId: u.userId, type: u.type, ...(u.userName != null && { userName: u.userName }) })),
+                                            };
+                                          }
+                                          return item;
+                                        });
+                                        setWorkflowWorkNodeDOList(listForSync);
+                                        const releaseUuids = listForSync
+                                          .filter((n: WorkflowWorkNodeDO) => (n.type === 'releaseIsolation' || n.type === '解除隔离') && n.isolationNodeUuid === selectedWorkflowNode.id)
+                                          .map((n: WorkflowWorkNodeDO) => n.uuid).filter((id: any) => id != null && id !== '');
+                                        if (releaseUuids.length > 0) {
+                                          setWorkflowNodeConfigCache(prev => {
+                                            const next = new Map(prev);
+                                            releaseUuids.forEach((uuid: any) => next.delete(uuid));
+                                            return next;
+                                          });
+                                        }
+                                        setWorkflowNodes((nds) =>
+                                          nds.map((node) => {
+                                            const nodeDO = listForSync.find((item: WorkflowWorkNodeDO) => item.uuid === node.id);
+                                            if (!nodeDO) return node.id === selectedWorkflowNode.id ? { ...node, data: { ...node.data, completed: true }, selected: false } : node;
+                                            let nodeData: any = {};
+                                            if (nodeDO.data) {
+                                              try {
+                                                nodeData = typeof nodeDO.data === 'string' ? JSON.parse(nodeDO.data) : nodeDO.data;
+                                              } catch (e) {}
+                                            }
+                                            const source = nodeData || node.data || {};
+                                            let lockPersonVal: string | number | undefined = undefined;
+                                            const coLockPersonIds: (string | number)[] = [];
+                                            if (nodeDO.nodeUserList && Array.isArray(nodeDO.nodeUserList)) {
+                                              nodeDO.nodeUserList.forEach((user: any) => {
+                                                if (user.type === 'jtlocker' && user.userId != null) {
+                                                  lockPersonVal = typeof user.userId === 'number' ? user.userId : Number(user.userId);
+                                                } else if (user.type === 'jtcolocker' && user.userId != null) {
+                                                  coLockPersonIds.push(typeof user.userId === 'number' ? user.userId : Number(user.userId));
+                                                }
+                                              });
+                                            }
+                                            let isolationPoints: string[] = [];
+                                            if (nodeDO.isolationPoints) {
+                                              try {
+                                                isolationPoints = typeof nodeDO.isolationPoints === 'string' ? JSON.parse(nodeDO.isolationPoints) : (Array.isArray(nodeDO.isolationPoints) ? nodeDO.isolationPoints : []);
+                                              } catch (e) {}
+                                            } else {
+                                              isolationPoints = source.isolationPoints || [];
+                                            }
+                                            const config = nodeConfigs.find((c: any) => c.type === (nodeDO.type || source.type));
+                                            const isCurrent = node.id === selectedWorkflowNode.id;
+                                            const updatedData = {
+                                              ...source,
+                                              label: nodeDO.nodeName || source.label || config?.label || '',
+                                              icon: nodeDO.nodeIcon || source.icon || '',
+                                              responsible: (nodeDO.workerUserId !== null && nodeDO.workerUserId !== undefined && nodeDO.workerUserId !== 0) ? nodeDO.workerUserId : source.responsible,
+                                              remark: source.remark ?? '',
+                                              submitForm: nodeDO.formId ?? source.submitForm,
+                                              isolationType: (nodeDO.isolationType != null && nodeDO.isolationType !== '') ? String(nodeDO.isolationType) : (source.isolationType ?? ''),
+                                              isolationPoints,
+                                              isolationNode: source.isolationNode || [],
+                                              isolationNodeUuid: nodeDO.isolationNodeUuid ?? source.isolationNodeUuid ?? '',
+                                              lockPerson: lockPersonVal !== undefined ? lockPersonVal : source.lockPerson,
+                                              coLockPersons: coLockPersonIds.length > 0 ? coLockPersonIds : (source.coLockPersons || []),
+                                              notificationMethods: source.notificationMethods || { sms: false, message: false, email: false, app: false },
+                                              notificationPerson: source.notificationPerson ?? '',
+                                              notificationTime: nodeDO.notifyTime ?? source.notificationTime ?? '',
+                                              completed: isCurrent ? true : (node.data?.completed ?? false),
+                                            };
+                                            return { ...node, data: updatedData, selected: isCurrent ? false : node.selected };
+                                          })
+                                        );
+                                        didRefreshFlowNodes = true;
+                                      }
+                                      
+                                      // 更新成功后,再调作业详情接口获取最新数据并合并(可选,用于其他字段与后端一致)
                                       if (workflowWorkId) {
                                         try {
                                           const detailResponse = await workJobApi.selectWorkflowWorkById(workflowWorkId);
-                                          const detail = detailResponse as any;
-                                          if (detail.workflowWorkNodeDOList && Array.isArray(detail.workflowWorkNodeDOList)) {
+                                          const detailRaw = detailResponse as any;
+                                          const detail = detailRaw?.data != null ? detailRaw.data : detailRaw;
+                                          const nodeListFromApi = detail?.workflowWorkNodeDOList;
+                                          if (nodeListFromApi && Array.isArray(nodeListFromApi)) {
                                             // 使用接口返回的最新数据更新 workflowWorkNodeDOList
                                             // 合并更新:保留现有数据,只更新接口返回的数据
-                                            // 先计算合并后的数据,然后再更新状态
                                             const prev = workflowWorkNodeDOList;
                                             console.log('合并更新前的 workflowWorkNodeDOList:', prev);
-                                            console.log('接口返回的 workflowWorkNodeDOList:', detail.workflowWorkNodeDOList);
+                                            console.log('接口返回的 workflowWorkNodeDOList:', nodeListFromApi);
                                             
                                             // 创建一个映射,方便查找
                                             const prevMap = new Map<string | number, WorkflowWorkNodeDO>();
@@ -5210,7 +5292,21 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                               if (item.uuid) prevMap.set(item.uuid, item);
                                             });
                                             
-                                            const updatedList = detail.workflowWorkNodeDOList.map((newItem: WorkflowWorkNodeDO) => {
+                                            const updatedList = nodeListFromApi.map((newItem: WorkflowWorkNodeDO) => {
+                                              // 解除隔离节点且对应本次保存的隔离/方案节点:用本次保存的隔离方案数据复刻到解除隔离节点(上锁人/共锁人),保证与隔离方案一致
+                                              const isReleaseForCurrentIsolation =
+                                                (newItem.type === 'releaseIsolation' || newItem.type === '解除隔离') &&
+                                                newItem.isolationNodeUuid === selectedWorkflowNode.id;
+                                              if (isReleaseForCurrentIsolation) {
+                                                const copiedNodeUserList = nodeUserDOList.length > 0
+                                                  ? nodeUserDOList.map((u: any) => ({ userId: u.userId, type: u.type, ...(u.userName != null && { userName: u.userName }) }))
+                                                  : [];
+                                                return {
+                                                  ...newItem,
+                                                  nodeUserList: copiedNodeUserList,
+                                                };
+                                              }
+                                              
                                               // 查找现有列表中对应的节点(通过 id 或 uuid 匹配)
                                               const existingItem = (newItem.id && prevMap.get(newItem.id)) || 
                                                                   (newItem.uuid && prevMap.get(newItem.uuid)) || 
@@ -5218,43 +5314,28 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                               
                                               // 如果找到现有节点,合并数据(优先使用接口返回的数据,但保留现有数据中接口没有返回的字段)
                                               if (existingItem) {
-                                                // 合并数据,确保关键字段不被覆盖为空
-                                                // 只有当接口返回的值是有效的(不是 null、undefined、0 或空字符串)时,才使用接口的值
                                                 const merged: WorkflowWorkNodeDO = {
                                                   ...existingItem,
                                                   ...newItem,
-                                                  // 如果接口返回的 workerUserId 是 null、undefined 或 0,保留现有的值
                                                   workerUserId: (newItem.workerUserId !== null && newItem.workerUserId !== undefined && newItem.workerUserId !== 0) 
                                                     ? newItem.workerUserId 
                                                     : existingItem.workerUserId,
-                                                  // 如果接口返回的 formId 是 null、undefined 或 0,保留现有的值
                                                   formId: (newItem.formId !== null && newItem.formId !== undefined && newItem.formId !== 0) 
                                                     ? newItem.formId 
                                                     : existingItem.formId,
-                                                  // 如果接口返回的 isolationType 是 null 或 undefined,保留现有的值
                                                   isolationType: (newItem.isolationType !== null && newItem.isolationType !== undefined && newItem.isolationType !== '') 
                                                     ? newItem.isolationType 
                                                     : existingItem.isolationType,
-                                                  // 如果接口返回的 isolationPoints 是 null 或 undefined,保留现有的值
                                                   isolationPoints: (newItem.isolationPoints !== null && newItem.isolationPoints !== undefined && newItem.isolationPoints !== '') 
                                                     ? newItem.isolationPoints 
                                                     : existingItem.isolationPoints,
-                                                  // 如果接口返回的 nodeUserList 是空数组或不存在,保留现有的值
                                                   nodeUserList: (newItem.nodeUserList && Array.isArray(newItem.nodeUserList) && newItem.nodeUserList.length > 0)
                                                     ? newItem.nodeUserList
                                                     : (existingItem.nodeUserList || []),
                                                 };
-                                                
-                                                console.log(`合并节点 ${newItem.uuid || newItem.id}:`, {
-                                                  existing: { workerUserId: existingItem.workerUserId, formId: existingItem.formId },
-                                                  new: { workerUserId: newItem.workerUserId, formId: newItem.formId },
-                                                  merged: { workerUserId: merged.workerUserId, formId: merged.formId }
-                                                });
-                                                
                                                 return merged;
                                               }
                                               
-                                              // 如果没找到,直接使用接口返回的数据
                                               return newItem;
                                             });
                                             
@@ -5265,7 +5346,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                               if (item.uuid) existingUuids.add(item.uuid);
                                             });
                                             
-                                            const newItems = detail.workflowWorkNodeDOList.filter((item: WorkflowWorkNodeDO) => {
+                                            const newItems = nodeListFromApi.filter((item: WorkflowWorkNodeDO) => {
                                               const itemKey = item.id || item.uuid;
                                               return itemKey && !existingUuids.has(itemKey);
                                             });
@@ -5275,6 +5356,75 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                             
                                             // 更新状态
                                             setWorkflowWorkNodeDOList(mergedWorkflowWorkNodeDOList);
+                                            
+                                            // 清除与该隔离节点对应的解除隔离节点的配置缓存,切换节点时从最新 workflowWorkNodeDOList 拉取(显示后端已同步的上锁人/共锁人)
+                                            const releaseIsolationUuids = mergedWorkflowWorkNodeDOList
+                                              .filter((n: WorkflowWorkNodeDO) => (n.type === 'releaseIsolation' || n.type === '解除隔离') && n.isolationNodeUuid === selectedWorkflowNode.id)
+                                              .map((n: WorkflowWorkNodeDO) => n.uuid)
+                                              .filter((id: any): id is string => id != null && id !== '');
+                                            if (releaseIsolationUuids.length > 0) {
+                                              setWorkflowNodeConfigCache(prev => {
+                                                const next = new Map(prev);
+                                                releaseIsolationUuids.forEach(uuid => next.delete(uuid));
+                                                return next;
+                                              });
+                                            }
+                                            
+                                            // 用最新列表刷新流程画布上所有节点的 data,保证画布与数据一致、解除隔离与隔离方案一致
+                                            setWorkflowNodes((nds) =>
+                                              nds.map((node) => {
+                                                const nodeDO = mergedWorkflowWorkNodeDOList.find((item: WorkflowWorkNodeDO) => item.uuid === node.id);
+                                                if (!nodeDO) return node.id === selectedWorkflowNode.id ? { ...node, data: { ...node.data, completed: true }, selected: false } : node;
+                                                let nodeData: any = {};
+                                                if (nodeDO.data) {
+                                                  try {
+                                                    nodeData = typeof nodeDO.data === 'string' ? JSON.parse(nodeDO.data) : nodeDO.data;
+                                                  } catch (e) {}
+                                                }
+                                                const source = nodeData || node.data || {};
+                                                let lockPersonVal: string | number | undefined = undefined;
+                                                const coLockPersonIds: (string | number)[] = [];
+                                                if (nodeDO.nodeUserList && Array.isArray(nodeDO.nodeUserList)) {
+                                                  nodeDO.nodeUserList.forEach((user: any) => {
+                                                    if (user.type === 'jtlocker' && user.userId != null) {
+                                                      lockPersonVal = typeof user.userId === 'number' ? user.userId : Number(user.userId);
+                                                    } else if (user.type === 'jtcolocker' && user.userId != null) {
+                                                      coLockPersonIds.push(typeof user.userId === 'number' ? user.userId : Number(user.userId));
+                                                    }
+                                                  });
+                                                }
+                                                let isolationPoints: string[] = [];
+                                                if (nodeDO.isolationPoints) {
+                                                  try {
+                                                    isolationPoints = typeof nodeDO.isolationPoints === 'string' ? JSON.parse(nodeDO.isolationPoints) : (Array.isArray(nodeDO.isolationPoints) ? nodeDO.isolationPoints : []);
+                                                  } catch (e) {}
+                                                } else {
+                                                  isolationPoints = source.isolationPoints || [];
+                                                }
+                                                const config = nodeConfigs.find((c: any) => c.type === (nodeDO.type || source.type));
+                                                const isCurrent = node.id === selectedWorkflowNode.id;
+                                                const updatedData = {
+                                                  ...source,
+                                                  label: nodeDO.nodeName || source.label || config?.label || '',
+                                                  icon: nodeDO.nodeIcon || source.icon || '',
+                                                  responsible: (nodeDO.workerUserId !== null && nodeDO.workerUserId !== undefined && nodeDO.workerUserId !== 0) ? nodeDO.workerUserId : source.responsible,
+                                                  remark: source.remark ?? '',
+                                                  submitForm: nodeDO.formId ?? source.submitForm,
+                                                  isolationType: (nodeDO.isolationType != null && nodeDO.isolationType !== '') ? String(nodeDO.isolationType) : (source.isolationType ?? ''),
+                                                  isolationPoints,
+                                                  isolationNode: source.isolationNode || [],
+                                                  isolationNodeUuid: nodeDO.isolationNodeUuid ?? source.isolationNodeUuid ?? '',
+                                                  lockPerson: lockPersonVal !== undefined ? lockPersonVal : source.lockPerson,
+                                                  coLockPersons: coLockPersonIds.length > 0 ? coLockPersonIds : (source.coLockPersons || []),
+                                                  notificationMethods: source.notificationMethods || { sms: false, message: false, email: false, app: false },
+                                                  notificationPerson: source.notificationPerson ?? '',
+                                                  notificationTime: nodeDO.notifyTime ?? source.notificationTime ?? '',
+                                                  completed: isCurrent ? true : (node.data?.completed ?? false),
+                                                };
+                                                return { ...node, data: updatedData, selected: isCurrent ? false : node.selected };
+                                              })
+                                            );
+                                            didRefreshFlowNodes = true;
                                           }
                                         } catch (error: any) {
                                           console.error('获取最新节点数据失败:', error);
@@ -5336,33 +5486,35 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                                     newSavedStatusCache.set(selectedWorkflowNode.id, true);
                                     setNodeSavedStatusCache(newSavedStatusCache);
                                     
-                                    // 更新节点显示
-                                    const { isolationMethod, ...restData } = selectedWorkflowNode.data || {};
-                                    const updatedData = {
-                                      ...restData,
-                                      label: workflowNodeConfig.nodeName,
-                                      icon: workflowNodeConfig.nodeIcon,
-                                      responsible: workflowNodeConfig.responsible,
-                                      remark: workflowNodeConfig.remark,
-                                      submitForm: workflowNodeConfig.submitForm,
-                                      isolationType: workflowNodeConfig.isolationType,
-                                      isolationPoints: workflowNodeConfig.isolationPoints,
-                                      isolationNode: workflowNodeConfig.isolationNode,
-                                      isolationNodeUuid: workflowNodeConfig.isolationNodeUuid,
-                                      lockPerson: workflowNodeConfig.lockPerson,
-                                      coLockPersons: workflowNodeConfig.coLockPersons,
-                                      notificationMethods: workflowNodeConfig.notificationMethods,
-                                      notificationPerson: workflowNodeConfig.notificationPerson,
-                                      notificationTime: workflowNodeConfig.notificationTime,
-                                      completed: true, // 标记为已完成
-                                    };
-                                    setWorkflowNodes((nds) =>
-                                      nds.map((node) =>
-                                        node.id === selectedWorkflowNode.id
-                                          ? { ...node, data: updatedData, selected: false }
-                                          : node
-                                      )
-                                    );
+                                    // 更新节点显示(若已用详情合并列表刷新过画布则不再覆盖,避免解除隔离等节点被旧数据覆盖)
+                                    if (!didRefreshFlowNodes) {
+                                      const { isolationMethod, ...restData } = selectedWorkflowNode.data || {};
+                                      const updatedData = {
+                                        ...restData,
+                                        label: workflowNodeConfig.nodeName,
+                                        icon: workflowNodeConfig.nodeIcon,
+                                        responsible: workflowNodeConfig.responsible,
+                                        remark: workflowNodeConfig.remark,
+                                        submitForm: workflowNodeConfig.submitForm,
+                                        isolationType: workflowNodeConfig.isolationType,
+                                        isolationPoints: workflowNodeConfig.isolationPoints,
+                                        isolationNode: workflowNodeConfig.isolationNode,
+                                        isolationNodeUuid: workflowNodeConfig.isolationNodeUuid,
+                                        lockPerson: workflowNodeConfig.lockPerson,
+                                        coLockPersons: workflowNodeConfig.coLockPersons,
+                                        notificationMethods: workflowNodeConfig.notificationMethods,
+                                        notificationPerson: workflowNodeConfig.notificationPerson,
+                                        notificationTime: workflowNodeConfig.notificationTime,
+                                        completed: true,
+                                      };
+                                      setWorkflowNodes((nds) =>
+                                        nds.map((node) =>
+                                          node.id === selectedWorkflowNode.id
+                                            ? { ...node, data: updatedData, selected: false }
+                                            : node
+                                        )
+                                      );
+                                    }
                                     
                                     // 查找下一个未完成的节点(使用新的completedSet,而不是状态中的)
                                     // 优先查找当前节点的子节点

+ 149 - 130
src/components/MyTask.tsx

@@ -1028,6 +1028,8 @@ export default function MyTask() {
       const isIsolation = nodeType === 'isolation' || nodeType === '隔离' || nodeType === '隔离/方案';
       const isReleaseIsolation = nodeType === 'releaseIsolation' || nodeType === '解除隔离';
       const isReturnLock = nodeType === 'returnLock';
+      const isolationType = String(detailDataWithWorkInfo?.isolationType ?? '').trim();
+      const isBlindOrDismantle = isolationType === '0' || isolationType === '2'; // 0=盲板 2=拆除
       
       // 检查任务状态是否为"已通过"
       const isApproved = detailDataWithWorkInfo.approvalStatus === 'approved';
@@ -1206,9 +1208,12 @@ export default function MyTask() {
           setFormLoading(false);
         }
       }
-      // 如果不是隔离类型,且有表单ID,且不是从 formData 获取,则从接口获取表单配置
-      else if (!isIsolation && !isReleaseIsolation && !isReturnLock && hasFormId) {
-        console.log('MyTask: ✅ 满足条件,开始获取表单', { formId, nodeType });
+      // 有 formId 时从接口获取表单:① 审核等非隔离节点 ② 盲板/拆除节点(隔离或解除隔离且隔离方式为盲板或拆除)
+      else if (hasFormId && (
+        (!isIsolation && !isReleaseIsolation && !isReturnLock) ||
+        ((isIsolation || isReleaseIsolation) && isBlindOrDismantle)
+      )) {
+        console.log('MyTask: ✅ 满足条件,开始获取表单', { formId, nodeType, isBlindOrDismantle });
         // 重置表单数据
         setFormData({ rule: [], option: {} });
         setOriginalFields([]);
@@ -1966,13 +1971,18 @@ export default function MyTask() {
                     const deviceLabel = isReleaseIsolation
                       ? (isBlindPlate ? t('form.releaseBlindPlateDeviceNo') : t('form.restoreDeviceNo'))
                       : (isBlindPlate ? t('form.blindPlateDeviceNo') : t('form.dismantleDeviceNo'));
+                    const formIdVal = detailData?.formId;
+                    const hasFormIdForBlindDismantle = formIdVal !== undefined && formIdVal !== null && (
+                      typeof formIdVal === 'number' || (typeof formIdVal === 'string' && String(formIdVal).trim() !== '')
+                    );
                     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%)' }}>
+                        <div style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
                           <Card
                             style={{
                               width: '100%',
                               maxWidth: 500,
+                              margin: '0 auto',
                               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)',
@@ -2088,6 +2098,46 @@ export default function MyTask() {
                                   </Upload.Dragger>
                                 )}
                               </div>
+                              {hasFormIdForBlindDismantle && (
+                                <div style={{ marginTop: 24, paddingTop: 24, borderTop: '1px solid rgba(22, 119, 255, 0.12)' }}>
+                                  {formLoading ? (
+                                    <div className="py-6 text-center text-gray-500">{t('form.formLoading')}</div>
+                                  ) : formData.rule && formData.rule.length > 0 ? (
+                                    (() => {
+                                      const formConfig = formData.option?.formConfig || defaultFormConfig;
+                                      const layoutColumns = formConfig.layoutColumns || 1;
+                                      const gridStyle = layoutColumns > 1 ? {
+                                        display: 'grid',
+                                        gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
+                                        gap: '12px',
+                                        rowGap: '16px',
+                                      } : undefined;
+                                      return (
+                                        <AntdForm
+                                          form={detailForm}
+                                          layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
+                                          size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
+                                          requiredMark={formConfig.hideRequiredMark ? false : undefined}
+                                          labelCol={formConfig.labelWidth ? {
+                                            style: {
+                                              width: `${formConfig.labelWidth}px`,
+                                              textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
+                                            }
+                                          } : undefined}
+                                        >
+                                          <div style={gridStyle} className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}>
+                                            <style>{`.form-detail-grid .ant-form-item { margin-bottom: 12px; }`}</style>
+                                            {(formData.rule || []).map((field: any) => {
+                                              const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
+                                              return renderFieldPreview(fieldWithDisabled);
+                                            })}
+                                          </div>
+                                        </AntdForm>
+                                      );
+                                    })()
+                                  ) : null}
+                                </div>
+                              )}
                             </div>
                           </Card>
                         </div>
@@ -2112,17 +2162,51 @@ export default function MyTask() {
                                   message.warning('请等待文件上传完成或移除未上传成功的文件');
                                   return;
                                 }
+                                if (hasFormIdForBlindDismantle && formData.rule && formData.rule.length > 0) {
+                                  try {
+                                    await detailForm.validateFields();
+                                  } catch (err: any) {
+                                    if (err?.errorFields?.[0]?.errors?.[0]) {
+                                      message.error(err.errorFields[0].errors[0]);
+                                    } else {
+                                      message.error('请完善表单内容');
+                                    }
+                                    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 };
                                   });
+                                  let formDataString: string | undefined = undefined;
+                                  if (hasFormIdForBlindDismantle && formData.rule && formData.rule.length > 0 && originalFields.length > 0) {
+                                    try {
+                                      const values = detailForm.getFieldsValue();
+                                      const fieldsWithValues = originalFields.map((fieldString: string) => {
+                                        try {
+                                          const fieldObj = JSON.parse(fieldString);
+                                          const fieldName = fieldObj.name || fieldObj.field;
+                                          const fieldValue = values[fieldName];
+                                          fieldObj.value = fieldValue !== undefined && fieldValue !== null ? fieldValue : '';
+                                          return JSON.stringify(fieldObj);
+                                        } catch {
+                                          return fieldString;
+                                        }
+                                      });
+                                      const submitData = { conf: formData.option, fields: fieldsWithValues };
+                                      formDataString = JSON.stringify(submitData);
+                                    } catch (e) {
+                                      console.error('MyTask: 盲板/拆除 表单数据序列化失败', e);
+                                    }
+                                  }
                                   await myTaskApi.updateNodeApproval({
                                     nodeId: typeof nodeId === 'number' ? nodeId : Number(nodeId),
                                     approvalStatus: 'approved',
                                     deviceNumber: isolationDeviceNumber,
                                     attachments: JSON.stringify(attachmentUrls),
+                                    ...(formDataString != null ? { formData: formDataString } : {}),
                                   });
                                   message.success(t('common.submit') + t('common.success'));
                                   setDetailVisible(false);
@@ -2223,140 +2307,75 @@ export default function MyTask() {
                   return isolationContent;
                 }
 
-                // 审核类型节点 (review)
+                // 审核类型节点 (review) - 样式与盲板/拆除、确认节点统一
                 if (isReview) {
                   return (
                     <div style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
-                      <div className="space-y-6" style={{ padding: '0 24px', flex: 1, overflowY: 'auto', minHeight: 0 }}>
-                      {/* 自定义表单 */}
-                      {formLoading ? (
-                        <div className="py-8 text-center text-gray-500">{t('form.formLoading')}</div>
-                      ) : formData.rule && formData.rule.length > 0 ? (
-                        <div>
-                          {(() => {
-                            const formConfig = formData.option?.formConfig || defaultFormConfig;
-                            const layoutColumns = formConfig.layoutColumns || 1;
-                            const gridStyle = layoutColumns > 1 ? {
-                              display: 'grid',
-                              gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
-                              gap: '12px',
-                              rowGap: '16px',
-                            } : undefined;
-                            
-                            return (
-                              <AntdForm
-                                form={detailForm}
-                                layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
-                                size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
-                                requiredMark={formConfig.hideRequiredMark ? false : undefined}
-                                labelCol={formConfig.labelWidth ? { 
-                                  style: { 
-                                    width: `${formConfig.labelWidth}px`,
-                                    textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
-                                  } 
-                                } : undefined}
-                              >
-                                <div 
-                                  style={gridStyle} 
-                                  className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}
-                                >
-                                  <style>{`
-                                    .form-detail-grid .ant-form-item {
-                                      margin-bottom: 12px;
-                                    }
-                                  `}</style>
-                                  {(formData.rule || []).map((field: any) => {
-                                    // 如果已通过,禁用所有字段
-                                    const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
-                                    return renderFieldPreview(fieldWithDisabled);
-                                  })}
-                                </div>
-                              </AntdForm>
-                            );
-                          })()}
-                        </div>
-                      ) : (
-                        <div 
+                      <div style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
+                        <Card
                           style={{
-                            display: 'flex',
-                            justifyContent: 'center',
-                            alignItems: 'center',
-                            minHeight: '400px',
-                            padding: '40px 20px'
+                            width: '100%',
+                            maxWidth: 500,
+                            margin: '0 auto',
+                            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 }}
                         >
-                          <Card
-                            style={{
-                              width: '100%',
-                              maxWidth: '500px',
-                              textAlign: 'center',
-                              borderRadius: '12px',
-                              boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
-                              border: '1px solid #e8e8e8'
-                            }}
-                            bodyStyle={{
-                              padding: '40px 30px'
-                            }}
-                          >
-                            <div style={{ marginBottom: '24px' }}>
-                              <CheckCircleOutlined 
-                                style={{ 
-                                  fontSize: '64px', 
-                                  color: '#1890ff',
-                                  display: 'block'
-                                }} 
-                              />
-                            </div>
-                            <div
-                              style={{
-                                fontSize: '20px',
-                                fontWeight: 500,
-                                color: '#333',
-                                lineHeight: '1.6'
-                              }}
-                            >
-                              无需填写表单,直接点击底部按钮提交即可
+                          <div style={{ padding: '24px 32px 28px' }}>
+                            {formLoading ? (
+                              <div className="py-8 text-center text-gray-500">{t('form.formLoading')}</div>
+                            ) : formData.rule && formData.rule.length > 0 ? (
+                              <div className="space-y-4">
+                                {(() => {
+                                  const formConfig = formData.option?.formConfig || defaultFormConfig;
+                                  const layoutColumns = formConfig.layoutColumns || 1;
+                                  const gridStyle = layoutColumns > 1 ? {
+                                    display: 'grid',
+                                    gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
+                                    gap: '12px',
+                                    rowGap: '16px',
+                                  } : undefined;
+                                  return (
+                                    <AntdForm
+                                      form={detailForm}
+                                      layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
+                                      size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
+                                      requiredMark={formConfig.hideRequiredMark ? false : undefined}
+                                      labelCol={formConfig.labelWidth ? { style: { width: `${formConfig.labelWidth}px`, textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left' } } : undefined}
+                                    >
+                                      <div style={gridStyle} className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}>
+                                        <style>{`.form-detail-grid .ant-form-item { margin-bottom: 12px; }`}</style>
+                                        {(formData.rule || []).map((field: any) => {
+                                          const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
+                                          return renderFieldPreview(fieldWithDisabled);
+                                        })}
+                                      </div>
+                                    </AntdForm>
+                                  );
+                                })()}
+                              </div>
+                            ) : (
+                              <div style={{ textAlign: 'center', padding: '32px 24px' }}>
+                                <span style={{ width: 72, height: 72, borderRadius: 18, 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)', marginBottom: 20 }}>
+                                  <CheckCircleOutlined style={{ fontSize: 36, color: '#fff' }} />
+                                </span>
+                                <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6 }}>无需填写表单,直接点击底部按钮提交即可</div>
+                              </div>
+                            )}
+                            <div className="flex items-start" style={{ gap: 16, marginTop: 24, paddingTop: 24, borderTop: '1px solid rgba(22, 119, 255, 0.12)', backgroundColor: 'rgba(22, 119, 255, 0.04)', padding: '16px', borderRadius: 12, border: '1px solid rgba(22, 119, 255, 0.1)' }}>
+                              <label className="text-sm font-medium text-gray-700 whitespace-nowrap pt-2" style={{ width: defaultFormConfig.labelWidth ? `${defaultFormConfig.labelWidth - 20}px` : '80px', textAlign: defaultFormConfig.labelPosition === 'left' ? 'left' : defaultFormConfig.labelPosition === 'right' ? 'right' : 'left', flexShrink: 0 }}>{t('form.reviewComment')}</label>
+                              <div className="flex-1">
+                                <Input.TextArea rows={4} value={approvalComment} onChange={(e) => setApprovalComment(e.target.value)} placeholder={t('form.reviewCommentPlaceholder')} maxLength={500} showCount disabled={isApproved} readOnly={isApproved} style={{ borderRadius: 10, borderColor: '#d9e8ff' }} />
+                              </div>
                             </div>
-                          </Card>
-                        </div>
-                      )}
-
-                      {/* 审核意见 - label 和 textarea 同一行 */}
-                      <div className="flex items-start" style={{ gap: '16px', backgroundColor: '#f5f5f5', padding: '12px 12px 22px 12px', borderRadius: '12px', border: '1px solid #e0e0e0', marginBottom: '12px' }}>
-                        <label 
-                          className="text-sm font-medium text-gray-700 whitespace-nowrap pt-2"
-                          style={{ 
-                            width: defaultFormConfig.labelWidth ? `${defaultFormConfig.labelWidth - 20}px` : '80px',
-                            textAlign: defaultFormConfig.labelPosition === 'left' ? 'left' : defaultFormConfig.labelPosition === 'right' ? 'right' : 'left',
-                            flexShrink: 0
-                          }}
-                        >
-                          {t('form.reviewComment')}
-                        </label>
-                        <div className="flex-1">
-                          <Input.TextArea
-                            rows={4}
-                            value={approvalComment}
-                            onChange={(e) => setApprovalComment(e.target.value)}
-                            placeholder={t('form.reviewCommentPlaceholder')}
-                            maxLength={500}
-                            showCount
-                            disabled={isApproved}
-                            readOnly={isApproved}
-                          />
-                        </div>
-                      </div>
-
+                          </div>
+                        </Card>
                       </div>
-                      {/* 底部按钮 */}
-                      <div 
-                        className="flex justify-end gap-3"
-                        style={{
-                          padding: '16px 24px',
-                          borderTop: '1px solid #f0f0f0',
-                          flexShrink: 0
-                        }}
-                      >
+                      <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%)' }}>
                         <Button
                           danger
                           loading={approvalLoading}

+ 177 - 126
src/components/TaskManagement.tsx

@@ -1041,6 +1041,8 @@ export default function TaskManagement() {
       const isIsolation = nodeType === 'isolation' || nodeType === '隔离' || nodeType === '隔离/方案';
       const isReleaseIsolation = nodeType === 'releaseIsolation' || nodeType === '解除隔离';
       const isReturnLock = nodeType === 'returnLock';
+      const isolationType = String(detailDataWithWorkInfo?.isolationType ?? '').trim();
+      const isBlindOrDismantle = isolationType === '0' || isolationType === '2'; // 0=盲板 2=拆除
       
       // 检查任务状态是否为"已通过"
       const isApproved = detailDataWithWorkInfo.approvalStatus === 'approved';
@@ -1219,9 +1221,12 @@ export default function TaskManagement() {
           setFormLoading(false);
         }
       }
-      // 如果不是隔离类型,且有表单ID,且不是从 formData 获取,则从接口获取表单配置
-      else if (!isIsolation && !isReleaseIsolation && !isReturnLock && hasFormId) {
-        console.log('TaskManagement: ✅ 满足条件,开始获取表单', { formId, nodeType });
+      // 有 formId 时从接口获取表单:① 审核等非隔离节点 ② 盲板/拆除节点(隔离或解除隔离且隔离方式为盲板或拆除)
+      else if (hasFormId && (
+        (!isIsolation && !isReleaseIsolation && !isReturnLock) ||
+        ((isIsolation || isReleaseIsolation) && isBlindOrDismantle)
+      )) {
+        console.log('TaskManagement: ✅ 满足条件,开始获取表单', { formId, nodeType, isBlindOrDismantle });
         // 重置表单数据
         setFormData({ rule: [], option: {} });
         setOriginalFields([]);
@@ -1979,13 +1984,18 @@ export default function TaskManagement() {
                     const deviceLabel = isReleaseIsolation
                       ? (isBlindPlate ? t('form.releaseBlindPlateDeviceNo') : t('form.restoreDeviceNo'))
                       : (isBlindPlate ? t('form.blindPlateDeviceNo') : t('form.dismantleDeviceNo'));
+                    const formIdVal = detailData?.formId;
+                    const hasFormIdForBlindDismantle = formIdVal !== undefined && formIdVal !== null && (
+                      typeof formIdVal === 'number' || (typeof formIdVal === 'string' && String(formIdVal).trim() !== '')
+                    );
                     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%)' }}>
+                        <div style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
                           <Card
                             style={{
                               width: '100%',
                               maxWidth: 500,
+                              margin: '0 auto',
                               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)',
@@ -2101,6 +2111,47 @@ export default function TaskManagement() {
                                   </Upload.Dragger>
                                 )}
                               </div>
+                              {/* 盲板/拆除有 formId 时在设备编号和附件上传下方渲染自定义表单,可滚动查看 */}
+                              {hasFormIdForBlindDismantle && (
+                                <div style={{ marginTop: 24, paddingTop: 24, borderTop: '1px solid rgba(22, 119, 255, 0.12)' }}>
+                                  {formLoading ? (
+                                    <div className="py-6 text-center text-gray-500">{t('form.formLoading')}</div>
+                                  ) : formData.rule && formData.rule.length > 0 ? (
+                                    (() => {
+                                      const formConfig = formData.option?.formConfig || defaultFormConfig;
+                                      const layoutColumns = formConfig.layoutColumns || 1;
+                                      const gridStyle = layoutColumns > 1 ? {
+                                        display: 'grid',
+                                        gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
+                                        gap: '12px',
+                                        rowGap: '16px',
+                                      } : undefined;
+                                      return (
+                                        <AntdForm
+                                          form={detailForm}
+                                          layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
+                                          size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
+                                          requiredMark={formConfig.hideRequiredMark ? false : undefined}
+                                          labelCol={formConfig.labelWidth ? {
+                                            style: {
+                                              width: `${formConfig.labelWidth}px`,
+                                              textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
+                                            }
+                                          } : undefined}
+                                        >
+                                          <div style={gridStyle} className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}>
+                                            <style>{`.form-detail-grid .ant-form-item { margin-bottom: 12px; }`}</style>
+                                            {(formData.rule || []).map((field: any) => {
+                                              const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
+                                              return renderFieldPreview(fieldWithDisabled);
+                                            })}
+                                          </div>
+                                        </AntdForm>
+                                      );
+                                    })()
+                                  ) : null}
+                                </div>
+                              )}
                             </div>
                           </Card>
                         </div>
@@ -2125,17 +2176,51 @@ export default function TaskManagement() {
                                   message.warning('请等待文件上传完成或移除未上传成功的文件');
                                   return;
                                 }
+                                if (hasFormIdForBlindDismantle && formData.rule && formData.rule.length > 0) {
+                                  try {
+                                    await detailForm.validateFields();
+                                  } catch (err: any) {
+                                    if (err?.errorFields?.[0]?.errors?.[0]) {
+                                      message.error(err.errorFields[0].errors[0]);
+                                    } else {
+                                      message.error('请完善表单内容');
+                                    }
+                                    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 };
                                   });
+                                  let formDataString: string | undefined = undefined;
+                                  if (hasFormIdForBlindDismantle && formData.rule && formData.rule.length > 0 && originalFields.length > 0) {
+                                    try {
+                                      const values = detailForm.getFieldsValue();
+                                      const fieldsWithValues = originalFields.map((fieldString: string) => {
+                                        try {
+                                          const fieldObj = JSON.parse(fieldString);
+                                          const fieldName = fieldObj.name || fieldObj.field;
+                                          const fieldValue = values[fieldName];
+                                          fieldObj.value = fieldValue !== undefined && fieldValue !== null ? fieldValue : '';
+                                          return JSON.stringify(fieldObj);
+                                        } catch {
+                                          return fieldString;
+                                        }
+                                      });
+                                      const submitData = { conf: formData.option, fields: fieldsWithValues };
+                                      formDataString = JSON.stringify(submitData);
+                                    } catch (e) {
+                                      console.error('TaskManagement: 盲板/拆除 表单数据序列化失败', e);
+                                    }
+                                  }
                                   await taskManagementApi.updateNodeApproval({
                                     nodeId: typeof nodeId === 'number' ? nodeId : Number(nodeId),
                                     approvalStatus: 'approved',
                                     deviceNumber: isolationDeviceNumber,
                                     attachments: JSON.stringify(attachmentUrls),
+                                    ...(formDataString != null ? { formData: formDataString } : {}),
                                   });
                                   message.success(t('common.submit') + t('common.success'));
                                   setDetailVisible(false);
@@ -2236,138 +2321,104 @@ export default function TaskManagement() {
                   return isolationContent;
                 }
 
-                // 审核类型节点 (review)
+                // 审核类型节点 (review) - 样式与盲板/拆除、确认节点统一:背景渐变 + Card 容器 + 底部按钮栏
                 if (isReview) {
                   return (
                     <div style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
-                      <div className="space-y-6" style={{ padding: '0 24px', flex: 1, overflowY: 'auto', minHeight: 0 }}>
-                      {/* 自定义表单 */}
-                      {formLoading ? (
-                        <div className="py-8 text-center text-gray-500">{t('form.formLoading')}</div>
-                      ) : formData.rule && formData.rule.length > 0 ? (
-                        <div>
-                          {(() => {
-                            const formConfig = formData.option?.formConfig || defaultFormConfig;
-                            const layoutColumns = formConfig.layoutColumns || 1;
-                            const gridStyle = layoutColumns > 1 ? {
-                              display: 'grid',
-                              gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
-                              gap: '12px',
-                              rowGap: '16px',
-                            } : undefined;
-                            
-                            return (
-                              <AntdForm
-                                form={detailForm}
-                                layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
-                                size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
-                                requiredMark={formConfig.hideRequiredMark ? false : undefined}
-                                labelCol={formConfig.labelWidth ? { 
-                                  style: { 
-                                    width: `${formConfig.labelWidth}px`,
-                                    textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
-                                  } 
-                                } : undefined}
-                              >
-                                <div 
-                                  style={gridStyle} 
-                                  className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}
-                                >
-                                  <style>{`
-                                    .form-detail-grid .ant-form-item {
-                                      margin-bottom: 12px;
-                                    }
-                                  `}</style>
-                                  {(formData.rule || []).map((field: any) => {
-                                    // 如果已通过,禁用所有字段
-                                    const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
-                                    return renderFieldPreview(fieldWithDisabled);
-                                  })}
-                                </div>
-                              </AntdForm>
-                            );
-                          })()}
-                        </div>
-                      ) : (
-                        <div 
+                      <div style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
+                        <Card
                           style={{
-                            display: 'flex',
-                            justifyContent: 'center',
-                            alignItems: 'center',
-                            minHeight: '400px',
-                            padding: '40px 20px'
-                          }}
-                        >
-                          <Card
-                            style={{
-                              width: '100%',
-                              maxWidth: '500px',
-                              textAlign: 'center',
-                              borderRadius: '12px',
-                              boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
-                              border: '1px solid #e8e8e8'
-                            }}
-                            bodyStyle={{
-                              padding: '40px 30px'
-                            }}
-                          >
-                            <div style={{ marginBottom: '24px' }}>
-                              <CheckCircleOutlined 
-                                style={{ 
-                                  fontSize: '64px', 
-                                  color: '#1890ff',
-                                  display: 'block'
-                                }} 
-                              />
-                            </div>
-                            <div
-                              style={{
-                                fontSize: '20px',
-                                fontWeight: 500,
-                                color: '#333',
-                                lineHeight: '1.6'
-                              }}
-                            >
-                              无需填写表单,直接点击底部按钮提交即可
-                            </div>
-                          </Card>
-                        </div>
-                      )}
-
-                      {/* 审核意见 - label 和 textarea 同一行 */}
-                      <div className="flex items-start" style={{ gap: '16px', backgroundColor: '#f5f5f5', padding: '12px 12px 22px 12px', borderRadius: '12px', border: '1px solid #e0e0e0', marginBottom: '12px' }}>
-                        <label 
-                          className="text-sm font-medium text-gray-700 whitespace-nowrap pt-2"
-                          style={{ 
-                            width: defaultFormConfig.labelWidth ? `${defaultFormConfig.labelWidth - 20}px` : '80px',
-                            textAlign: defaultFormConfig.labelPosition === 'left' ? 'left' : defaultFormConfig.labelPosition === 'right' ? 'right' : 'left',
-                            flexShrink: 0
+                            width: '100%',
+                            maxWidth: 500,
+                            margin: '0 auto',
+                            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 }}
                         >
-                          {t('form.reviewComment')}
-                        </label>
-                        <div className="flex-1">
-                          <Input.TextArea
-                            rows={4}
-                            value={approvalComment}
-                            onChange={(e) => setApprovalComment(e.target.value)}
-                            placeholder={t('form.reviewCommentPlaceholder')}
-                            maxLength={500}
-                            showCount
-                            disabled={isApproved}
-                            readOnly={isApproved}
-                          />
-                        </div>
-                      </div>
+                          <div style={{ padding: '24px 32px 28px' }}>
+                            {/* 自定义表单 */}
+                            {formLoading ? (
+                              <div className="py-8 text-center text-gray-500">{t('form.formLoading')}</div>
+                            ) : formData.rule && formData.rule.length > 0 ? (
+                              <div className="space-y-4">
+                                {(() => {
+                                  const formConfig = formData.option?.formConfig || defaultFormConfig;
+                                  const layoutColumns = formConfig.layoutColumns || 1;
+                                  const gridStyle = layoutColumns > 1 ? {
+                                    display: 'grid',
+                                    gridTemplateColumns: `repeat(${layoutColumns}, minmax(0, 1fr))`,
+                                    gap: '12px',
+                                    rowGap: '16px',
+                                  } : undefined;
+                                  return (
+                                    <AntdForm
+                                      form={detailForm}
+                                      layout={formConfig.labelPosition === 'top' ? 'vertical' : formConfig.labelPosition === 'left' ? 'horizontal' : 'horizontal'}
+                                      size={formConfig.formSize === 'default' ? 'middle' : formConfig.formSize}
+                                      requiredMark={formConfig.hideRequiredMark ? false : undefined}
+                                      labelCol={formConfig.labelWidth ? {
+                                        style: {
+                                          width: `${formConfig.labelWidth}px`,
+                                          textAlign: formConfig.labelPosition === 'left' ? 'left' : formConfig.labelPosition === 'right' ? 'right' : 'left'
+                                        }
+                                      } : undefined}
+                                    >
+                                      <div style={gridStyle} className={layoutColumns === 1 ? 'space-y-4' : 'form-detail-grid'}>
+                                        <style>{`.form-detail-grid .ant-form-item { margin-bottom: 12px; }`}</style>
+                                        {(formData.rule || []).map((field: any) => {
+                                          const fieldWithDisabled = isApproved ? { ...field, disabled: true, readOnly: true } : field;
+                                          return renderFieldPreview(fieldWithDisabled);
+                                        })}
+                                      </div>
+                                    </AntdForm>
+                                  );
+                                })()}
+                              </div>
+                            ) : (
+                              <div style={{ textAlign: 'center', padding: '32px 24px' }}>
+                                <span style={{ width: 72, height: 72, borderRadius: 18, 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)', marginBottom: 20 }}>
+                                  <CheckCircleOutlined style={{ fontSize: 36, color: '#fff' }} />
+                                </span>
+                                <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6 }}>
+                                  无需填写表单,直接点击底部按钮提交即可
+                                </div>
+                              </div>
+                            )}
 
+                            {/* 审核意见 */}
+                            <div className="flex items-start" style={{ gap: 16, marginTop: 24, paddingTop: 24, borderTop: '1px solid rgba(22, 119, 255, 0.12)', backgroundColor: 'rgba(22, 119, 255, 0.04)', padding: '16px', borderRadius: 12, border: '1px solid rgba(22, 119, 255, 0.1)' }}>
+                              <label className="text-sm font-medium text-gray-700 whitespace-nowrap pt-2" style={{ width: defaultFormConfig.labelWidth ? `${defaultFormConfig.labelWidth - 20}px` : '80px', textAlign: defaultFormConfig.labelPosition === 'left' ? 'left' : defaultFormConfig.labelPosition === 'right' ? 'right' : 'left', flexShrink: 0 }}>
+                                {t('form.reviewComment')}
+                              </label>
+                              <div className="flex-1">
+                                <Input.TextArea
+                                  rows={4}
+                                  value={approvalComment}
+                                  onChange={(e) => setApprovalComment(e.target.value)}
+                                  placeholder={t('form.reviewCommentPlaceholder')}
+                                  maxLength={500}
+                                  showCount
+                                  disabled={isApproved}
+                                  readOnly={isApproved}
+                                  style={{ borderRadius: 10, borderColor: '#d9e8ff' }}
+                                />
+                              </div>
+                            </div>
+                          </div>
+                        </Card>
                       </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