sunqijun 2 miesięcy temu
rodzic
commit
1ab5c2f48e

+ 2 - 1
src/api/production-safety/responsibility-implementation/index.ts

@@ -920,4 +920,5 @@ export function dangerWorkDeleteDangerWork(id) {
     url: `/dangerWork/delete?id=${id}`,
     method: 'delete',
   });
-}
+}
+

+ 42 - 9
src/views/production-safety/implement-safety-duty/responsibility-agree-manage-dept.vue

@@ -194,9 +194,17 @@
     @submit="handleSubmit"
   /> -->
   <el-dialog title="批量签署" v-if="batchSignDialogVisible" v-model="batchSignDialogVisible" width="600">
-    <el-form-item label="">
-      <UploadFiles label="上传附件" :maxCount="1" @uploadSuccess="handleUploadSuccess" :fileList="attachment" />
-    </el-form-item>
+    <el-form label-width="auto">
+      <el-form-item label="上传附件" style="margin-bottom: 20px">
+        <UploadFiles label="上传附件" :maxCount="1" @uploadSuccess="handleUploadSuccess" :fileList="attachment" />
+      </el-form-item>
+      <el-form-item label="下一节点签署人" v-if="hasSignerId">
+        <el-select v-model="signerId" placeholder="请选择" size="large" style="width: 50%" filterable>
+          <el-option v-for="item in userOptions" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+      </el-form-item>
+    </el-form>
+
     <template #footer>
       <div class="dialog-footer">
         <el-button @click="handleCloseBatchSign">取消</el-button>
@@ -208,16 +216,15 @@
   </el-dialog>
 </template>
 <script lang="ts" setup>
-  import { onMounted, ref, reactive } from 'vue';
+  import { onMounted, ref, reactive, computed } from 'vue';
   import dayjs from 'dayjs';
   import { ElMessage } from 'element-plus';
   import { useRouter } from 'vue-router';
   import {
     safetyResponsibilityDeptQueryPage,
-    safetyResponsibilityAdminDelete,
-    safetyResponsibilityAdminIssuedSafety,
     safetyResponsibilityDeptSignOrFeedback,
     safetyResponsibilityDeptBatchSign,
+    queryAvailableUserList,
   } from '@/api/production-safety/responsibility-implementation';
   import { omit } from 'lodash-es';
   import { queryUserGroupPage } from '@/api/system/person-group';
@@ -237,7 +244,7 @@
   const groupList = ref<any[]>([]);
   const submitLoading = ref(false);
   const { id: userId } = useUserInfoHook();
-
+  const signerId = ref('');
   const queryParams = reactive<any>({
     pageNumber: 1,
     pageSize: 10,
@@ -256,10 +263,30 @@
     total: 0,
   });
 
-  const attachment = ref<any[]>([]);
+  const hasSignerId = computed(
+    () =>
+      !!selectedRows.value.every(
+        (item: any) => item.departmentName === '所/中心级部门' || item.departmentName === '科室',
+      ),
+  );
 
+  const attachment = ref<any[]>([]);
+  const userOptions = ref<any[]>([]);
   const selectable = (row) => {
-    return row.status === 2 && !/员工|常驻供应商/.test(row.departmentName);
+    return row.status === 2 && !/员工|常驻供应商/.test(row.departmentName.trim());
+  };
+  // 获取用户列表封装
+  const getUserData = () => {
+    return queryAvailableUserList({
+      pageNumber: 1,
+      pageSize: 200,
+      queryParam: { deptName: '' },
+    }).then((res: any) => {
+      userOptions.value = (res.records || []).map((u: any) => ({
+        value: u.id,
+        label: u.realname,
+      }));
+    });
   };
 
   const handleSelectionChange = (val) => {
@@ -289,6 +316,7 @@
       ElMessage.warning('请选择相同类别的责任书');
       return;
     }
+    console.log('@@:', selectedRows.value);
     batchSignDialogVisible.value = true;
   };
 
@@ -303,6 +331,9 @@
     if (!attachment.value.length) {
       ElMessage.warning('请上传附件');
       return;
+    } else if (!signerId.value && hasSignerId.value) {
+      ElMessage.warning('下一节点签署人');
+      return;
     }
     submitLoading.value = true;
 
@@ -310,6 +341,7 @@
     safetyResponsibilityDeptBatchSign({
       ids: selectedRows.value.map((item: any) => item.id)?.join(','),
       attachment: JSON.stringify(attachments),
+      signerId: signerId?.value ?? undefined,
     })
       .then(() => {
         ElMessage.success('批量签署成功');
@@ -410,6 +442,7 @@
   // };
 
   onMounted(async () => {
+    await getUserData();
     await handleQueryUserGroupPage();
     queryTableList();
   });

+ 4 - 4
src/views/production-safety/implement-safety-duty/responsibility-agree-manage.vue

@@ -15,7 +15,7 @@
     <main class="safety-platform-container__main">
       <div class="search-form">
         <el-form :inline="true">
-          <el-form-item label="安全责任书">
+          <el-form-item label="责任书名称">
             <el-input
               v-model="queryParams.queryParam.responsibilityName"
               placeholder="搜索安全责任书名称"
@@ -60,6 +60,8 @@
         </el-form>
 
         <div>
+          <el-button type="primary" @click="queryTableList">查询</el-button>
+          <el-button @click="handleRestParams">重置</el-button>
           <el-button
             type="primary"
             @click="
@@ -67,10 +69,8 @@
                 name: 'createResponsibilityAgree',
               })
             "
-            >添加
+            >添加责任书
           </el-button>
-          <el-button type="primary" @click="queryTableList">查询</el-button>
-          <el-button @click="handleRestParams">重置</el-button>
         </div>
       </div>
 

+ 51 - 3
src/views/production-safety/implement-safety-duty/responsibility-notice-manage-admin/list.vue

@@ -71,19 +71,40 @@
           <el-table-column label="下发数" prop="issuedQuantity" width="120" />
           <el-table-column label="反馈人数" prop="signedQuantity" width="120" />
           <el-table-column label="反馈比例" prop="signedRatio" width="120" />
-          <el-table-column label="责任通知文档" prop="attachment" width="150">
+          <el-table-column label="责任通知文档" prop="attachment" width="250">
             <template #default="scope">
-              <div
+              <!-- <div
                 class="file-container--div"
                 v-for="item in unformatAttachment(scope.row.attachment)"
                 :key="item.fileId"
               >
                 <span class="file-container--div__name">{{ item.fileName }}</span>
+              </div> -->
+              <div
+                class="file-container--div"
+                v-for="item in unformatAttachment(scope.row.attachment)"
+                :key="item.fileId"
+              >
+                <img
+                  class="file-container--div__icon"
+                  @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
+                  :src="FILE_TYPE_ICON[item.fileType]"
+                />
+                <span
+                  class="file-container--div__name"
+                  @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
+                  >{{ item.fileName }}</span
+                >
+                <img
+                  class="file-container--div__download"
+                  :src="DownloadIcon"
+                  @click="downloadFile(item.fileUrl, item.fileName)"
+                />
               </div>
             </template>
           </el-table-column>
           <el-table-column label="计划完成时间" prop="planEndTime" width="150" />
-          <el-table-column fixed="right" min-width="300" label="操作">
+          <el-table-column fixed="right" min-width="240" label="操作">
             <template #default="scope">
               <el-button
                 link
@@ -153,6 +174,7 @@
     v-model.visible="notifySendOpen"
     @submit="handleNotifySend"
   />
+  <PreviewOnline ref="previewOnlineRef" />
 </template>
 <script lang="ts" setup>
   import { onMounted, ref, reactive, watch } from 'vue';
@@ -169,6 +191,9 @@
 
   import { unformatAttachment } from '@/components/UploadFiles/utils';
   import { downloadFile } from '@/views/disaster/utils';
+  import { FILE_TYPE_ICON } from '@/components/UploadFiles/constants';
+  import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
+  import DownloadIcon from '@/views/disaster/disaster-control/src/svg/download.svg';
 
   import NotifySend from './components/NotifySend.vue';
 
@@ -206,6 +231,14 @@
     },
   );
 
+  // 预览
+  const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
+  const previewOnline = (url: string | undefined, type: keyof typeof FILE_TYPE_ICON) => {
+    if (url) {
+      previewOnlineRef.value?.open(url, type);
+    }
+  };
+
   const handleSend = (scope) => {
     notifySendOpen.value = true;
     currentRow.value = scope.row;
@@ -348,4 +381,19 @@
     display: flex;
     justify-content: flex-end;
   }
+  .file-container--div {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+    .file-container--div__name {
+      min-width: 0;
+      max-width: 100px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+    img {
+      cursor: pointer;
+    }
+  }
 </style>

+ 1 - 1
src/views/production-safety/implement-safety-duty/responsibility-notice-manage-admin/notice-view.vue

@@ -66,7 +66,7 @@
         <el-table :data="tableData.data">
           <el-table-column label="责任书名称" prop="responsibilityName" width="180" />
           <el-table-column label="状态" prop="statusName" width="100" />
-          <el-table-column label="类别名称" prop="departmentName" />
+          <el-table-column label="类别名称" prop="safetyAreaTypeName" />
 
           <el-table-column label="通知区域" prop="safetyAreaName" />
           <el-table-column label="计划完成时间" prop="planEndTime" />

+ 0 - 1
src/views/production-safety/risk-identification-and-control/construction-safety-manage/list.vue

@@ -175,7 +175,6 @@
   } from '@/api/production-safety/responsibility-implementation';
   import BasicDialog from '@/components/BasicDialog.vue';
   import { getApprovalNodeInstanceList } from '@/api/approval/approval';
-  import { submitReceiptRecordApprovalProcess } from '@/api/receiptRecord';
   import { omit } from 'lodash-es';
   import { useUserInfoHook } from '@/hooks/useUserInfoHook';
   import type { ApprovalNodeInstanceType } from '@/views/system/approval/types';

+ 696 - 1
src/views/production-safety/risk-identification-and-control/hazard-approval-manage/add.vue

@@ -1 +1,696 @@
-<template> xxx </template>
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title"><BreadcrumbBack /> 新增危险作业申请</div>
+    </header>
+
+    <main class="safety-platform-container__main">
+      <el-form ref="formRef" :model="formValue" :rules="rules" label-width="160px" label-position="right">
+        <div class="section-title">基础信息</div>
+        <div class="form-grid">
+          <el-form-item label="审批流程" prop="templateId" class="span-full">
+            <el-select v-model="formValue.templateId" placeholder="请选择审批流程" size="large" clearable>
+              <el-option v-for="opt in approvalOptions" :key="opt.id" :label="opt.templateName" :value="opt.id" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="作业类型" prop="hazardOperationType">
+            <el-select size="large" v-model="formValue.hazardOperationType" class="w-100">
+              <el-option :value="1" label="有限空间作业" />
+              <el-option :value="2" label="高处作业" />
+              <el-option :value="3" label="临时用电" />
+              <el-option :value="4" label="动火作业" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="申请单位" prop="applicationUnitName"
+            ><el-input size="large" v-model="formValue.applicationUnitName"
+          /></el-form-item>
+          <el-form-item label="申请人" prop="applicantName"
+            ><el-input size="large" v-model="formValue.applicantName"
+          /></el-form-item>
+          <el-form-item label="申请部门" prop="applicationDepartment"
+            ><el-cascader
+              v-model="formValue.applicationDepartmentId"
+              size="large"
+              ref="cascaderRef"
+              :options="firstLevelDepts"
+              :props="cascaderProp"
+              :show-all-levels="false"
+              placeholder="请选择部门名称"
+              filterable
+              @change="handleChangeDept"
+              style="width: 100%"
+          /></el-form-item>
+          <el-form-item label="联系电话" prop="applicantPhone"
+            ><el-input size="large" v-model="formValue.applicantPhone"
+          /></el-form-item>
+          <el-form-item label="备注信息" prop="remark" class="span-full">
+            <el-input size="large" v-model="formValue.remark" type="textarea" :rows="2" placeholder="备注信息..." />
+          </el-form-item>
+        </div>
+
+        <div class="form-line-divider"></div>
+
+        <template v-if="formValue.hazardOperationType === 1">
+          <div class="section-title">有限空间作业详情</div>
+          <div class="form-grid">
+            <el-form-item label="所属单位" prop="space.confinedSpaceUnit"
+              ><el-input size="large" v-model="formValue.space.confinedSpaceUnit"
+            /></el-form-item>
+            <el-form-item label="空间名称" prop="space.confinedSpaceName"
+              ><el-input size="large" v-model="formValue.space.confinedSpaceName"
+            /></el-form-item>
+            <el-form-item label="负责人" prop="space.unitResponsible"
+              ><el-input size="large" v-model="formValue.space.unitResponsible"
+            /></el-form-item>
+            <el-form-item label="监护人" prop="space.supervisor"
+              ><el-input size="large" v-model="formValue.space.supervisor"
+            /></el-form-item>
+            <el-form-item label="作业人" prop="space.operator"
+              ><el-input size="large" v-model="formValue.space.operator" placeholder="多人请用逗号分隔"
+            /></el-form-item>
+            <el-form-item label="其他作业" prop="space.otherSpecialOps"
+              ><el-input size="large" v-model="formValue.space.otherSpecialOps"
+            /></el-form-item>
+            <el-form-item label="开始时间" prop="space.operationStartTime"
+              ><el-date-picker
+                size="large"
+                v-model="formValue.space.operationStartTime"
+                value-format="YYYY-MM-DD"
+                class="w-100"
+            /></el-form-item>
+            <el-form-item label="结束时间" prop="space.operationEndTime"
+              ><el-date-picker
+                size="large"
+                v-model="formValue.space.operationEndTime"
+                value-format="YYYY-MM-DD"
+                class="w-100"
+            /></el-form-item>
+          </div>
+
+          <div class="sub-group-box">
+            <div class="sub-label is-required-manual">环境当前浓度指标 (初始评估)</div>
+            <el-table :data="[formValue.space]" border class="density-table">
+              <el-table-column label="有毒有害物质 (toxicHazardous)">
+                <template #default="scope">
+                  <el-form-item prop="space.toxicHazardous" :rules="r" label-width="0" class="m-0"
+                    ><el-input v-model="scope.row.toxicHazardous"
+                  /></el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column label="可燃气 (flammable)">
+                <template #default="scope">
+                  <el-form-item prop="space.flammable" :rules="r" label-width="0" class="m-0"
+                    ><el-input v-model="scope.row.flammable"
+                  /></el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column label="氧含量 (oxygenContent)">
+                <template #default="scope">
+                  <el-form-item prop="space.oxygenContent" :rules="r" label-width="0" class="m-0"
+                    ><el-input v-model="scope.row.oxygenContent"
+                  /></el-form-item>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+
+          <div class="form-grid mt-20">
+            <el-form-item label="作业内容" prop="space.operationContent" class="span-full"
+              ><el-input size="large" v-model="formValue.space.operationContent" type="textarea"
+            /></el-form-item>
+            <el-form-item label="危害辨识" prop="space.hazardIdentification" class="span-full"
+              ><el-input size="large" v-model="formValue.space.hazardIdentification" type="textarea"
+            /></el-form-item>
+            <el-form-item label="作业附件" class="span-full"
+              ><UploadFiles
+                label="上传附件"
+                @upload-success="(l) => handleUpload('space', 'attachment', l)"
+                :fileList="listMap['space.attachment']"
+            /></el-form-item>
+          </div>
+        </template>
+
+        <template v-else-if="formValue.hazardOperationType === 2">
+          <div class="section-title">高处作业详情</div>
+          <div class="form-grid">
+            <el-form-item label="作业地点" prop="highAltitude.operationLocation"
+              ><el-input size="large" v-model="formValue.highAltitude.operationLocation"
+            /></el-form-item>
+            <el-form-item label="作业单位" prop="highAltitude.operationUnit"
+              ><el-input size="large" v-model="formValue.highAltitude.operationUnit"
+            /></el-form-item>
+            <el-form-item label="作业高度(m)" prop="highAltitude.operationHeight"
+              ><el-input-number
+                size="large"
+                v-model="formValue.highAltitude.operationHeight"
+                class="w-100"
+                :controls="false"
+            /></el-form-item>
+            <el-form-item label="作业类别" prop="highAltitude.operationType"
+              ><el-input size="large" v-model="formValue.highAltitude.operationType" placeholder="如:悬挂作业"
+            /></el-form-item>
+            <el-form-item label="作业人姓名" prop="highAltitude.operatorName"
+              ><el-input size="large" v-model="formValue.highAltitude.operatorName"
+            /></el-form-item>
+            <el-form-item label="监护人" prop="highAltitude.supervisor"
+              ><el-input size="large" v-model="formValue.highAltitude.supervisor"
+            /></el-form-item>
+            <el-form-item label="开始时间" prop="highAltitude.operationStartTime"
+              ><el-date-picker
+                size="large"
+                v-model="formValue.highAltitude.operationStartTime"
+                value-format="YYYY-MM-DD"
+                class="w-100"
+            /></el-form-item>
+            <el-form-item label="结束时间" prop="highAltitude.operationEndTime"
+              ><el-date-picker
+                size="large"
+                v-model="formValue.highAltitude.operationEndTime"
+                value-format="YYYY-MM-DD"
+                class="w-100"
+            /></el-form-item>
+            <el-form-item label="作业内容" prop="highAltitude.operationContent" class="span-full"
+              ><el-input size="large" v-model="formValue.highAltitude.operationContent" type="textarea"
+            /></el-form-item>
+            <el-form-item label="危害辨识" prop="highAltitude.hazardIdentification" class="span-full"
+              ><el-input size="large" v-model="formValue.highAltitude.hazardIdentification" type="textarea"
+            /></el-form-item>
+            <el-form-item label="作业附件" class="span-full"
+              ><UploadFiles
+                label="上传附件"
+                @upload-success="(l) => handleUpload('highAltitude', 'attachment', l)"
+                :fileList="listMap['highAltitude.attachment']"
+            /></el-form-item>
+          </div>
+        </template>
+
+        <template v-else-if="formValue.hazardOperationType === 3">
+          <div class="section-title">临时用电详情</div>
+          <div class="form-grid">
+            <el-form-item label="需求部门" prop="electricityList.requestDepartment"
+              ><el-input size="large" v-model="formValue.electricityList.requestDepartment"
+            /></el-form-item>
+            <el-form-item label="用电类型" prop="electricityList.electricityType">
+              <el-select v-model="formValue.electricityList.electricityType" class="w-100">
+                <el-option :value="1" label="长期" /><el-option :value="2" label="临时" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="责任人" prop="electricityList.contactPerson"
+              ><el-input size="large" v-model="formValue.electricityList.contactPerson"
+            /></el-form-item>
+            <el-form-item label="设备功率" prop="electricityList.equipmentPower"
+              ><el-input size="large" v-model="formValue.electricityList.equipmentPower"
+            /></el-form-item>
+            <el-form-item label="开始时间" prop="electricityList.operationStartTime"
+              ><el-date-picker
+                size="large"
+                v-model="formValue.electricityList.operationStartTime"
+                value-format="YYYY-MM-DD"
+                class="w-100"
+            /></el-form-item>
+            <el-form-item label="结束时间" prop="electricityList.operationEndTime"
+              ><el-date-picker
+                size="large"
+                v-model="formValue.electricityList.operationEndTime"
+                value-format="YYYY-MM-DD"
+                class="w-100"
+            /></el-form-item>
+            <el-form-item label="保护措施" prop="electricityList.safety" class="span-full"
+              ><el-input size="large" v-model="formValue.electricityList.safety"
+            /></el-form-item>
+            <el-form-item label="用电事由" prop="electricityList.reason" class="span-full"
+              ><el-input size="large" v-model="formValue.electricityList.reason" type="textarea"
+            /></el-form-item>
+            <el-form-item label="申请附件" class="span-full"
+              ><UploadFiles
+                label="上传附件"
+                @upload-success="(l) => handleUpload('electricityList', 'attachment', l)"
+                :fileList="listMap['electricityList.attachment']"
+            /></el-form-item>
+          </div>
+        </template>
+
+        <template v-else-if="formValue.hazardOperationType === 4">
+          <div class="section-title">动火作业详情</div>
+          <div class="sub-label">A. 动火任务信息</div>
+          <div class="form-grid">
+            <el-form-item label="动火地点" prop="hot.hotWorkLocation"
+              ><el-input size="large" v-model="formValue.hot.hotWorkLocation"
+            /></el-form-item>
+            <el-form-item label="动火级别" prop="hot.hotWorkLevel">
+              <el-select v-model="formValue.hot.hotWorkLevel" class="w-100">
+                <el-option :value="1" label="一级" /><el-option :value="2" label="二级" /><el-option
+                  :value="3"
+                  label="三级"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="特殊时段" prop="hot.isSpecialPeriod">
+              <el-radio-group v-model="formValue.hot.isSpecialPeriod">
+                <el-radio :label="1">是 (节假日/重大活动)</el-radio>
+                <el-radio :label="0">否</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="动火类型" prop="hot.hotWorkType"
+              ><el-input size="large" v-model="formValue.hot.hotWorkType" placeholder="气焊/电焊/打磨等"
+            /></el-form-item>
+            <el-form-item label="开始时间" prop="hot.hotWorkStart"
+              ><el-date-picker
+                size="large"
+                v-model="formValue.hot.hotWorkStart"
+                value-format="YYYY-MM-DD"
+                class="w-100"
+            /></el-form-item>
+            <el-form-item label="结束时间" prop="hot.hotWorkEnd"
+              ><el-date-picker size="large" v-model="formValue.hot.hotWorkEnd" value-format="YYYY-MM-DD" class="w-100"
+            /></el-form-item>
+            <el-form-item label="动火任务" prop="hot.hotWorkTask" class="span-full"
+              ><el-input size="large" v-model="formValue.hot.hotWorkTask" type="textarea"
+            /></el-form-item>
+            <el-form-item label="危险性分析" prop="hot.hazardAnalysis" class="span-full"
+              ><el-input size="large" v-model="formValue.hot.hazardAnalysis" type="textarea"
+            /></el-form-item>
+          </div>
+
+          <div class="sub-label">B. 责任人信息</div>
+          <div class="form-grid">
+            <el-form-item label="动火作业人员" prop="hot.hotWorkman"
+              ><el-input size="large" v-model="formValue.hot.hotWorkman"
+            /></el-form-item>
+            <el-form-item label="动火人电话" prop="hot.hotWorkContact"
+              ><el-input size="large" v-model="formValue.hot.hotWorkContact"
+            /></el-form-item>
+            <el-form-item label="施工单位监护人" prop="hot.constructionSupervisor"
+              ><el-input size="large" v-model="formValue.hot.constructionSupervisor"
+            /></el-form-item>
+            <el-form-item label="监护人电话" prop="hot.supervisorContact"
+              ><el-input size="large" v-model="formValue.hot.supervisorContact"
+            /></el-form-item>
+            <el-form-item label="现场负责人" prop="hot.constructionSiteLeader"
+              ><el-input size="large" v-model="formValue.hot.constructionSiteLeader"
+            /></el-form-item>
+            <el-form-item label="教育人" prop="hot.constructionEducator"
+              ><el-input size="large" v-model="formValue.hot.constructionEducator"
+            /></el-form-item>
+            <el-form-item label="申请部门监护人" prop="hot.hotWorkSupervisor"
+              ><el-input size="large" v-model="formValue.hot.hotWorkSupervisor"
+            /></el-form-item>
+            <el-form-item label="部门监护电话" prop="hot.supervisorNumber"
+              ><el-input size="large" v-model="formValue.hot.supervisorNumber"
+            /></el-form-item>
+          </div>
+
+          <div class="sub-label">C. 防护措施与附件</div>
+          <div class="form-grid">
+            <el-form-item label="安全防护措施" prop="hot.fireSafetyMeasures" class="span-full"
+              ><el-input size="large" v-model="formValue.hot.fireSafetyMeasures" type="textarea"
+            /></el-form-item>
+            <el-form-item label="现场检查情况" prop="hot.supervisorMeasures" class="span-full"
+              ><el-input size="large" v-model="formValue.hot.supervisorMeasures" type="textarea"
+            /></el-form-item>
+
+            <el-form-item label="动火现场照片"
+              ><UploadFiles
+                label="上传附件"
+                @upload-success="(l) => handleUpload('hot', 'photos', l)"
+                :fileList="listMap['hot.photos']"
+            /></el-form-item>
+            <el-form-item label="身份证复印件"
+              ><UploadFiles
+                label="上传附件"
+                @upload-success="(l) => handleUpload('hot', 'idCard', l)"
+                :fileList="listMap['hot.idCard']"
+            /></el-form-item>
+            <el-form-item label="特种作业证"
+              ><UploadFiles
+                label="上传附件"
+                @upload-success="(l) => handleUpload('hot', 'optionCard', l)"
+                :fileList="listMap['hot.optionCard']"
+            /></el-form-item>
+            <el-form-item label="安全教育记录"
+              ><UploadFiles
+                label="上传附件"
+                @upload-success="(l) => handleUpload('hot', 'safetyEducationPlan', l)"
+                :fileList="listMap['hot.safetyEducationPlan']"
+            /></el-form-item>
+            <el-form-item label="施工方案"
+              ><UploadFiles
+                label="上传附件"
+                @upload-success="(l) => handleUpload('hot', 'constructionPlan', l)"
+                :fileList="listMap['hot.constructionPlan']"
+            /></el-form-item>
+            <el-form-item label="准备工作照片"
+              ><UploadFiles
+                label="上传附件"
+                @upload-success="(l) => handleUpload('hot', 'preparationPhotos', l)"
+                :fileList="listMap['hot.preparationPhotos']"
+            /></el-form-item>
+            <el-form-item label="管理协议"
+              ><UploadFiles
+                label="上传附件"
+                @upload-success="(l) => handleUpload('hot', 'safetyManagementAgreement', l)"
+                :fileList="listMap['hot.safetyManagementAgreement']"
+            /></el-form-item>
+          </div>
+        </template>
+
+        <div class="form-line-divider"></div>
+
+        <div class="section-title-flex">
+          <span class="is-required-manual">安全措施清单</span>
+          <el-button type="primary" @click="addRow('measure')">+ 新增措施</el-button>
+        </div>
+        <el-table :data="formValue.measure" border class="mb-20">
+          <el-table-column label="序号" prop="serialNumber" width="100" align="center" />
+          <el-table-column label="措施内容"
+            ><template #default="scope"><el-input v-model="scope.row.safetyMeasure" /></template
+          ></el-table-column>
+          <el-table-column label="确认人" width="200"
+            ><template #default="scope"><el-input v-model="scope.row.confirmer" /></template
+          ></el-table-column>
+          <el-table-column label="操作" width="80" align="center">
+            <template #default="scope"
+              ><el-button type="primary" link @click="removeRow('measure', scope.$index)">删除</el-button></template
+            >
+          </el-table-column>
+        </el-table>
+
+        <div class="section-title-flex">
+          <span class="is-required-manual">危害分析记录</span>
+          <el-button type="primary" @click="addRow('analysis')">+ 新增记录</el-button>
+        </div>
+        <el-table :data="formValue.analysis" border>
+          <el-table-column label="检测部位"
+            ><template #default="scope"><el-input v-model="scope.row.analysisPosition" /></template
+          ></el-table-column>
+          <el-table-column label="有毒物质及浓度"
+            ><template #default="scope"><el-input v-model="scope.row.toxicSubstance" /></template
+          ></el-table-column>
+          <el-table-column label="可燃气及浓度"
+            ><template #default="scope"><el-input v-model="scope.row.flammableGas" /></template
+          ></el-table-column>
+          <el-table-column label="氧含量"
+            ><template #default="scope"><el-input v-model="scope.row.oxygenContent" /></template
+          ></el-table-column>
+          <el-table-column label="检测人" width="130"
+            ><template #default="scope"><el-input v-model="scope.row.analyst" /></template
+          ></el-table-column>
+          <el-table-column label="检测时间" width="220"
+            ><template #default="scope">
+              <el-date-picker
+                v-model="scope.row.analysisTime"
+                value-format="YYYY-MM-DD"
+                style="width: 100%"
+              /> </template
+          ></el-table-column>
+          <el-table-column label="操作" width="80" align="center">
+            <template #default="scope"
+              ><el-button type="primary" link @click="removeRow('analysis', scope.$index)">删除</el-button></template
+            >
+          </el-table-column>
+        </el-table>
+      </el-form>
+    </main>
+
+    <footer class="safety-platform-container__footer">
+      <el-button size="large" @click="router.back()">取 消</el-button>
+      <el-button type="primary" size="large" :loading="loading" @click="handleSave">提交</el-button>
+    </footer>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, reactive, onMounted } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
+  import { getAllApproval } from '@/api/approval/approval';
+  import { dangerWorkSaveDangerWork } from '@/api/production-safety/responsibility-implementation';
+  import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
+  import { getAllDepartments } from '@/api/auth/dept';
+
+  const router = useRouter();
+  const formRef = ref<any>(null);
+  const loading = ref(false);
+  const approvalOptions = ref<any[]>([]);
+  const firstLevelDepts = ref<any[]>([]);
+  const cascaderRef = ref<any>(null);
+  const cascaderProp = {
+    expandTrigger: 'click',
+    checkStrictly: true,
+    // emitPath: false,
+    value: 'id',
+    label: 'deptName',
+  };
+
+  // 附件管理 (存储 fileList 用于回显)
+  const listMap = reactive<any>({
+    'space.attachment': [],
+    'highAltitude.attachment': [],
+    'electricityList.attachment': [],
+    'hot.photos': [],
+    'hot.idCard': [],
+    'hot.optionCard': [],
+    'hot.safetyEducationPlan': [],
+    'hot.constructionPlan': [],
+    'hot.preparationPhotos': [],
+    'hot.safetyManagementAgreement': [],
+  });
+
+  const formValue = reactive<any>({
+    templateId: '',
+    applicationDepartment: '',
+    applicationDepartmentId: [],
+    applicationUnitName: '',
+    applicantName: '',
+    applicantPhone: '',
+    hazardOperationType: 1,
+    remark: '',
+    space: {
+      confinedSpaceUnit: '',
+      confinedSpaceName: '',
+      operationContent: '',
+      operationStartTime: '',
+      operationEndTime: '',
+      unitResponsible: '',
+      supervisor: '',
+      operator: '',
+      otherSpecialOps: '',
+      hazardIdentification: '',
+      attachment: '',
+      toxicHazardous: '',
+      flammable: '',
+      oxygenContent: '',
+    },
+    highAltitude: {
+      operationStartTime: '',
+      operationEndTime: '',
+      operationLocation: '',
+      operationContent: '',
+      operationHeight: undefined,
+      operationType: '',
+      operationUnit: '',
+      supervisor: '',
+      operatorName: '',
+      operatorJob: '',
+      hazardIdentification: '',
+      attachment: '',
+    },
+    electricityList: {
+      requestDepartment: '',
+      electricityType: 2,
+      operationStartTime: '',
+      operationEndTime: '',
+      reason: '',
+      contactPerson: '',
+      equipmentPower: '',
+      safety: '',
+      attachment: '',
+    },
+    hot: {
+      hotWorkLocation: '',
+      hotWorkLevel: 2,
+      isSpecialPeriod: 0,
+      hotWorkStart: '',
+      hotWorkEnd: '',
+      hotWorkTask: '',
+      hotWorkType: '',
+      hotWorkman: '',
+      hotWorkContact: '',
+      hazardAnalysis: '',
+      fireSafetyMeasures: '',
+      constructionSupervisor: '',
+      supervisorContact: '',
+      constructionSiteLeader: '',
+      constructionEducator: '',
+      hotWorkSupervisor: '',
+      supervisorNumber: '',
+      supervisorMeasures: '',
+      photos: '',
+      idCard: '',
+      optionCard: '',
+      safetyEducationPlan: '',
+      constructionPlan: '',
+      preparationPhotos: '',
+      safetyManagementAgreement: '',
+    },
+    measure: [{ serialNumber: 1, safetyMeasure: '', confirmer: '' }],
+    analysis: [
+      { toxicSubstance: '', flammableGas: '', oxygenContent: '', analysisTime: '', analysisPosition: '', analyst: '' },
+    ],
+  });
+
+  const r = { required: true, message: '此项必填', trigger: ['blur', 'change'] };
+
+  // 核心校验规则
+  const rules = reactive({
+    templateId: [r],
+    applicationUnitName: [r],
+    applicantName: [r],
+    applicationDepartment: [r],
+    applicantPhone: [r],
+    remark: [r],
+    // 有限空间
+    'space.confinedSpaceUnit': [r],
+    'space.confinedSpaceName': [r],
+    'space.unitResponsible': [r],
+    'space.supervisor': [r],
+    'space.operator': [r],
+    'space.otherSpecialOps': [r],
+    'space.operationStartTime': [r],
+    'space.operationEndTime': [r],
+    'space.operationContent': [r],
+    'space.hazardIdentification': [r],
+    // 高处作业
+    'highAltitude.operationLocation': [r],
+    'highAltitude.operationUnit': [r],
+    'highAltitude.operationHeight': [r],
+    'highAltitude.operationType': [r],
+    'highAltitude.operatorName': [r],
+    'highAltitude.supervisor': [r],
+    'highAltitude.operationStartTime': [r],
+    'highAltitude.operationEndTime': [r],
+    'highAltitude.operationContent': [r],
+    'highAltitude.hazardIdentification': [r],
+    // 临时用电
+    'electricityList.requestDepartment': [r],
+    'electricityList.electricityType': [r],
+    'electricityList.contactPerson': [r],
+    'electricityList.equipmentPower': [r],
+    'electricityList.operationStartTime': [r],
+    'electricityList.operationEndTime': [r],
+    'electricityList.safety': [r],
+    'electricityList.reason': [r],
+    // 动火作业
+    'hot.hotWorkLocation': [r],
+    'hot.hotWorkLevel': [r],
+    'hot.hotWorkType': [r],
+    'hot.hotWorkStart': [r],
+    'hot.hotWorkEnd': [r],
+    'hot.hotWorkman': [r],
+    'hot.hotWorkContact': [r],
+    'hot.constructionSupervisor': [r],
+    'hot.supervisorContact': [r],
+    'hot.constructionSiteLeader': [r],
+    'hot.constructionEducator': [r],
+    'hot.hotWorkSupervisor': [r],
+    'hot.supervisorNumber': [r],
+    'hot.hotWorkTask': [r],
+    'hot.hazardAnalysis': [r],
+    'hot.fireSafetyMeasures': [r],
+    'hot.supervisorMeasures': [r],
+  });
+  const handleChangeDept = () => {
+    const deptInfo = cascaderRef.value?.getCheckedNodes();
+    if (deptInfo?.[0]) {
+      formValue.applicationDepartment = deptInfo[0].label;
+      formValue.applicationDepartmentId = deptInfo[0].pathValues;
+    }
+  };
+  const handleUpload = (objName: string, keyName: string, fileList: any[]) => {
+    const propPath = `${objName}.${keyName}`;
+    listMap[propPath] = fileList;
+    formValue[objName][keyName] = fileList.map((f) => f.response?.url || f.url || f.name).join(',');
+  };
+
+  const handleSave = () => {
+    formRef.value.validate((valid: boolean) => {
+      if (!valid) return ElMessage.error('请完善表单必填信息');
+
+      loading.value = true;
+
+      // 1. 深拷贝表单数据
+      const submitData = JSON.parse(JSON.stringify(formValue));
+
+      // 2. 根据类型删除不相关的对象
+      const type = submitData.hazardOperationType;
+      if (type !== 1) delete submitData.space;
+      if (type !== 2) delete submitData.highAltitude;
+      if (type !== 3) delete submitData.electricityList;
+      if (type !== 4) delete submitData.hot;
+
+      // 3. 执行提交
+      dangerWorkSaveDangerWork(submitData)
+        .then(() => {
+          ElMessage.success('提交成功');
+          router.back();
+        })
+        .finally(() => (loading.value = false));
+    });
+  };
+
+  const addRow = (type: string) => {
+    if (type === 'measure') {
+      formValue.measure.push({ serialNumber: formValue.measure.length + 1, safetyMeasure: '', confirmer: '' });
+    } else {
+      formValue.analysis.push({
+        toxicSubstance: '',
+        flammableGas: '',
+        oxygenContent: '',
+        analysisTime: '',
+        analysisPosition: '',
+        analyst: '',
+      });
+    }
+  };
+
+  const removeRow = (type: string, index: number) => {
+    formValue[type].splice(index, 1);
+    if (type === 'measure') formValue.measure.forEach((item: any, idx: number) => (item.serialNumber = idx + 1));
+  };
+  const getDeptData = () => {
+    getAllDepartments().then((res) => {
+      firstLevelDepts.value = formatDeptTree(res);
+    });
+  };
+
+  onMounted(async () => {
+    await getDeptData();
+    const res = await getAllApproval();
+    approvalOptions.value = res || [];
+  });
+</script>
+
+<style lang="scss" scoped>
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/page-details-layout.scss' as *;
+  .safety-platform-container {
+    .section-title-flex {
+      display: flex;
+      justify-content: space-between;
+      margin: 15px 0;
+    }
+    &__main {
+      padding: 24px;
+      background-color: #fff;
+    }
+    :deep(.el-form-item) {
+      margin-right: 20px;
+      margin-bottom: 24px;
+      vertical-align: top;
+      .el-form-item__label {
+        font-weight: bold;
+      }
+    }
+  }
+</style>