Browse Source

Merge branch 'feat/production-safety' into 'dev'

Feat/production safety

See merge request product-group-fe/sfy-safety-group/sfy-safety!357
ai0197(吴云丰) 1 month ago
parent
commit
4e3ebf33b4
26 changed files with 881 additions and 268 deletions
  1. 1 1
      src/api/production-safety-system/index.ts
  2. 16 0
      src/api/production-safety/responsibility-implementation/index.ts
  3. 1 0
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/areaCheckPlanManagement/areaCheckPlanManagement.vue
  4. 8 3
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/areaCheckPlanManagement/components/areaCheckPlanManagementDetail.vue
  5. 1 1
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/areaCheckPlanManagementDept/components/areaCheckPlanManagementDeptDetail.vue
  6. 8 8
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/employeeReportHiddenTroubleManagement/employeeReportHiddenTroubleManagement.vue
  7. 17 7
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/hiddenTroubleAccountManagement/components/hiddenTroubleAccountManagementDetail.vue
  8. 7 7
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/hiddenTroubleReviewManagement/hiddenTroubleReviewManagement.vue
  9. 6 0
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagement/oneByOneManagement.vue
  10. 9 2
      src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagementDept/components/oneByOneManagementDeptDetail.vue
  11. 187 18
      src/views/production-safety/implement-safety-duty/components/IssueSafetyResponsibility.vue
  12. 41 0
      src/views/production-safety/implement-safety-duty/config/table.ts
  13. 1 1
      src/views/production-safety/implement-safety-duty/responsibility-agree-manage-dept.vue
  14. 3 3
      src/views/production-safety/productionSafetySystem/collegeFileManagement/collegeFileManagement.vue
  15. 5 4
      src/views/production-safety/productionSafetySystem/doubleSystemManagement/doubleSystemManagement.vue
  16. 4 3
      src/views/production-safety/productionSafetySystem/lawManagement/lawManagement.vue
  17. 202 90
      src/views/production-safety/risk-identification-and-control/construction-safety-manage/add.vue
  18. 3 4
      src/views/production-safety/risk-identification-and-control/construction-safety-manage/audit.vue
  19. 145 35
      src/views/production-safety/risk-identification-and-control/construction-safety-manage/edit.vue
  20. 5 7
      src/views/production-safety/risk-identification-and-control/construction-safety-manage/list.vue
  21. 144 34
      src/views/production-safety/risk-identification-and-control/construction-safety-manage/view.vue
  22. 23 15
      src/views/production-safety/risk-identification-and-control/hazard-approval-manage/add.vue
  23. 23 15
      src/views/production-safety/risk-identification-and-control/hazard-approval-manage/edit.vue
  24. 2 2
      src/views/production-safety/safetyAssessment/evaluationSystem/components/EvaluationTarget.vue
  25. 1 1
      src/views/production-safety/safetyAssessment/evaluationSystem/configs/targetTables.ts
  26. 18 7
      src/views/production-safety/safetyAssessment/evaluationSystem/evaluationSystem.vue

+ 1 - 1
src/api/production-safety-system/index.ts

@@ -1240,7 +1240,7 @@ export function sandAreaCheckRecordToProductionHiddenDanger(data: SandAreaCheckR
 export function cancelAreaCheckPlanManage(id: number) {
   return http.request({
     url: `${ADMIN_BASE}/updateStatusAreaCheckPlanManage?id=${id}`,
-    method: 'post',
+    method: 'delete',
   });
 }
 

+ 16 - 0
src/api/production-safety/responsibility-implementation/index.ts

@@ -78,6 +78,8 @@ export function safetyResponsibilityAdminQueryDetail(params) {
   });
 }
 
+
+
 /**
  * 作废指定 ID 的安全责任书(管理端)
  * @param id - 安全责任书 ID
@@ -103,6 +105,20 @@ export function safetyResponsibilityAdminQueryIssuedObject(params) {
   });
 }
 
+
+/**
+ * 分页查询分组下面的人(管理端)
+ * @param params - 查询条件,如责任书 ID、分页参数等
+ * @returns Promise<QueryPageResponse>
+ */
+export function queryUserByGroupIds(params) {
+  return http.request({
+    url: '/safetyResponsibility/admin/queryUserByGroupIds',
+    method: 'post',
+    params
+  });
+}
+
 /**
  * 导出已下发对象列表(管理端)
  * @param params - 导出条件参数

+ 1 - 0
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/areaCheckPlanManagement/areaCheckPlanManagement.vue

@@ -19,6 +19,7 @@
                   v-model="tableQuery.queryParam.keyword"
                   placeholder="搜索检查场所或计划名称"
                   class="act-search-input"
+                  style="min-width: 200px;"
                   clearable
                 />
               </div>

+ 8 - 3
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/areaCheckPlanManagement/components/areaCheckPlanManagementDetail.vue

@@ -152,9 +152,9 @@
           start-placeholder="开始日期"
           end-placeholder="结束日期"
           value-format="YYYY-MM-DD"
-          style="margin-right: 12px"
+          style="margin-right: 12px;max-width: 280px;"
         />
-        <el-button type="primary" @click="onRecordSearch">查询</el-button>
+        <el-button type="primary" class="view-record-toolbar-btn" @click="onRecordSearch">查询</el-button>
         <el-button @click="onRecordExport">导出</el-button>
       </div>
       <BasicTable
@@ -1172,7 +1172,7 @@ import { id } from 'element-plus/es/locale';
           align-items: center;
           justify-content: flex-end;
           flex-shrink: 0;
-          width: 200px;
+          width: 260px;
           padding: 0 12px;
           background-color: #f5f5f5;
           border-right: 1px solid #dcdfe6;
@@ -1222,4 +1222,9 @@ import { id } from 'element-plus/es/locale';
   .view-record-table {
     margin-bottom: 16px;
   }
+
+  .view-record-toolbar-btn{
+    margin-left: auto;
+  }
+
 </style>

+ 1 - 1
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/areaCheckPlanManagementDept/components/areaCheckPlanManagementDeptDetail.vue

@@ -952,7 +952,7 @@
             align-items: center;
             justify-content: flex-end;
             flex-shrink: 0;
-            width: 200px;
+            width: 260px;
             padding: 0 12px;
             background-color: #f5f5f5;
             border-right: 1px solid #dcdfe6;

+ 8 - 8
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/employeeReportHiddenTroubleManagement/employeeReportHiddenTroubleManagement.vue

@@ -17,7 +17,7 @@
                 <span>隐患问题:</span>
                 <el-input
                   v-model="tableQuery.queryParam.keyword"
-                  placeholder="请输入隐患问题、地点、姓名、工号或联系电话"
+                  placeholder="请输入隐患问题"
                   class="act-search-input"
                 />
               </div>
@@ -32,17 +32,17 @@
               </div>
               <div class="select-box--item">
                 <span>提交类型:</span>
-                <el-input
-                  v-model="tableQuery.queryParam.sourceTypeName"
-                  placeholder="请输入提交类型"
-                  class="act-search-input"
-                />
+                <el-select v-model="tableQuery.queryParam.sourceTypeName" placeholder="请选择提交类型" clearable>
+                  <el-option label="员工提交" value="员工提交" />
+                  <el-option label="供应商提交" value="供应商提交" />
+                  <el-option label="第三方提交" value="第三方提交" />
+                </el-select>
               </div>
             </section>
             <section class="search-btn">
               <el-button type="primary" @click="handleSearch">查询</el-button>
               <el-button @click="handleReset">重置</el-button>
-              <el-button plain class="search-table-container--button" @click="handleDownload"> 导出 </el-button>
+              <el-button plain @click="handleDownload"> 导出 </el-button>
             </section>
           </div>
         </header>
@@ -64,7 +64,7 @@
               <div class="action-container--div" style="justify-content: left">
                 <!-- 待审核:显示审核和查看 -->
                 <template v-if="scope.row.status === 1">
-                  <ActionButton text="审核" @click="handleApprove(scope.row.id)" />
+                  <ActionButton text="审核" @click="handleApprove(scope.row.id)" v-if="scope.row.canApprove"/>
                   <ActionButton text="查看" @click="handleView(scope.row.id)" />
                 </template>
                 <!-- 审核通过(需求部门通过或安全部门通过):显示查看和入账 -->

+ 17 - 7
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/hiddenTroubleAccountManagement/components/hiddenTroubleAccountManagementDetail.vue

@@ -19,10 +19,10 @@
           style="width: 450px"
         >
           <el-option
-            v-for="option in ruleFormConfig.find((c) => c.prop === 'typeId')?.selectOptions || []"
-            :key="option.value"
-            :label="option.label"
-            :value="option.value"
+            v-for="option in hiddenRiskCategoriesList || []"
+            :key="option.id"
+            :label="option.categoryName"
+            :value="option.id"
           />
         </el-select>
       </el-form-item>
@@ -235,7 +235,7 @@
   <footer class="safety-platform-container__footer">
     <el-button @click="router.back()">返回</el-button>
     <template v-if="isReviewMode">
-      <el-button type="warning" @click="openReviewRejectDialog">审查不通过</el-button>
+      <el-button type="primary" @click="openReviewRejectDialog">审查不通过</el-button>
       <el-button type="primary" @click="handleReviewPass">审查通过</el-button>
     </template>
     <template v-else-if="!isViewMode">
@@ -376,14 +376,17 @@
   import type { DeptTree } from '@/types/dept/type';
   import { queryAvailableUserList } from '@/api/production-safety/responsibility-implementation';
   import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
-import { ru } from 'element-plus/es/locale';
+  import { ru } from 'element-plus/es/locale';
+  import {
+    queryDangerTypePage
+  } from '@/api/production-safety';
 
   const router = useRouter();
   const route = useRoute();
   const searchDeptName = ref('');
   const operate = computed(() => (route.query.operate as string) || 'hidden-trouble-account-create');
   const currentId = computed(() => Number(route.query.id));
-
+  const hiddenRiskCategoriesList = ref();
   const isCreateMode = computed(() => operate.value === 'hidden-trouble-account-create');
   const isEditMode = computed(() => operate.value === 'hidden-trouble-account-edit');
   const isViewMode = computed(
@@ -868,6 +871,13 @@ const attachmentsFileList = ref([]) as any
   onMounted(async () => {
     cloneRuleFormData();
     await loadDeptAndUserOptions();
+    await queryDangerTypePage({
+      pageNumber: 1,
+      pageSize: 9999,
+      queryParam: {}
+    }).then(res => {
+      hiddenRiskCategoriesList.value = res?.records || [];
+    });
     if (isEditMode.value || isViewMode.value) {
       await getDetail();
       if (route.query.action === 'review' && isViewMode.value && canReview.value) {

+ 7 - 7
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/hiddenTroubleReviewManagement/hiddenTroubleReviewManagement.vue

@@ -34,17 +34,17 @@
               </div>
               <div class="select-box--item">
                 <span>提交类型:</span>
-                <el-input
-                  v-model="tableQuery.queryParam.sourceTypeName"
-                  placeholder="请输入提交类型"
-                  class="act-search-input"
-                />
+                <el-select v-model="tableQuery.queryParam.sourceTypeName" placeholder="请选择提交类型" clearable>
+                  <el-option label="员工提交" value="员工提交" />
+                  <el-option label="供应商提交" value="供应商提交" />
+                  <el-option label="第三方提交" value="第三方提交" />
+                </el-select>
               </div>
             </section>
             <section class="search-btn">
               <el-button type="primary" @click="handleSearch">查询</el-button>
               <el-button @click="handleReset">重置</el-button>
-              <el-button plain class="search-table-container--button" @click="handleDownload"> 导出 </el-button>
+              <el-button plain @click="handleDownload"> 导出 </el-button>
             </section>
           </div>
         </header>
@@ -66,7 +66,7 @@
               <div class="action-container--div" style="justify-content: left">
                 <!-- 待审核:显示审核和查看 -->
                 <template v-if="scope.row.status === 1">
-                  <ActionButton text="审核" @click="handleApprove(scope.row.id)" />
+                  <ActionButton text="审核" @click="handleApprove(scope.row.id)" v-if="scope.row.canApprove"/>
                   <ActionButton text="查看" @click="handleView(scope.row.id)" />
                 </template>
                 <!-- 审核通过(需求部门通过或安全部门通过):显示查看和入账 -->

+ 6 - 0
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagement/oneByOneManagement.vue

@@ -489,6 +489,12 @@
   async function handleIssueSubmit() {
     await issueFormRef.value?.validate?.().catch(() => {});
     if (!currentIssueRow.value) return;
+    const startDate = issueForm.value.planStartDate;
+    const endDate = issueForm.value.planEndDate;
+    if (startDate && endDate && endDate < startDate) {
+      ElMessage.warning('计划结束时间不能早于计划开始时间');
+      return;
+    }
     try {
       await issueDrawLessons({
         associationOtId: currentIssueRow.value.id,

+ 9 - 2
src/views/production-safety/hiddenTroubleInvestigationAndGovernance/oneByOneManagementDept/components/oneByOneManagementDeptDetail.vue

@@ -112,12 +112,13 @@
           <div class="row row--upload" v-if="!isViewMode">
             <div class="col col--full">
               <div class="label">选择附件:</div>
-              <div class="value value--attachment">
+              <div class="value">
                 <UploadFiles
+                  label="上传附件"
                   v-if="!isViewMode"
-                  label="选择附件"
                   :file-list="materialAttachmentList"
                   @uploadSuccess="handleMaterialUploadSuccess"
+                  class="custom-upload-files"
                 />
               </div>
             </div>
@@ -557,4 +558,10 @@
       }
     }
   }
+
+  .custom-upload-files :deep(.upload-button) {
+    height: 34.49px;
+    line-height: 34.49px;
+  }
+
 </style>

+ 187 - 18
src/views/production-safety/implement-safety-duty/components/IssueSafetyResponsibility.vue

@@ -52,22 +52,28 @@
           </el-select>
       </el-form-item>
 
-      <el-form-item label="下发分组名称" prop="userGroupId" v-else>
-        <el-select
-          multiple
-          size="large"
-          v-model="formData.userGroupId"
-          placeholder="请选择下发分组"
-          style="width: 100%"
-        >
-          <el-option
-            v-for="group in groupList"
-            :disabled="originUserGroupId.includes(group.id)"
-            :key="group.id"
-            :label="group.name"
-            :value="group.id"
-          />
-        </el-select>
+      <el-form-item label="下发分组名称" prop="userGroupId"  v-else>
+        <el-row style="width:100%" :gutter="12">
+            <el-col :span="16">
+              <el-select
+                multiple
+                size="large"
+                v-model="formData.userGroupId"
+                placeholder="请选择下发分组"
+                >
+                <el-option
+                    v-for="group in groupList"
+                    :disabled="originUserGroupId.includes(group.id)"
+                    :key="group.id"
+                    :label="group.name"
+                    :value="group.id"
+                />
+                </el-select>
+            </el-col>
+            <el-col :span="8">
+                <el-button link type="primary" @click="reviewStaffData">查看人员详情</el-button>
+            </el-col>
+        </el-row>
       </el-form-item>
 
       <el-form-item label="多人签署设置" prop="signConfig" v-if="currentDepartmentKey === 'B'">
@@ -226,6 +232,50 @@
       <el-button @click="handleClose">取消</el-button>
     </template>
   </el-dialog>
+  <el-dialog v-model="staffDialogVisible" width="70%" title="查看分组人员详情" @close="handleRestParams">
+    <div class="search-form">
+        <el-form :inline="true">
+          <el-form-item>
+            <el-input
+              v-model="tableQuery.queryParam.keyword"
+              placeholder="搜索工号/姓名/分组名称"
+              style="width: 170px"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-cascader
+                ref="cascaderRef"
+                style="width: 100%"
+                size="large"
+                v-model="tableQuery.queryParam.deptId"
+                :options="firstLevelDepts"
+                :props="deptProp"
+                :show-all-levels="false"
+                placeholder="请选择部门"
+                :clearable="false"
+                @change="handleDeptChange"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="handleSearch">查询</el-button>
+            <el-button @click="handleRestParams">重置</el-button>
+          </el-form-item>
+        </el-form>
+        
+      </div>
+
+    <h4 style="margin:10px 0" v-if="messages">{{ messages.toString() }}</h4>
+    <div class="batch-table">
+        <BasicTable
+        ref="basicTableRef"
+        :tableData="staffTableData"
+        :tableConfig="tableConfig"
+        @update:pageSize="handleSizeChange"
+        @update:pageNumber="handleCurrentChange"
+        >
+        </BasicTable>
+    </div>
+  </el-dialog>
 </template>
 
 <script setup lang="ts">
@@ -236,7 +286,19 @@
   import {
     queryAvailableUserList,
     safetyResponsibilityAdminQueryDetail,
+    queryUserByGroupIds
   } from '@/api/production-safety/responsibility-implementation';
+  import {STAFF_TABLE_COLUMNS, TABLE_OPTIONS} from "../config/table"
+  import type { QueryPageRequest } from '@/types/basic-query';
+  import useTableConfig from '@/hooks/useTableConfigHook';
+  import BasicTable from '@/components/BasicTable.vue';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
+
+  // 表格
+  const basicTableRef = ref<InstanceType<typeof BasicTable>>();
+
+  const { tableConfig, pagination } = useTableConfig(STAFF_TABLE_COLUMNS, TABLE_OPTIONS);
 
   const props = defineProps<{
     modelValue: boolean;
@@ -248,6 +310,8 @@
 
   const emit = defineEmits(['close', 'submit', 'update:modelValue']);
   const submitLoading = ref(false);
+  const staffDialogVisible = ref(false);
+  const staffTableData = ref<any[]>([]);
   const ruleFormRef = ref<FormInstance>();
   const cascaderRef = ref<any>();
   const userList = reactive<{
@@ -271,6 +335,17 @@
     deptId: [],
   });
 
+  const tableQuery = reactive<QueryPageRequest<any>>({
+    pageNumber: pagination.pageNumber,
+    pageSize: pagination.pageSize,
+    queryParam: {
+        userGroupIds: [], // 用户分组
+        keyword: '',// 支持搜索工号,姓名,分组名称
+        deptName: undefined,// 部门
+        deptId: undefined // 部门id 
+    },
+  });
+
   const cascaderProp = {
     expandTrigger: 'click',
     checkStrictly: props.currentDepartmentKey === 'default',
@@ -281,7 +356,32 @@
       return originDeptId.value.some((path) => isEqual(path, node.pathValues));
     },
   };
-
+  const firstLevelDepts = ref<any[]>([]);
+  const deptProp = {
+    expandTrigger: 'click',
+    checkStrictly: true,
+    // emitPath: false,
+    value: 'id',
+    label: 'deptName',
+  };
+  const getDeptData = () => {
+    getAllDepartments().then((res) => {
+      firstLevelDepts.value = formatDeptTree(res);
+    });
+  };
+  const handleDeptChange = () => {
+  const nodes = cascaderRef.value?.getCheckedNodes();
+  
+  if (nodes && nodes.length > 0) {
+    // 当前选中的【唯一节点】
+    const currentDept = nodes[0];
+    tableQuery.queryParam.deptId = currentDept.value
+    // tableQuery.queryParam.deptName = currentDept.label
+    // console.log('✅ 当前选中部门完整数据:', currentDept.data); 
+    // console.log('ID:', currentDept.value);
+    // console.log('名称:', currentDept.label);
+  }
+};
   watch(
     () => [props.currentDepartmentKey, props.departmentOptions],
     ([currentDepartmentKey, departmentOptions]) => {
@@ -331,7 +431,67 @@
     // if (!query) return;
     getUserData(dataName, query);
   };
+  
+  const handleSizeChange = (value: number) => {
+    pagination.pageSize = value;
+    tableQuery.pageSize = value;
+    getTableData();
+  };
+    const handleCurrentChange = (value: number) => {
+    pagination.pageNumber = value;
+    tableQuery.pageNumber = value;
+    getTableData();
+  };
+  const messages = ref([])
+  async function getTableData() {
+    tableConfig.loading = true;
+    try {
+      tableQuery.queryParam.userGroupIds =  formData.userGroupId
+      const res = await queryUserByGroupIds(tableQuery);
+      if (res) {
+        // 映射返回数据字段到表格字段
+        messages.value = res.messages
+        staffTableData.value = res.pages.records;
+        pagination.total = res.pages.totalRow;
+      }
+    } catch (e) {
+      staffTableData.value = [];
+      pagination.total = 0;
+    } finally {
+      tableConfig.loading = false;
+    }
+  }
 
+  const handleSearch = () => {
+    pagination.pageNumber = 1;
+    tableQuery.pageNumber = 1;
+    getTableData();
+  };
+  const handleRestParams = async ()=>{
+  pagination.pageNumber = 1;
+  pagination.pageSize = 10
+  Object.assign(tableQuery, {
+    pageNumber: pagination.pageNumber,
+    pageSize: pagination.pageSize,
+    queryParam: {
+        userGroupIds: formData.userGroupId,
+        keyword: undefined,
+        deptName: undefined,
+        deptId: undefined
+    }
+  })
+  await getTableData();
+  }
+  const reviewStaffData = async ()=>{
+    console.log(formData.userGroupId, formData.userGroupId.length)
+    if(formData.userGroupId.length === 0){
+        ElMessage.error('请先选择分组')
+        return
+    }
+    staffDialogVisible.value = true
+    await getDeptData();
+    await getTableData();
+  }
   // const handleRemoveTag = (val, field, originData) => {
   //   console.log('has:', originData, val);
 
@@ -456,4 +616,13 @@
   }
 </script>
 
-<style scoped lang="scss"></style>
+<style scoped lang="scss">
+.flex {
+    display: flex;
+    gap: 6px;
+}
+.align-center {
+    align-items: center;
+}
+
+</style>

+ 41 - 0
src/views/production-safety/implement-safety-duty/config/table.ts

@@ -0,0 +1,41 @@
+import type { TableColumnProps } from '@/types/basic-table';
+// 基础表格样式配置
+export const TABLE_OPTIONS = {
+  emptyText: '暂无数据',
+  loading: true,
+  maxHeight: 'calc(70vh - 150px)',
+};
+export const STAFF_TABLE_COLUMNS: TableColumnProps[] = [
+  {
+    label: '编号',
+    type: 'index',
+    align: 'center',
+    width: '80px',
+    fixed: 'left',
+  },
+  {
+    label: '员工工号',
+    prop: 'staffNo',
+    align: 'left',
+    minWidth: '200px',
+    fixed: 'left',
+  },
+  {
+    label: '员工姓名',
+    prop: 'realname',
+    align: 'center',
+    minWidth: '200px',
+  },
+  {
+    label: '所属部门',
+    prop: 'deptName',
+    align: 'center',
+    minWidth: '180px',
+  },
+  {
+    label: '分组名称',
+    prop: 'userGroupName',
+    align: 'center',
+    minWidth: '140px',
+  },
+];

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

@@ -305,7 +305,7 @@
   };
 
   const handleOpenBatchSign = () => {
-    const selectedData = selectedRows.value;
+    const selectedData = selectedRows.value as any;
     if (!selectedData.length) {
       ElMessage.warning('请选择需要批量签署的责任书');
       return;

+ 3 - 3
src/views/production-safety/productionSafetySystem/collegeFileManagement/collegeFileManagement.vue

@@ -13,9 +13,6 @@
             <el-button plain class="search-table-container--button" @click="handleImport">
               导入
             </el-button>
-            <el-button plain class="search-table-container--button" @click="handleDownload">
-              导出
-            </el-button>
           </div>
 
           <div class="act-search">
@@ -66,6 +63,9 @@
             <section class="search-btn">
               <el-button type="primary" @click="handleSearch">查询</el-button>
               <el-button @click="handleReset">重置</el-button>
+              <el-button plain @click="handleDownload">
+                导出
+              </el-button>
             </section>
           </div>
         </header>

+ 5 - 4
src/views/production-safety/productionSafetySystem/doubleSystemManagement/doubleSystemManagement.vue

@@ -7,15 +7,12 @@
       <div class="search-table-container">
         <header>
           <div style="position: relative">
-            <el-button type="primary" class="search-table-container--button" @click="handleCreate">
+            <el-button type="primary" :icon="Plus" class="search-table-container--button" @click="handleCreate">
               添加
             </el-button>
             <el-button plain class="search-table-container--button" @click="handleImport">
               导入
             </el-button>
-            <el-button plain class="search-table-container--button" @click="handleDownload">
-              导出
-            </el-button>
           </div>
 
           <div class="act-search">
@@ -66,6 +63,9 @@
             <section class="search-btn">
               <el-button type="primary" @click="handleSearch">查询</el-button>
               <el-button @click="handleReset">重置</el-button>
+              <el-button plain @click="handleDownload">
+                导出
+              </el-button>
             </section>
           </div>
         </header>
@@ -117,6 +117,7 @@
 <script setup lang="ts">
   import { onMounted, reactive, ref } from 'vue';
   import { ElMessage } from 'element-plus';
+  import {Plus} from "@element-plus/icons-vue"
   import BasicTable from '@/components/BasicTable.vue';
   import useTableConfig from '@/hooks/useTableConfigHook';
   import ActionButton from '@/components/ActionButton.vue';

+ 4 - 3
src/views/production-safety/productionSafetySystem/lawManagement/lawManagement.vue

@@ -13,9 +13,7 @@
             <el-button plain class="search-table-container--button" @click="handleImport">
               导入
             </el-button>
-            <el-button plain class="search-table-container--button" @click="handleDownload">
-              导出
-            </el-button>
+
           </div>
 
           <div class="act-search">
@@ -66,6 +64,9 @@
             <section class="search-btn">
               <el-button type="primary" @click="handleSearch">查询</el-button>
               <el-button @click="handleReset">重置</el-button>
+              <el-button plain  @click="handleDownload">
+                导出
+              </el-button>
             </section>
           </div>
         </header>

+ 202 - 90
src/views/production-safety/risk-identification-and-control/construction-safety-manage/add.vue

@@ -31,15 +31,30 @@
         </el-form-item>
 
         <el-form-item label="施工项目负责人" prop="projectManagerName">
-          <el-input v-model="formValue.projectManagerName" size="large" placeholder="输入施工项目负责人" style="width: 330px" />
+          <el-input
+            v-model="formValue.projectManagerName"
+            size="large"
+            placeholder="输入施工项目负责人"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工项目负责人电话1" prop="projectManagerPhone1">
-          <el-input v-model="formValue.projectManagerPhone1" size="large" placeholder="输入施工项目负责人电话1" style="width: 330px" />
+          <el-input
+            v-model="formValue.projectManagerPhone1"
+            size="large"
+            placeholder="输入施工项目负责人电话1"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工项目负责人电话2" prop="projectManagerPhone2">
-          <el-input v-model="formValue.projectManagerPhone2" size="large" placeholder="输入施工项目负责人电话2" style="width: 330px" />
+          <el-input
+            v-model="formValue.projectManagerPhone2"
+            size="large"
+            placeholder="输入施工项目负责人电话2"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工现场安全负责人" prop="siteSafetyManagerName">
@@ -52,11 +67,21 @@
         </el-form-item>
 
         <el-form-item label="施工现场安全负责人电话1" prop="siteSafetyPhone1">
-          <el-input v-model="formValue.siteSafetyPhone1" size="large" placeholder="输入施工现场安全负责人电话1" style="width: 330px" />
+          <el-input
+            v-model="formValue.siteSafetyPhone1"
+            size="large"
+            placeholder="输入施工现场安全负责人电话1"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工现场安全负责人电话2" prop="siteSafetyPhone2">
-          <el-input v-model="formValue.siteSafetyPhone2" size="large" placeholder="输入施工现场安全负责人电话2" style="width: 330px" />
+          <el-input
+            v-model="formValue.siteSafetyPhone2"
+            size="large"
+            placeholder="输入施工现场安全负责人电话2"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="开始时间" prop="projectStartTime">
@@ -91,6 +116,7 @@
               v-for="t in ['水电', '泥瓦', '木工', '焊接', '气割', '登高', '密闭', '特种驾驶', '其他']"
               :key="t"
               :label="t"
+              :value="t"
             />
           </el-checkbox-group>
         </el-form-item>
@@ -120,23 +146,29 @@
         </el-form-item>
 
         <div style="width: 100%; height: 1px; background: #eee; margin: 20px 0"></div>
-        <div style="width: 100%; margin-bottom: 20px; font-weight: bold; padding-left: 10px"
-          >附件上传清单(全项必填)</div>
+        <div style="width: 100%; margin-bottom: 20px; font-weight: bold; padding-left: 10px">附件上传清单</div>
 
         <el-form-item
           v-for="item in attachmentConfigs"
           :key="item.prop"
           :label="item.label"
-          label-width="240px"
+          label-width="250px"
           :prop="item.prop"
           class="label"
-          style="width: 43.6%;"
+          style="width: 43%"
         >
-          <UploadFiles
-            label="上传文件"
-            @upload-success="(fileList) => handleUploadSuccess(item.prop, fileList)"
-            :fileList="attachmentLists[item.prop]"
-          />
+          <el-row style="width: 100%">
+            <el-col :span="12">
+              <UploadFiles
+                label="上传文件"
+                @upload-success="(fileList) => handleUploadSuccess(item.prop, fileList)"
+                :fileList="attachmentLists[item.prop]"
+              />
+            </el-col>
+            <el-col :span="12">
+              <el-button type="primary" link @click="templateDownload(item)"> 模版下载 </el-button>
+            </el-col>
+          </el-row>
         </el-form-item>
 
         <div style="width: 100%; height: 1px; background: #eee; margin: 20px 0"></div>
@@ -171,6 +203,7 @@
 
   import { constructionSafetySaveConstruction } from '@/api/production-safety/responsibility-implementation';
   import { formatAttachmentList } from '@/components/UploadFiles/utils';
+  import { downloadFile } from '@/views/disaster/utils';
 
   const router = useRouter();
   const formRef = ref<any>(null);
@@ -178,26 +211,42 @@
   const tradeArray = ref<string[]>([]);
   const approvalOptions = ref<any[]>([]);
   const attachmentConfigs = [
-    { label: '施工人员身份信息(复印件,施工单位盖章)', prop: 'personnelIdAttachment' },
-    
-    { label: '施工方提供施工安全交底(施工方敲章)', prop: 'safetyCommitmentAttachment' },
-
-    { label: '施工方提供施工人员安全教育(施工人员签字并敲章)', prop: 'safetyEducationAttachment' },
-
-    { label: '安全管理协议(双方签字两份,上飞院部门领导签甲方代表、乙方盖章并签字)', prop: 'safetyAgreementAttachment' },
-
-    { label: '施工作业环境承诺(上飞院的项目具体经办人签字)', prop: 'environmentCommitmentAttachment' },
-
-    { label: '安全告知单(部门具体经办人签字)', prop: 'safetyNoticeAttachment' },
-    
-    { label: '消防管理承诺书(部门具体经办人签字)', prop: 'fireManagementAttachment' },
-
-    { label: '施工方案(含风险识别、安全措施、应急预案等)', prop: 'constructionPlanAttachment' },
-    
-    { label: '劳保用品清单', prop: 'ppeListAttachment' },
-    { label: '施工机械清单', prop: 'equipmentListAttachment' },
-    { label: '若有特种施工人员提供特种作业证(复印件,施工单位盖章)', prop: 'specialWorkerCertAttachment' },
-    { label: '若有特种设备提供特种设备的合格证(复印件,施工单位盖章', prop: 'specialEquipmentCertAttachment' },
+    { label: '施工人员身份信息(复印件,施工单位盖章)', prop: 'personnelIdAttachment', required: true },
+
+    { label: '施工方提供施工安全交底(施工方敲章)', prop: 'safetyCommitmentAttachment', required: true },
+
+    { label: '施工方提供施工人员安全教育(施工人员签字并敲章)', prop: 'safetyEducationAttachment', required: true },
+
+    {
+      label: '安全管理协议(双方签字两份,上飞院部门领导签甲方代表、乙方盖章并签字)',
+      prop: 'safetyAgreementAttachment',
+      required: true,
+    },
+
+    {
+      label: '施工作业环境承诺(上飞院的项目具体经办人签字)',
+      prop: 'environmentCommitmentAttachment',
+      required: true,
+    },
+
+    { label: '安全告知单(部门具体经办人签字)', prop: 'safetyNoticeAttachment', required: true },
+
+    { label: '消防管理承诺书(部门具体经办人签字)', prop: 'fireManagementAttachment', required: true },
+
+    { label: '施工方案(含风险识别、安全措施、应急预案等)', prop: 'constructionPlanAttachment', required: true },
+
+    { label: '劳保用品清单', prop: 'ppeListAttachment', required: true },
+    { label: '施工机械清单', prop: 'equipmentListAttachment', required: true },
+    {
+      label: '若有特种施工人员提供特种作业证(复印件,施工单位盖章)',
+      prop: 'specialWorkerCertAttachment',
+      required: false,
+    },
+    {
+      label: '若有特种设备提供特种设备的合格证(复印件,施工单位盖章',
+      prop: 'specialEquipmentCertAttachment',
+      required: false,
+    },
   ];
 
   const attachmentLists = reactive<any>(Object.fromEntries(attachmentConfigs.map((a) => [a.prop, []])));
@@ -242,66 +291,67 @@
     constructionLocation: [{ required: true, message: '请输入施工地点', trigger: 'blur' }],
     workerCount: [{ required: true, message: '请输入人数', trigger: 'blur' }],
     projectManagerName: [{ required: true, message: '请输入项目负责人', trigger: 'blur' }],
+    otherDescription: [{ required: true, message: '请输入其他说明', trigger: 'blur' }],
     projectManagerPhone1: [
-        { required: true, message: '请输入电话', trigger: 'blur' },
-        {
-          validator: (_rule: any, value: any, callback: any) => {
-            if (!value) {
-                callback(new Error('请输入电话'));
-            } else if (!/^1[3-9]\d{9}$/.test(value)) {
-                callback(new Error('请输入有效的11位手机号'));
-            } else {
-                callback();
-            }
-          },
-          trigger: 'blur'
-        }
+      { required: true, message: '请输入电话', trigger: 'blur' },
+      {
+        validator: (_rule: any, value: any, callback: any) => {
+          if (!value) {
+            callback(new Error('请输入电话'));
+          } else if (!/^1[3-9]\d{9}$/.test(value)) {
+            callback(new Error('请输入有效的11位手机号'));
+          } else {
+            callback();
+          }
+        },
+        trigger: 'blur',
+      },
     ],
     projectManagerPhone2: [
-        { required: true, message: '请输入电话', trigger: 'blur' },
-        {
-          validator: (_rule: any, value: any, callback: any) => {
-            if (!value) {
-                callback(new Error('请输入电话'));
-            } else if (!/^1[3-9]\d{9}$/.test(value)) {
-                callback(new Error('请输入有效的11位手机号'));
-            } else {
-                callback();
-            }
-          },
-          trigger: 'blur'
-        }
+      { required: true, message: '请输入电话', trigger: 'blur' },
+      {
+        validator: (_rule: any, value: any, callback: any) => {
+          if (!value) {
+            callback(new Error('请输入电话'));
+          } else if (!/^1[3-9]\d{9}$/.test(value)) {
+            callback(new Error('请输入有效的11位手机号'));
+          } else {
+            callback();
+          }
+        },
+        trigger: 'blur',
+      },
     ],
     siteSafetyManagerName: [{ required: true, message: '请输入安全负责人', trigger: 'blur' }],
     siteSafetyPhone1: [
-        { required: true, message: '请输入电话', trigger: 'blur' },
-        {
-          validator: (_rule: any, value: any, callback: any) => {
-            if (!value) {
-                callback(new Error('请输入电话'));
-            } else if (!/^1[3-9]\d{9}$/.test(value)) {
-                callback(new Error('请输入有效的11位手机号'));
-            } else {
-                callback();
-            }
-          },
-          trigger: 'blur'
-        }
+      { required: true, message: '请输入电话', trigger: 'blur' },
+      {
+        validator: (_rule: any, value: any, callback: any) => {
+          if (!value) {
+            callback(new Error('请输入电话'));
+          } else if (!/^1[3-9]\d{9}$/.test(value)) {
+            callback(new Error('请输入有效的11位手机号'));
+          } else {
+            callback();
+          }
+        },
+        trigger: 'blur',
+      },
     ],
     siteSafetyPhone2: [
-        { required: true, message: '请输入电话', trigger: 'blur' },
-        {
-          validator: (_rule: any, value: any, callback: any) => {
-            if (!value) {
-                callback(new Error('请输入电话'));
-            } else if (!/^1[3-9]\d{9}$/.test(value)) {
-                callback(new Error('请输入有效的11位手机号'));
-            } else {
-                callback();
-            }
-          },
-          trigger: 'blur'
-        }
+      { required: true, message: '请输入电话', trigger: 'blur' },
+      {
+        validator: (_rule: any, value: any, callback: any) => {
+          if (!value) {
+            callback(new Error('请输入电话'));
+          } else if (!/^1[3-9]\d{9}$/.test(value)) {
+            callback(new Error('请输入有效的11位手机号'));
+          } else {
+            callback();
+          }
+        },
+        trigger: 'blur',
+      },
     ],
     projectStartTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
     projectEndTime: [
@@ -321,10 +371,72 @@
     constructionContent: [{ required: true, message: '请输入简述', trigger: 'blur' }],
     templateId: [{ required: true, message: '请选择审批流程', trigger: 'change' }],
     ...Object.fromEntries(
-      attachmentConfigs.map((a) => [a.prop, [{ required: true, message: `请上传${a.label}`, trigger: 'change' }]]),
+      attachmentConfigs.map((a) => [
+        a.prop,
+        [{ required: a.required, message: `请上传${a.label}`, trigger: 'change' }],
+      ]),
     ),
   });
 
+  // 下载模版
+  const templateDownload = (item) => {
+    const baseUrl = './skyeye-file-upload/sfysecurity/TEMPLATE/';
+
+    switch (item.prop) {
+      case 'personnelIdAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyCommitmentAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyEducationAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyAgreementAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'environmentCommitmentAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyNoticeAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'fireManagementAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'constructionPlanAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'ppeListAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'equipmentListAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'specialWorkerCertAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'specialEquipmentCertAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      // 无匹配时提示
+      default:
+        ElMessage.warning('该附件暂无模板可下载');
+    }
+  };
+
   /**
    * 统一保存逻辑
    * @param status 0-保存草稿(通常不强校验附件),1-提交申请(强制校验)
@@ -371,9 +483,9 @@
       }
     }
   }
-  .label{
-    :deep(.el-form-item__label){
-        line-height: 26px;
+  .label {
+    :deep(.el-form-item__label) {
+      line-height: 26px;
     }
   }
 </style>

+ 3 - 4
src/views/production-safety/risk-identification-and-control/construction-safety-manage/audit.vue

@@ -5,9 +5,9 @@
     </header>
 
     <main class="safety-platform-container__main">
-        <div style="margin-bottom:20px">
-            <el-button type="primary" @click="$router.push({ name: 'constructionSafetyManageAdd' })">添加 </el-button>
-        </div>
+      <div style="margin-bottom: 20px">
+        <el-button type="primary" @click="$router.push({ name: 'constructionSafetyManageAdd' })">添加 </el-button>
+      </div>
       <div class="search-form">
         <el-form :inline="true">
           <el-form-item label="项目名称">
@@ -44,7 +44,6 @@
         </el-form>
 
         <div>
-          
           <el-button type="primary" @click="queryTableList">查询</el-button>
           <el-button @click="handleRestParams">重置</el-button>
         </div>

+ 145 - 35
src/views/production-safety/risk-identification-and-control/construction-safety-manage/edit.vue

@@ -10,7 +10,6 @@
 
     <main class="safety-platform-container__main">
       <el-form ref="formRef" :inline="true" label-width="auto" :model="formValue" :rules="rules">
-
         <!-- <el-form-item label="项目名称" prop="projectName">
           <el-input v-model="formValue.projectName" size="large" placeholder="请输入项目名称" style="width: 330px" />
         </el-form-item>
@@ -142,15 +141,30 @@
         </el-form-item>
 
         <el-form-item label="施工项目负责人" prop="projectManagerName">
-          <el-input v-model="formValue.projectManagerName" size="large" placeholder="输入施工项目负责人" style="width: 330px" />
+          <el-input
+            v-model="formValue.projectManagerName"
+            size="large"
+            placeholder="输入施工项目负责人"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工项目负责人电话1" prop="projectManagerPhone1">
-          <el-input v-model="formValue.projectManagerPhone1" size="large" placeholder="输入施工项目负责人电话1" style="width: 330px" />
+          <el-input
+            v-model="formValue.projectManagerPhone1"
+            size="large"
+            placeholder="输入施工项目负责人电话1"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工项目负责人电话2" prop="projectManagerPhone2">
-          <el-input v-model="formValue.projectManagerPhone2" size="large" placeholder="输入施工项目负责人电话2" style="width: 330px" />
+          <el-input
+            v-model="formValue.projectManagerPhone2"
+            size="large"
+            placeholder="输入施工项目负责人电话2"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工现场安全负责人" prop="siteSafetyManagerName">
@@ -163,11 +177,21 @@
         </el-form-item>
 
         <el-form-item label="施工现场安全负责人电话1" prop="siteSafetyPhone1">
-          <el-input v-model="formValue.siteSafetyPhone1" size="large" placeholder="输入施工现场安全负责人电话1" style="width: 330px" />
+          <el-input
+            v-model="formValue.siteSafetyPhone1"
+            size="large"
+            placeholder="输入施工现场安全负责人电话1"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工现场安全负责人电话2" prop="siteSafetyPhone2">
-          <el-input v-model="formValue.siteSafetyPhone2" size="large" placeholder="输入施工现场安全负责人电话2" style="width: 330px" />
+          <el-input
+            v-model="formValue.siteSafetyPhone2"
+            size="large"
+            placeholder="输入施工现场安全负责人电话2"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="开始时间" prop="projectStartTime">
@@ -202,6 +226,7 @@
               v-for="t in ['水电', '泥瓦', '木工', '焊接', '气割', '登高', '密闭', '特种驾驶', '其他']"
               :key="t"
               :label="t"
+              :value="t"
             />
           </el-checkbox-group>
         </el-form-item>
@@ -237,15 +262,22 @@
           v-for="item in attachmentConfigs"
           :key="item.prop"
           :label="item.label"
-          label-width="240px"
+          label-width="250px"
           :prop="item.prop"
-          style="width: 43.6%"
+          style="width: 43%"
         >
-          <UploadFiles
-            label="上传文件"
-            @upload-success="(fileList) => handleUploadSuccess(item.prop, fileList)"
-            :fileList="attachmentLists[item.prop]"
-          />
+          <el-row style="width: 100%">
+            <el-col :span="12">
+              <UploadFiles
+                label="上传文件"
+                @upload-success="(fileList) => handleUploadSuccess(item.prop, fileList)"
+                :fileList="attachmentLists[item.prop]"
+              />
+            </el-col>
+            <el-col :span="12">
+              <el-button type="primary" link @click="templateDownload(item)"> 模版下载 </el-button>
+            </el-col>
+          </el-row>
         </el-form-item>
 
         <div style="width: 100%; height: 1px; background: #eee; margin: 20px 0"></div>
@@ -281,6 +313,7 @@
     constructionSafetyUpdateConstruction,
   } from '@/api/production-safety/responsibility-implementation';
   import { unformatAttachment, formatAttachmentList } from '@/components/UploadFiles/utils';
+  import { downloadFile } from '@/views/disaster/utils';
 
   const router = useRouter();
   const route = useRoute();
@@ -295,26 +328,42 @@
     return formValue.status === 1 && formValue.rejectReason !== '';
   });
   const attachmentConfigs = [
-    { label: '施工人员身份信息(复印件,施工单位盖章)', prop: 'personnelIdAttachment' },
-    
-    { label: '施工方提供施工安全交底(施工方敲章)', prop: 'safetyCommitmentAttachment' },
-
-    { label: '施工方提供施工人员安全教育(施工人员签字并敲章)', prop: 'safetyEducationAttachment' },
-
-    { label: '安全管理协议(双方签字两份,上飞院部门领导签甲方代表、乙方盖章并签字)', prop: 'safetyAgreementAttachment' },
-
-    { label: '施工作业环境承诺(上飞院的项目具体经办人签字)', prop: 'environmentCommitmentAttachment' },
-
-    { label: '安全告知单(部门具体经办人签字)', prop: 'safetyNoticeAttachment' },
-    
-    { label: '消防管理承诺书(部门具体经办人签字)', prop: 'fireManagementAttachment' },
-
-    { label: '施工方案(含风险识别、安全措施、应急预案等)', prop: 'constructionPlanAttachment' },
-    
-    { label: '劳保用品清单', prop: 'ppeListAttachment' },
-    { label: '施工机械清单', prop: 'equipmentListAttachment' },
-    { label: '若有特种施工人员提供特种作业证(复印件,施工单位盖章)', prop: 'specialWorkerCertAttachment' },
-    { label: '若有特种设备提供特种设备的合格证(复印件,施工单位盖章', prop: 'specialEquipmentCertAttachment' },
+    { label: '施工人员身份信息(复印件,施工单位盖章)', prop: 'personnelIdAttachment', required: true },
+
+    { label: '施工方提供施工安全交底(施工方敲章)', prop: 'safetyCommitmentAttachment', required: true },
+
+    { label: '施工方提供施工人员安全教育(施工人员签字并敲章)', prop: 'safetyEducationAttachment', required: true },
+
+    {
+      label: '安全管理协议(双方签字两份,上飞院部门领导签甲方代表、乙方盖章并签字)',
+      prop: 'safetyAgreementAttachment',
+      required: true,
+    },
+
+    {
+      label: '施工作业环境承诺(上飞院的项目具体经办人签字)',
+      prop: 'environmentCommitmentAttachment',
+      required: true,
+    },
+
+    { label: '安全告知单(部门具体经办人签字)', prop: 'safetyNoticeAttachment', required: true },
+
+    { label: '消防管理承诺书(部门具体经办人签字)', prop: 'fireManagementAttachment', required: true },
+
+    { label: '施工方案(含风险识别、安全措施、应急预案等)', prop: 'constructionPlanAttachment', required: true },
+
+    { label: '劳保用品清单', prop: 'ppeListAttachment', required: true },
+    { label: '施工机械清单', prop: 'equipmentListAttachment', required: true },
+    {
+      label: '若有特种施工人员提供特种作业证(复印件,施工单位盖章)',
+      prop: 'specialWorkerCertAttachment',
+      required: false,
+    },
+    {
+      label: '若有特种设备提供特种设备的合格证(复印件,施工单位盖章',
+      prop: 'specialEquipmentCertAttachment',
+      required: false,
+    },
   ];
 
   const attachmentLists = reactive<any>(Object.fromEntries(attachmentConfigs.map((a) => [a.prop, []])));
@@ -348,6 +397,7 @@
     constructionUnit: [{ required: true, message: '请输入施工单位', trigger: 'blur' }],
     constructionLocation: [{ required: true, message: '请输入施工地点', trigger: 'blur' }],
     workerCount: [{ required: true, message: '请输入施工人数', trigger: 'blur' }],
+    otherDescription: [{ required: true, message: '请输入其他说明', trigger: 'blur' }],
     projectManagerName: [{ required: true, message: '请输入项目负责人', trigger: 'blur' }],
     projectManagerPhone1: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
     siteSafetyManagerName: [{ required: true, message: '请输入现场安全员', trigger: 'blur' }],
@@ -372,7 +422,10 @@
     templateId: [{ required: true, message: '请选择审批流程', trigger: 'change' }],
     // 自动为所有附件添加必填验证
     ...Object.fromEntries(
-      attachmentConfigs.map((a) => [a.prop, [{ required: true, message: `请上传${a.label}`, trigger: 'change' }]]),
+      attachmentConfigs.map((a) => [
+        a.prop,
+        [{ required: a.required, message: `请上传${a.label}`, trigger: 'change' }],
+      ]),
     ),
   });
 
@@ -414,7 +467,64 @@
       console.error('获取详情数据失败:', error);
     }
   };
-
+  // 下载模版
+  const templateDownload = (item) => {
+    const baseUrl = './skyeye-file-upload/sfysecurity/TEMPLATE/';
+
+    switch (item.prop) {
+      case 'personnelIdAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyCommitmentAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyEducationAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyAgreementAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'environmentCommitmentAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyNoticeAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'fireManagementAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'constructionPlanAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'ppeListAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'equipmentListAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'specialWorkerCertAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'specialEquipmentCertAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      // 无匹配时提示
+      default:
+        ElMessage.warning('该附件暂无模板可下载');
+    }
+  };
   const handleSave = (status: number) => {
     // status: 0 保存, 1 提交
     formRef.value?.validate(async (valid: boolean) => {

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

@@ -4,12 +4,12 @@
       <div class="breadcrumb-title"> 施工作业安全管理 </div>
     </header>
     <main class="safety-platform-container__main">
-        <div style="margin-bottom:20px">
-            <el-button type="primary" @click="$router.push({ name: 'constructionSafetyManageAdd' })">添加 </el-button>
-            <!-- <el-button plain class="search-table-container--button" @click="handleImport">
+      <div style="margin-bottom: 20px">
+        <el-button type="primary" @click="$router.push({ name: 'constructionSafetyManageAdd' })">添加 </el-button>
+        <!-- <el-button plain class="search-table-container--button" @click="handleImport">
               导入
             </el-button> -->
-        </div>
+      </div>
       <div class="search-form">
         <el-form :inline="true">
           <el-form-item label="项目名称">
@@ -48,9 +48,7 @@
         <div>
           <el-button type="primary" @click="queryTableList">查询</el-button>
           <el-button @click="handleRestParams">重置</el-button>
-          <el-button plain  @click="handleDownload">
-            导出
-          </el-button>
+          <el-button plain @click="handleDownload"> 导出 </el-button>
         </div>
       </div>
 

+ 144 - 34
src/views/production-safety/risk-identification-and-control/construction-safety-manage/view.vue

@@ -11,7 +11,13 @@
     <main class="safety-platform-container__main">
       <el-form ref="formRef" :inline="true" label-width="auto" :model="formValue">
         <el-form-item label="项目名称" prop="projectName">
-          <el-input v-model="formValue.projectName" size="large" :disabled="true" placeholder="请输入项目名称" style="width: 330px" />
+          <el-input
+            v-model="formValue.projectName"
+            size="large"
+            :disabled="true"
+            placeholder="请输入项目名称"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工地点(区域)" prop="constructionLocation">
@@ -35,15 +41,33 @@
         </el-form-item>
 
         <el-form-item label="施工项目负责人" prop="projectManagerName">
-          <el-input v-model="formValue.projectManagerName" :disabled="true" size="large" placeholder="输入施工项目负责人" style="width: 330px" />
+          <el-input
+            v-model="formValue.projectManagerName"
+            :disabled="true"
+            size="large"
+            placeholder="输入施工项目负责人"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工项目负责人电话1" prop="projectManagerPhone1">
-          <el-input v-model="formValue.projectManagerPhone1" :disabled="true" size="large" placeholder="输入施工项目负责人电话1" style="width: 330px" />
+          <el-input
+            v-model="formValue.projectManagerPhone1"
+            :disabled="true"
+            size="large"
+            placeholder="输入施工项目负责人电话1"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工项目负责人电话2" prop="projectManagerPhone2">
-          <el-input v-model="formValue.projectManagerPhone2" :disabled="true" size="large" placeholder="输入施工项目负责人电话2" style="width: 330px" />
+          <el-input
+            v-model="formValue.projectManagerPhone2"
+            :disabled="true"
+            size="large"
+            placeholder="输入施工项目负责人电话2"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工现场安全负责人" prop="siteSafetyManagerName">
@@ -57,11 +81,23 @@
         </el-form-item>
 
         <el-form-item label="施工现场安全负责人电话1" prop="siteSafetyPhone1">
-          <el-input v-model="formValue.siteSafetyPhone1" :disabled="true" size="large" placeholder="输入施工现场安全负责人电话1" style="width: 330px" />
+          <el-input
+            v-model="formValue.siteSafetyPhone1"
+            :disabled="true"
+            size="large"
+            placeholder="输入施工现场安全负责人电话1"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="施工现场安全负责人电话2" prop="siteSafetyPhone2">
-          <el-input v-model="formValue.siteSafetyPhone2" size="large" :disabled="true" placeholder="输入施工现场安全负责人电话2" style="width: 330px" />
+          <el-input
+            v-model="formValue.siteSafetyPhone2"
+            size="large"
+            :disabled="true"
+            placeholder="输入施工现场安全负责人电话2"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="开始时间" prop="projectStartTime">
@@ -89,7 +125,13 @@
         </el-form-item>
 
         <el-form-item label="施工人数" prop="workerCount">
-          <el-input-number v-model="formValue.workerCount" :disabled="true" size="large" :min="1" style="width: 330px" />
+          <el-input-number
+            v-model="formValue.workerCount"
+            :disabled="true"
+            size="large"
+            :min="1"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="所涉工种" prop="involvedTrades" style="width: 87.2%">
@@ -104,7 +146,13 @@
         </el-form-item>
 
         <el-form-item v-if="tradeArray.includes('其他')" label="其他工种说明" prop="otherTrade">
-          <el-input v-model="formValue.otherTrade" size="large" :disabled="true" placeholder="请补充工种类型" style="width: 330px" />
+          <el-input
+            v-model="formValue.otherTrade"
+            size="large"
+            :disabled="true"
+            placeholder="请补充工种类型"
+            style="width: 330px"
+          />
         </el-form-item>
 
         <el-form-item label="工程施工内容(方案)简要描述" prop="constructionContent" style="width: 81%">
@@ -139,27 +187,27 @@
           label-width="240px"
           :prop="item.prop"
           class="label"
-          style="width: 43.6%;"
+          style="width: 43.6%"
         >
-            <!-- 详情页面只展示,不需要上传 -->
+          <!-- 详情页面只展示,不需要上传 -->
           <!-- <UploadFiles
             label="上传文件"
             :disabled="true"
             :fileList="attachmentLists[item.prop]"
           /> -->
-           <div class="file-list" v-if="attachmentLists[item.prop]?.length">
-                <div class="file-item" v-for="li in attachmentLists[item.prop]" :key="li.fileId">
-                <span class="file-item--name">{{ li.fileName }}</span>
-                <div class="file-item--footer">
-                    <el-button link type="primary" @click="previewOnline(li.fileUrl, li.fileType)"
-                    >预览</el-button
-                    >
-                    <el-button link type="primary" @click.stop="downloadFile(li.fileUrl, li.fileName)"
-                    >下载</el-button
-                    >
-                </div>
-                </div>
+          <div class="file-list" v-if="attachmentLists[item.prop]?.length">
+            <div class="file-item" v-for="li in attachmentLists[item.prop]" :key="li.fileId">
+              <span class="file-item--name">{{ li.fileName }}</span>
+              <div class="file-item--footer">
+                <el-button link type="primary" @click="previewOnline(li.fileUrl, li.fileType)">预览</el-button>
+                <el-button link type="primary" @click.stop="downloadFile(li.fileUrl, li.fileName)">下载</el-button>
+                <el-button type="primary" link @click="templateDownload(item)"> 模版下载 </el-button>
+              </div>
             </div>
+          </div>
+          <div v-else>
+            <el-button type="primary" link @click="templateDownload(item)"> 模版下载 </el-button>
+          </div>
         </el-form-item>
 
         <div style="width: 100%; height: 1px; background: #eee; margin: 20px 0"></div>
@@ -180,6 +228,7 @@
 <script lang="ts" setup>
   import { ref, reactive, onMounted, computed } from 'vue';
   import { useRouter, useRoute } from 'vue-router';
+  import { ElMessage } from 'element-plus';
   import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
   import { getAllApproval } from '@/api/approval/approval';
   import { constructionSafetyQueryDetailConstruction } from '@/api/production-safety/responsibility-implementation';
@@ -193,21 +242,24 @@
 
   const attachmentConfigs = [
     { label: '施工人员身份信息(复印件,施工单位盖章)', prop: 'personnelIdAttachment' },
-    
+
     { label: '施工方提供施工安全交底(施工方敲章)', prop: 'safetyCommitmentAttachment' },
 
     { label: '施工方提供施工人员安全教育(施工人员签字并敲章)', prop: 'safetyEducationAttachment' },
 
-    { label: '安全管理协议(双方签字两份,上飞院部门领导签甲方代表、乙方盖章并签字)', prop: 'safetyAgreementAttachment' },
+    {
+      label: '安全管理协议(双方签字两份,上飞院部门领导签甲方代表、乙方盖章并签字)',
+      prop: 'safetyAgreementAttachment',
+    },
 
     { label: '施工作业环境承诺(上飞院的项目具体经办人签字)', prop: 'environmentCommitmentAttachment' },
 
     { label: '安全告知单(部门具体经办人签字)', prop: 'safetyNoticeAttachment' },
-    
+
     { label: '消防管理承诺书(部门具体经办人签字)', prop: 'fireManagementAttachment' },
 
     { label: '施工方案(含风险识别、安全措施、应急预案等)', prop: 'constructionPlanAttachment' },
-    
+
     { label: '劳保用品清单', prop: 'ppeListAttachment' },
     { label: '施工机械清单', prop: 'equipmentListAttachment' },
     { label: '若有特种施工人员提供特种作业证(复印件,施工单位盖章)', prop: 'specialWorkerCertAttachment' },
@@ -279,6 +331,64 @@
       console.error('获取详情数据失败:', error);
     }
   };
+  // 下载模版
+  const templateDownload = (item) => {
+    const baseUrl = './skyeye-file-upload/sfysecurity/TEMPLATE/';
+
+    switch (item.prop) {
+      case 'personnelIdAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyCommitmentAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyEducationAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyAgreementAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'environmentCommitmentAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'safetyNoticeAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'fireManagementAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'constructionPlanAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'ppeListAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'equipmentListAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'specialWorkerCertAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      case 'specialEquipmentCertAttachment':
+        downloadFile('./skyeye-file-upload/sfysecurity/TEMPLATE/施工证申请表.doc', item.label);
+        break;
+
+      // 无匹配时提示
+      default:
+        ElMessage.warning('该附件暂无模板可下载');
+    }
+  };
 
   onMounted(() => {
     initData();
@@ -307,16 +417,16 @@
     //   color: #606266;
     // }
   }
-  .label{
-    :deep(.el-form-item__label){
-        line-height: 26px;
+  .label {
+    :deep(.el-form-item__label) {
+      line-height: 26px;
     }
   }
-  .file-list{
-    .file-item--name{
-        padding:6px 14px;
-        border:1px solid #efefef;
-        border-radius: 8px;
+  .file-list {
+    .file-item--name {
+      padding: 6px 14px;
+      border: 1px solid #efefef;
+      border-radius: 8px;
     }
   }
 </style>

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

@@ -465,24 +465,28 @@
       <el-button type="primary" size="large" :loading="loading" @click="handleSave">提交</el-button>
     </footer>
   </div>
-  <el-dialog v-model="hotWorkDialogVisible" title="">
+  <el-dialog v-model="hotWorkDialogVisible" width="60%" title="新增危险作业审批单">
     <div class="hotWorkDialog"
       v-html="
-        `<p style='margin-bottom:10px'>备注1:</p>
-    <p style='margin-bottom:10px'>1.【重要】申请部门联络人手机号需与企业微信中登记的手机号一致,系統根据申请部门联络人手机号推送审批单及相关信息。</p>
-    <p style='margin-bottom:10px'>2.一级动火,由动火部门申请,须由院消防安全麦任人南批;二级动火,由动火都门申清,须由消防安全分管院领导南批;三级动火,由动火部门申请,须由消防安全管理部门领导南批。</p>
-    <p style='margin-bottom:10px'>3.申请动火时需提供动火地点具体位置的照片,否则不子以办理动火证。</p>
-    <p style='margin-bottom:10px'>4.一级动火区域为:一级危险点、油库、燃油试验室、燃气区域等高危场所;二级动火区域为:二级危险点、35KV变电站、高处作业场所、试验场所、仓库。堆场、待建区等;三级动火区域为:三级危险点、10KV交电站、空压机房、办公室、室外区域(除待建区外)
-    等。 </p>
-    <p style='margin-bottom:10px'>备注2:</p>
-    <p style='margin-bottom:10px'>1.填写动火开始时间及截止时间,一级动火和二级动火有效期不应超过1天,三级动火有效期不应超过6天:</p>
+        `<p style='margin-bottom:5px'>备注1:</p>
+    <p style='margin-bottom:10px;color:red'>1.【重要】申请部门联络人手机号需与企业微信中登记的手机号一致,系统根据申请部门联络人手机号推送审批单及相关信息。</p>
+    <p style='margin-bottom:10px'>2.一级动火,由动火部门申请,须由院消防安全责任人审批;二级动火,由动火部门申清,须由消防安全分管院领导审批;三级动火,由动火部门申请,须由消防安全管理部门领导审批。</p>
+    <p style='margin-bottom:10px'>3.申请动火时需提供动火地点具体位置的照片,否则不予以办理动火证。</p>
+    <p style='margin-bottom:10px'>4.一级动火区域为:一级危险点、油库、燃油试验室、燃气区域等高危场所;二级动火区域为:二级危险点、35KV变电站、高处作业场所、试验场所、仓库、堆场、待建区等;三级动火区域为:三级危险点、10KV交电站、空压机房、办公室、室外区域(除待建区外) 等。 </p>
+    <p style='margin-bottom:5px; margin-top:20px'>备注2:</p>
+    <p style='margin-bottom:10px'>1.填写动火开始时间及截止时间,一级动火和二级动火有效期不应超过1天,三级动火有效期不应超过6天;</p>
     <p style='margin-bottom:10px'>2.动火作业需提前三天开始申请;</p>
-    <p style='margin-bottom:10px'>3.申请部门完成申请后,将动火作业表打印纸质版本及动火申请材料放置于动火现场,消防值班人员将进行现场检查,现场若无紙质动火作业表及动火申请材料不予动火。</p>
-    <p style='margin-bottom:10px'>备注3:</p>
-    <p style='margin-bottom:10px'>1,电煌 气割,严格道守“十不烧”规程操作: 2.操作前检查动火人员的工具、电焊机、电源开关及线路是否良好;</p>
-    <p style='margin-bottom:10px'>3.注意安全用电,检查电线是否有乱拖乱拉现象,电源线是否架空扎车; 4.检查人员在动火期间不得离开现场,应当留下监护, 5.
-    动火结束后,督促动火人员清理现场,不得留下残渣; 6.现场配备灭火器: 7.现场专人看护, 8.清理周边易燃物;</p>
-    <p style='margin-bottom:10px'>9.现场动火人员持证上岗;</p>`
+    <p style='margin-bottom:10px'>3.申请部门完成申请后,将动火作业表打印纸质版本及动火申请材料放置于动火现场,消防值班人员将进行现场检查,现场若无纸质动火作业表及动火申请材料不予动火。</p>
+    <p style='margin-bottom:5px;margin-top:20px'>备注3:</p>
+    <p style='margin-bottom:10px'>1.电焊、气割,严格遵守“十不烧”规程操作;</p>
+    <p style='margin-bottom:10px'>2.操作前检查动火人员的工具、电焊机、电源开关及线路是否良好;</p>
+    <p style='margin-bottom:10px'>3.注意安全用电,检查电线是否有乱拖乱拉现象,电源线是否架空扎车;</p>
+    <p style='margin-bottom:10px'>4.检查人员在动火期间不得离开现场,应当留下监护;</p>
+    <p style='margin-bottom:10px'>5. 动火结束后,督促动火人员清理现场,不得留下残渣;</p>
+    <p style='margin-bottom:10px'>6.现场配备灭火器;</p>
+    <p style='margin-bottom:10px'>7.现场专人看护;</p>
+    <p style='margin-bottom:10px'>8.清理周边易燃物;</p>
+    <p style='margin-bottom:10px'>9.现场动火人员持证上岗。</p>`
       "
     ></div>
     <template #footer>
@@ -818,4 +822,8 @@
         text-align: left;
     }
   }
+  .hotWorkDialog {
+    color:black;
+    padding-left: 2rem;
+  }
 </style>

+ 23 - 15
src/views/production-safety/risk-identification-and-control/hazard-approval-manage/edit.vue

@@ -873,24 +873,28 @@
       <el-button type="primary" size="large" :loading="loading" @click="handleSave">提交</el-button>
     </footer>
 
-    <el-dialog v-model="hotWorkDialogVisible" title="">
+    <el-dialog v-model="hotWorkDialogVisible" title="编辑危险作业审批单">
     <div class="hotWorkDialog"
       v-html="
-        `<p style='margin-bottom:10px'>备注1:</p>
-    <p style='margin-bottom:10px'>1.【重要】申请部门联络人手机号需与企业微信中登记的手机号一致,系統根据申请部门联络人手机号推送审批单及相关信息。</p>
-    <p style='margin-bottom:10px'>2.一级动火,由动火部门申请,须由院消防安全麦任人南批;二级动火,由动火都门申清,须由消防安全分管院领导南批;三级动火,由动火部门申请,须由消防安全管理部门领导南批。</p>
-    <p style='margin-bottom:10px'>3.申请动火时需提供动火地点具体位置的照片,否则不子以办理动火证。</p>
-    <p style='margin-bottom:10px'>4.一级动火区域为:一级危险点、油库、燃油试验室、燃气区域等高危场所;二级动火区域为:二级危险点、35KV变电站、高处作业场所、试验场所、仓库。堆场、待建区等;三级动火区域为:三级危险点、10KV交电站、空压机房、办公室、室外区域(除待建区外)
-    等。 </p>
-    <p style='margin-bottom:10px'>备注2:</p>
-    <p style='margin-bottom:10px'>1.填写动火开始时间及截止时间,一级动火和二级动火有效期不应超过1天,三级动火有效期不应超过6天:</p>
+        `<p style='margin-bottom:5px'>备注1:</p>
+    <p style='margin-bottom:10px;color:red'>1.【重要】申请部门联络人手机号需与企业微信中登记的手机号一致,系统根据申请部门联络人手机号推送审批单及相关信息。</p>
+    <p style='margin-bottom:10px'>2.一级动火,由动火部门申请,须由院消防安全责任人审批;二级动火,由动火部门申清,须由消防安全分管院领导审批;三级动火,由动火部门申请,须由消防安全管理部门领导审批。</p>
+    <p style='margin-bottom:10px'>3.申请动火时需提供动火地点具体位置的照片,否则不予以办理动火证。</p>
+    <p style='margin-bottom:10px'>4.一级动火区域为:一级危险点、油库、燃油试验室、燃气区域等高危场所;二级动火区域为:二级危险点、35KV变电站、高处作业场所、试验场所、仓库、堆场、待建区等;三级动火区域为:三级危险点、10KV交电站、空压机房、办公室、室外区域(除待建区外) 等。 </p>
+    <p style='margin-bottom:5px; margin-top:20px'>备注2:</p>
+    <p style='margin-bottom:10px'>1.填写动火开始时间及截止时间,一级动火和二级动火有效期不应超过1天,三级动火有效期不应超过6天;</p>
     <p style='margin-bottom:10px'>2.动火作业需提前三天开始申请;</p>
-    <p style='margin-bottom:10px'>3.申请部门完成申请后,将动火作业表打印纸质版本及动火申请材料放置于动火现场,消防值班人员将进行现场检查,现场若无紙质动火作业表及动火申请材料不予动火。</p>
-    <p style='margin-bottom:10px'>备注3:</p>
-    <p style='margin-bottom:10px'>1,电煌 气割,严格道守“十不烧”规程操作: 2.操作前检查动火人员的工具、电焊机、电源开关及线路是否良好;</p>
-    <p style='margin-bottom:10px'>3.注意安全用电,检查电线是否有乱拖乱拉现象,电源线是否架空扎车; 4.检查人员在动火期间不得离开现场,应当留下监护, 5.
-    动火结束后,督促动火人员清理现场,不得留下残渣; 6.现场配备灭火器: 7.现场专人看护, 8.清理周边易燃物;</p>
-    <p style='margin-bottom:10px'>9.现场动火人员持证上岗;</p>`
+    <p style='margin-bottom:10px'>3.申请部门完成申请后,将动火作业表打印纸质版本及动火申请材料放置于动火现场,消防值班人员将进行现场检查,现场若无纸质动火作业表及动火申请材料不予动火。</p>
+    <p style='margin-bottom:5px;margin-top:20px'>备注3:</p>
+    <p style='margin-bottom:10px'>1.电焊、气割,严格遵守“十不烧”规程操作;</p>
+    <p style='margin-bottom:10px'>2.操作前检查动火人员的工具、电焊机、电源开关及线路是否良好;</p>
+    <p style='margin-bottom:10px'>3.注意安全用电,检查电线是否有乱拖乱拉现象,电源线是否架空扎车;</p>
+    <p style='margin-bottom:10px'>4.检查人员在动火期间不得离开现场,应当留下监护;</p>
+    <p style='margin-bottom:10px'>5. 动火结束后,督促动火人员清理现场,不得留下残渣;</p>
+    <p style='margin-bottom:10px'>6.现场配备灭火器;</p>
+    <p style='margin-bottom:10px'>7.现场专人看护;</p>
+    <p style='margin-bottom:10px'>8.清理周边易燃物;</p>
+    <p style='margin-bottom:10px'>9.现场动火人员持证上岗。</p>`
       "
     ></div>
     <template #footer>
@@ -1212,4 +1216,8 @@ const rules = reactive({
       }
     }
   }
+  .hotWorkDialog {
+    color:black;
+    padding-left: 2rem;
+  }
 </style>

+ 2 - 2
src/views/production-safety/safetyAssessment/evaluationSystem/components/EvaluationTarget.vue

@@ -163,7 +163,7 @@
             <template #evaluationDocument="scope">
               <div
                 class="file-container--div"
-                v-for="item in parseAttachments(scope.row.evaluationDocument)"
+                v-for="item in scope.row.evaluationDocument"
                 :key="item.fileUrl"
               >
                 <img
@@ -483,7 +483,7 @@
             issueDepartment: item.deptName, // 下发部门(部门考核结果)
             departmentLeader: item.deptUserName || '-', // 部门负责人
             contactPhone: item.deptUserLink || '-', // 联系方式
-            evaluationDocument: item.attachments, // 考核文档
+            evaluationDocument: JSON.parse(item.attachments || '[]'), // 考核文档
             plannedCompletionTime: item.planEndTime || '-', // 计划完成时间(使用计划结束时间)
             // 保留原始数据,供其他操作使用
             psemId: item.psemId,

+ 1 - 1
src/views/production-safety/safetyAssessment/evaluationSystem/configs/targetTables.ts

@@ -37,7 +37,7 @@ export const EVALUATION_TARGET_TABLE_COLUMNS: TableColumnProps[] = [
     prop: 'evaluationDocument',
     slot: 'evaluationDocument',
     align: 'left',
-    minWidth: '150px',
+    minWidth: '250px',
   },
   {
     label: '计划完成时间',

+ 18 - 7
src/views/production-safety/safetyAssessment/evaluationSystem/evaluationSystem.vue

@@ -128,12 +128,12 @@
                     }"
                     @confirm="handleDelete(scope.row.id)"
                   />
-                  <ActionButton text="下发" @click="handleIssue(scope.row.id)" />
+                  <ActionButton text="下发" @click="handleIssue(scope.row)" />
                 </template>
 
                 <!-- 待反馈:作废 / 考核对象 -->
                 <template v-else-if="Number(scope.row.status) === 2">
-                  <!-- <ActionButton text="下发" @click="handleIssue(scope.row.id)" /> -->
+                  <ActionButton text="下发" @click="handleIssue(scope.row)" />
                   <ActionButton
                     text="作废"
                     :popconfirm="{
@@ -255,6 +255,7 @@
               value-format="YYYY-MM-DD"
               format="YYYY-MM-DD"
               style="width: 100%"
+              :disabled="showTimeDisabled"
               :disabled-date="(date: Date) => {
                 if (issueForm.endDate) {
                   return date > new Date(issueForm.endDate);
@@ -265,6 +266,7 @@
           </el-form-item>
           <el-form-item label="计划结束日期:">
             <el-date-picker
+              :disabled="showTimeDisabled"
               v-model="issueForm.endDate"
               type="date"
               placeholder="请选择计划结束日期"
@@ -322,6 +324,8 @@
 
   const router = useRouter();
 
+  const showTimeDisabled = ref(false);
+
   // 表格
   const basicTableRef = ref<InstanceType<typeof BasicTable>>();
 
@@ -444,6 +448,8 @@
             item.planStartTime && item.planEndTime
               ? `${item.planStartTime} 至 ${item.planEndTime}`
               : item.planStartTime || item.planEndTime || '-', // 计划完成时间
+          planStartTime: item.planStartTime || '', // 计划开始时间
+          planEndTime: item.planEndTime || '', // 计划结束时间
         }));
         pagination.total = res.totalRow;
       }
@@ -511,17 +517,22 @@
   };
 
   // 下发考核表
-  const handleIssue = async (id: number) => {
-    currentIssueId.value = id;
+  const handleIssue = async (row: any) => {
+    currentIssueId.value = row.id;
     issueForm.departmentName = '';
-    issueForm.startDate = '';
-    issueForm.endDate = '';
+    issueForm.startDate = row.planStartTime;
+    issueForm.endDate = row.planEndTime;
     issueForm.userGroupId = undefined;
     issueForm.deptSelfApproveUserId = undefined;
     issueDeptIds.value = [];
     issueDialogVisible.value = true;
+    if(row.planStartTime && row.planEndTime && row.status === 2) {
+      showTimeDisabled.value = true;
+    } else {
+      showTimeDisabled.value = false;
+    }
     // 弹窗打开时才加载下发弹窗所需数据
-    await Promise.all([getDeptTreeData(), getUserGroupOptions(), getDeptSelfApproveUserList(id)]);
+    await Promise.all([getDeptTreeData(), getUserGroupOptions(), getDeptSelfApproveUserList(row.id)]);
   };
 
   const handleIssueDeptChange = () => {