Explorar el Código

feat: 安全责任清单,安全责任通知相关模块

sunqijun hace 3 meses
padre
commit
291f83b006

+ 305 - 14
src/api/production-safety/responsibility-implementation/index.ts

@@ -1,6 +1,11 @@
 import { http } from '@/utils/http/axios';
 import type { QueryPageRequest, QueryPageResponse } from '@/types/basic-query';
 
+/**
+ * 根据用户名分页查询用户列表(管理员)
+ * @param params - 分页查询参数,通常包含 username、pageNum、pageSize 等
+ * @returns Promise<QueryPageResponse>
+ */
 export function queryUserPageByUsername(params) {
   return http.request({
     url: `/admin/user/queryUserPageByUsername`,
@@ -8,6 +13,12 @@ export function queryUserPageByUsername(params) {
     params
   });
 }
+
+/**
+ * 安全责任书 - 管理端分页查询
+ * @param params - 分页查询条件
+ * @returns Promise<QueryPageResponse>
+ */
 export function safetyResponsibilityAdminqueryPage(params) {
   return http.request({
     url: '/safetyResponsibility/admin/queryPage',
@@ -16,7 +27,11 @@ export function safetyResponsibilityAdminqueryPage(params) {
   });
 }
 
-
+/**
+ * 删除指定 ID 的安全责任书(管理端)
+ * @param id - 安全责任书 ID
+ * @returns Promise<void>
+ */
 export function safetyResponsibilityAdminDelete(id) {
   return http.request({
     url: `/safetyResponsibility/admin/delete?id=${id}`,
@@ -24,6 +39,11 @@ export function safetyResponsibilityAdminDelete(id) {
   });
 }
 
+/**
+ * 新增安全责任书(管理端)
+ * @param params - 安全责任书表单数据
+ * @returns Promise<void>
+ */
 export function safetyResponsibilitySaveSafetyResponsibility(params) {
   return http.request({
     url: '/safetyResponsibility/admin/saveSafetyResponsibility',
@@ -31,6 +51,12 @@ export function safetyResponsibilitySaveSafetyResponsibility(params) {
     params
   });
 }
+
+/**
+ * 更新安全责任书(管理端)
+ * @param params - 更新后的安全责任书数据(需包含 ID)
+ * @returns Promise<void>
+ */
 export function safetyResponsibilityUpdateSafetyResponsibility(params) {
   return http.request({
     url: '/safetyResponsibility/admin/updateSafetyResponsibility',
@@ -39,6 +65,11 @@ export function safetyResponsibilityUpdateSafetyResponsibility(params) {
   });
 }
 
+/**
+ * 查询安全责任书详情(管理端)
+ * @param params - 查询参数,通常包含 id
+ * @returns Promise<any> 安全责任书详细信息
+ */
 export function safetyResponsibilityAdminQueryDetail(params) {
   return http.request({
     url: '/safetyResponsibility/admin/queryDetail',
@@ -47,6 +78,11 @@ export function safetyResponsibilityAdminQueryDetail(params) {
   });
 }
 
+/**
+ * 作废指定 ID 的安全责任书(管理端)
+ * @param id - 安全责任书 ID
+ * @returns Promise<void>
+ */
 export function safetyResponsibilityAdminScrap(id) {
   return http.request({
     url: `/safetyResponsibility/admin/scrap?id=${id}`,
@@ -54,6 +90,11 @@ export function safetyResponsibilityAdminScrap(id) {
   });
 }
 
+/**
+ * 分页查询已下发对象列表(管理端)
+ * @param params - 查询条件,如责任书 ID、分页参数等
+ * @returns Promise<QueryPageResponse>
+ */
 export function safetyResponsibilityAdminQueryIssuedObject(params) {
   return http.request({
     url: '/safetyResponsibility/admin/queryIssuedObject',
@@ -62,6 +103,11 @@ export function safetyResponsibilityAdminQueryIssuedObject(params) {
   });
 }
 
+/**
+ * 导出已下发对象列表(管理端)
+ * @param params - 导出条件参数
+ * @returns Promise<Blob> Excel 文件流
+ */
 export function safetyResponsibilityAdminExportIssuedObject(params) {
   return http.request({
     url: '/safetyResponsibility/admin/exportIssuedObject',
@@ -73,7 +119,11 @@ export function safetyResponsibilityAdminExportIssuedObject(params) {
   });
 }
 
-
+/**
+ * 删除指定 ID 的已下发对象(管理端)
+ * @param id - 已下发对象 ID
+ * @returns Promise<void>
+ */
 export function safetyResponsibilityAdminDeleteIssuedObject(id) {
   return http.request({
     url: `/safetyResponsibility/admin/deleteIssuedObject?id=${id}`,
@@ -81,7 +131,11 @@ export function safetyResponsibilityAdminDeleteIssuedObject(id) {
   });
 }
 
-
+/**
+ * 下发安全责任书(管理端)
+ * @param params - 下发参数,如责任书 ID、接收部门/人员等
+ * @returns Promise<void>
+ */
 export function safetyResponsibilityAdminIssuedSafety(params) {
   return http.request({
     url: '/safetyResponsibility/admin/issuedSafety',
@@ -90,6 +144,11 @@ export function safetyResponsibilityAdminIssuedSafety(params) {
   });
 }
 
+/**
+ * 查询安全责任书详情(部门端)
+ * @param params - 查询参数,通常包含 id
+ * @returns Promise<any> 安全责任书详情(含签收/反馈状态)
+ */
 export function safetyResponsibilityDeptQueryDetail(params) {
   return http.request({
     url: '/safetyResponsibility/dept/queryDetail',
@@ -98,6 +157,11 @@ export function safetyResponsibilityDeptQueryDetail(params) {
   });
 }
 
+/**
+ * 部门端对安全责任书进行签收或反馈
+ * @param params - 包含 id、feedbackContent、status 等字段
+ * @returns Promise<void>
+ */
 export function safetyResponsibilityDeptSignOrFeedback(params) {
   return http.request({
     url: '/safetyResponsibility/dept/signOrFeedback',
@@ -106,6 +170,11 @@ export function safetyResponsibilityDeptSignOrFeedback(params) {
   });
 }
 
+/**
+ * 部门端分页查询接收到的安全责任书
+ * @param params - 分页及过滤条件
+ * @returns Promise<QueryPageResponse>
+ */
 export function safetyResponsibilityDeptQueryPage(params) {
   return http.request({
     url: '/safetyResponsibility/dept/queryPage',
@@ -114,6 +183,11 @@ export function safetyResponsibilityDeptQueryPage(params) {
   });
 }
 
+/**
+ * 管理端审批安全责任书(如反馈内容审核)
+ * @param params - 审批参数,包含 id、approveStatus 等
+ * @returns Promise<void>
+ */
 export function safetyResponsibilityAdminApprove(params) {
   return http.request({
     url: '/safetyResponsibility/admin/approve',
@@ -122,18 +196,26 @@ export function safetyResponsibilityAdminApprove(params) {
   });
 }
 
-// 分页查询区域
+// ==================== 区域检查清单模块 ====================
 
+/**
+ * 区域检查清单 - 分页查询
+ * @param params - 分页查询条件
+ * @returns Promise<QueryPageResponse>
+ */
 export function areaCheckListQueryPage(params) {
   return http.request({
     url: '/areaCheckList/queryPage',
     method: 'post',
     params
   });
-
 }
-// 区域详情
 
+/**
+ * 查询区域检查清单详情
+ * @param params - 查询参数,通常包含 id
+ * @returns Promise<any> 区域检查清单详细信息
+ */
 export function areaCheckListQueryDetail(params) {
   return http.request({
     url: '/areaCheckList/queryDetail',
@@ -142,8 +224,11 @@ export function areaCheckListQueryDetail(params) {
   });
 }
 
-// 正常状态可删除
-
+/**
+ * 删除指定 ID 的区域检查清单(仅限正常状态)
+ * @param id - 清单 ID
+ * @returns Promise<void>
+ */
 export function areaCheckListDelete(id) {
   return http.request({
     url: `/areaCheckList/delete?id=${id}`,
@@ -151,6 +236,11 @@ export function areaCheckListDelete(id) {
   });
 }
 
+/**
+ * 新增区域检查清单
+ * @param params - 区域检查清单表单数据
+ * @returns Promise<void>
+ */
 export function areaCheckListSavaArea(params) {
   return http.request({
     url: `/areaCheckList/saveArea`,
@@ -159,7 +249,11 @@ export function areaCheckListSavaArea(params) {
   });
 }
 
-
+/**
+ * 更新区域检查清单
+ * @param params - 更新数据(需包含 ID)
+ * @returns Promise<void>
+ */
 export function areaCheckListUpdateArea(params) {
   return http.request({
     url: `/areaCheckList/updateArea`,
@@ -168,6 +262,11 @@ export function areaCheckListUpdateArea(params) {
   });
 }
 
+/**
+ * 切换区域检查清单状态(如启用/停用)
+ * @param params - 包含 id 和目标状态
+ * @returns Promise<void>
+ */
 export function areaCheckListChange(params) {
   return http.request({
     url: `/areaCheckList/change`,
@@ -175,6 +274,12 @@ export function areaCheckListChange(params) {
     params
   });
 }
+
+/**
+ * 审批区域检查清单
+ * @param params - 审批参数,如 id、approveResult 等
+ * @returns Promise<void>
+ */
 export function areaCheckListApprove(params) {
   return http.request({
     url: `/areaCheckList/approve`,
@@ -183,15 +288,201 @@ export function areaCheckListApprove(params) {
   });
 }
 
-
+/**
+ * 导出区域检查清单数据
+ * @param params - 导出筛选条件
+ * @returns Promise<Blob> Excel 文件流
+ */
 export function areaCheckListExportArea(params) {
   return http.request({
     url: `/areaCheckList/exportArea`,
     method: 'post',
     params,
     responseType: 'blob',
-  },
-    {
-      isTransformResponse: false,
-    });
+  }, {
+    isTransformResponse: false,
+  });
+}
+
+
+/**
+ * 部门端分页查询安全通知列表
+ * @param params - 分页及查询条件(如通知标题、状态、分页参数等)
+ * @returns Promise<QueryPageResponse> 安全通知分页数据
+ */
+export function safetyNoticeDeptQueryPage(params) {
+  return http.request({
+    url: `/safetyNotice/dept/queryPage`,
+    method: 'post',
+    params,
+  });
+}
+
+/**
+ * 查询安全通知详情(部门端)
+ * @param params - 查询参数,通常包含通知 ID(如 { id: number })
+ * @returns Promise<any> 安全通知的详细内容,包括发布信息、附件、反馈状态等
+ */
+export function safetyNoticeDeptQueryDetail(params) {
+  return http.request({
+    url: `/safetyNotice/dept/queryDetail`,
+    method: 'get',
+    params,
+  });
+}
+
+/**
+ * 部门对安全通知提交反馈
+ * @param params - 反馈内容,通常包含通知 ID、反馈意见、附件等字段
+ * @returns Promise<void>
+ */
+export function safetyNoticeDeptFeedback(params) {
+  return http.request({
+    url: `/safetyNotice/dept/feedback`,
+    method: 'put',
+    params,
+  });
+}
+
+/**
+ * 管理端分页查询安全通知列表
+ * @param params - 分页及筛选条件(如标题、发布状态、时间范围、pageNum、pageSize 等)
+ * @returns Promise<QueryPageResponse> 安全通知分页数据列表
+ */
+export function safetyNoticeAdminQueryPage(params) {
+  return http.request({
+    url: `/safetyNotice/admin/queryPage`,
+    method: 'post',
+    params,
+  });
+}
+/**
+ * 管理端删除指定 ID 的安全通知
+ * @param id - 安全通知的唯一标识 ID
+ * @returns Promise<void>
+ */
+export function safetyNoticeAdminDelete(id) {
+  return http.request({
+    url: `/safetyNotice/admin/delete?id=${id}`,
+    method: 'delete',
+  });
+}
+
+/**
+ * 管理端查询安全通知详情
+ * @param params - 查询参数,通常包含通知 ID(如 { id: number })
+ * @returns Promise<any> 安全通知的完整详细信息,包括发布内容、接收部门、反馈状态等
+ */
+export function safetyNoticeAdminQueryDetail(params) {
+   return http.request({
+    url: `/safetyNotice/admin/queryDetail`,
+    method: 'get',
+    params
+  });
+}
+
+/**
+ * 管理端审核安全通知(如审核部门提交的反馈内容)
+ * @param params - 审核参数,通常包含通知 ID、审核状态(通过/驳回)、审核意见等
+ * @returns Promise<void>
+ */
+export function safetyNoticeAdminApprove(params) {
+  return http.request({
+    url: `/safetyNotice/admin/approve`,
+    method: 'put',
+    params
+  });
+}
+
+
+/**
+ * 管理端查询安全通知的已下发对象列表
+ * @param params - 查询条件,通常包含安全通知 ID、分页参数(pageNum、pageSize)等
+ * @returns Promise<QueryPageResponse> 已下发对象的分页数据
+ */
+export function safetyNoticeAdminQueryIssuedObject(params) {
+  return http.request({
+    url: `/safetyNotice/admin/queryIssuedObject`,
+    method: 'post',
+    params
+  });
+}
+
+/**
+ * 管理端作废(撤销/废止)指定的安全通知
+ * @param params - 作废参数,通常包含安全通知 ID(如 { id: number })及其他必要字段
+ * @returns Promise<void>
+ */
+export function safetyNoticeAdminScrap(id) {
+  return http.request({
+    url: `/safetyNotice/admin/scrap?id=${id}`,
+    method: 'put',
+  });
+}
+
+/**
+ * 管理端导出安全通知的已下发对象列表(如接收部门/人员清单)
+ * @returns Promise<Blob> Excel 或 CSV 文件的二进制流,用于下载
+ */
+export function safetyNoticeAdminExportIssuedObject(params) {
+  return http.request({
+    url: `/safetyNotice/admin/exportIssuedObject`,
+    method: 'post',
+    responseType: 'blob',
+  }, {
+    isTransformResponse: false,
+  });
+}
+
+/**
+ * 管理端新增安全通知
+ * @param params - 安全通知表单数据,通常包含标题、内容、下发范围、附件等字段
+ * @returns Promise<void>
+ */
+export function safetyNoticeAdminSaveSafetyNotice(params) {
+  return http.request({
+    url: `/safetyNotice/admin/saveSafetyNotice`,
+    method: 'post',
+    params
+  });
+}
+
+/**
+ * 查询区域检查清单列表(非分页,通常用于下拉选项或全量轻量查询)
+ * @param params - 查询条件,如区域类型、状态等筛选参数
+ * @returns Promise<Array<any>> 区域检查清单数据列表
+ */
+export function areaCheckListQueryList(params) {
+  return http.request({
+    url: '/areaCheckList/queryList',
+    method: 'get',
+    params
+  });
+}
+
+/**
+ * 管理端更新安全通知信息
+ * @param params - 更新后的安全通知数据,通常包含 ID、标题、内容、下发范围等字段
+ * @returns Promise<void>
+ */
+export function safetyNoticeAdminUpdateSafetyNotice(params) {
+  return http.request({
+    url: '/safetyNotice/admin/updateSafetyNotice',
+    method: 'put',
+    params
+  });
+}
+
+
+/**
+ * 管理端下发安全通知(将通知发布给指定部门或人员)
+ * @param params - 下发参数,通常包含通知 ID、接收对象列表(如部门 IDs 或用户 IDs)等
+ * @returns Promise<void>
+ */
+export function safetyNoticeAdminIssuedNotice(params) {
+  return http.request({
+    url: '/safetyNotice/admin/issuedNotice',
+    method: 'put',
+    params
+  });
 }

+ 130 - 0
src/router/routers/production-safety.ts

@@ -473,6 +473,136 @@ const productionSafetyRoutes = {
 
         }
       },
+      // 安全责任通知管理(部门侧)
+      {
+
+        id: 90040,
+        parentId: 90014,
+        name: 'responsibilityNoticeManage',
+        path: 'responsibility-notice-manage',
+        component: '/production-safety/implement-safety-duty/responsibility-notice-manage-dept/list',
+        meta: {
+          title: '安全责任通知管理(部门侧)',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+      {
+        id: 90041,
+        parentId: 90014,
+        name: 'responsibilityNoticeFeedback',
+        path: 'responsibility-notice-feedback',
+        component: '/production-safety/implement-safety-duty/responsibility-notice-manage-dept/feedback',
+        meta: {
+          title: '安全责任通知反馈(部门侧)',
+          activeMenu: '/work-safety/responsibility-implementation/responsibility-notice-manage',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+      {
+        id: 90042,
+        parentId: 90014,
+        name: 'responsibilityNoticeView',
+        path: 'responsibility-notice-view',
+        component: '/production-safety/implement-safety-duty/responsibility-notice-manage-dept/view',
+        meta: {
+          title: '查看安全责任通知(部门侧)',
+          activeMenu: '/work-safety/responsibility-implementation/responsibility-notice-manage',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+      // 全部安全责任通知管理(管理侧)
+      {
+        id: 91000,
+        parentId: 90014,
+        name: 'responsibilityNoticeManage:admin',
+        path: 'admin-responsibility-notice-manage',
+        component: '/production-safety/implement-safety-duty/responsibility-notice-manage-admin/list',
+        meta: {
+          title: '安全责任通知管理(管理侧全部)',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+      {
+        id: 91001,
+        parentId: 90014,
+        name: 'responsibilityNoticeAdd:admin',
+        path: 'admin-responsibility-notice-add',
+        component: '/production-safety/implement-safety-duty/responsibility-notice-manage-admin/add',
+        meta: {
+          title: '创建责任通知(管理侧全部)',
+          activeMenu: '/work-safety/responsibility-implementation/responsibility-notice-manage-admin',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+      {
+        id: 91002,
+        parentId: 90014,
+        name: 'responsibilityNoticeEdit:admin',
+        path: 'admin-responsibility-notice-edit',
+        component: '/production-safety/implement-safety-duty/responsibility-notice-manage-admin/edit',
+        meta: {
+          title: '编辑责任通知(管理侧全部)',
+          activeMenu: '/work-safety/responsibility-implementation/responsibility-notice-manage-admin',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+      {
+        id: 91003,
+        parentId: 90014,
+        name: 'responsibilityNoticeReview:admin',
+        path: 'admin-responsibility-notice-review',
+        component: '/production-safety/implement-safety-duty/responsibility-notice-manage-admin/review',
+        meta: {
+          title: '安全责任通知材料审核(管理侧全部)',
+          activeMenu: '/work-safety/responsibility-implementation/responsibility-notice-manage-admin',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+      {
+        id: 91004,
+        parentId: 90014,
+        name: 'responsibilityNoticeView:admin',
+        path: 'admin-responsibility-notice-view',
+        component: '/production-safety/implement-safety-duty/responsibility-notice-manage-admin/notice-view',
+        meta: {
+          title: '查看通知对象(管理侧全部)',
+          activeMenu: '/work-safety/responsibility-implementation/responsibility-notice-manage-admin',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+        }
+      },
+      
+   
     ]
     }
   ],

+ 30 - 2
src/views/production-safety/implement-safety-duty/non-public-area-responsibilities/add.vue

@@ -52,7 +52,7 @@
         </el-form-item>
 
         <el-form-item label="安全责任部门" prop="safetyResponsibleDepartment">
-          <el-select
+          <!-- <el-select
             v-model="formValue.safetyResponsibleDepartment"
             placeholder="请选择"
             size="large"
@@ -65,7 +65,20 @@
               :label="item.deptName"
               :value="item.deptName"
             />
-          </el-select>
+          </el-select> -->
+          <el-cascader
+            style="width: 50%"
+            size="large"
+            ref="cascaderRef"
+            popper-class="cascader-popper--custom"
+            v-model="formValue.safetyResponsibleDepartment"
+            :options="firstLevelDepts"
+            :props="cascaderProp"
+            :show-all-levels="false"
+            placeholder="请选择安全责任部门"
+            filterable
+            @change="handleChangeDept('safetyResponsibleDepartment')"
+          />
         </el-form-item>
 
         <el-form-item label="安全责任部门负责人" prop="safetyDepartmentManager">
@@ -127,9 +140,17 @@
   const router = useRouter();
   const formRef = ref<any>(null);
   const submiting = ref(false);
+  const cascaderRef = ref();
 
   const userOptions = ref<any[]>([]);
   const firstLevelDepts = ref<any[]>([]);
+  const cascaderProp = {
+    expandTrigger: 'click',
+    checkStrictly: true,
+    emitPath: false,
+    value: 'id',
+    label: 'deptName',
+  };
 
   const formValue = reactive({
     buildingNo: '',
@@ -166,10 +187,17 @@
     ],
   });
 
+  const handleChangeDept = (prop) => {
+    const deptInfo = cascaderRef.value?.getCheckedNodes();
+    formValue[prop] = deptInfo[0].label;
+    console.log(deptInfo);
+  };
+
   // 获取部门数据
   const getDeptData = () => {
     getAllDepartments().then((res) => {
       firstLevelDepts.value = formatDeptTree(res);
+      console.log('@res:', firstLevelDepts.value);
     });
   };
 

+ 364 - 0
src/views/production-safety/implement-safety-duty/responsibility-notice-manage-admin/add.vue

@@ -0,0 +1,364 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title">
+        <BreadcrumbBack />
+        {{ $route?.meta?.title }}
+      </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <el-form ref="formRef" label-width="auto" :model="formValue" :rules="rules">
+        <el-form-item label="责任通知名称" prop="responsibilityName">
+          <el-input
+            v-model="formValue.responsibilityName"
+            size="large"
+            placeholder="安全责任通知名称"
+            style="width: 50%"
+          />
+        </el-form-item>
+        <el-form-item label="上传附件责任通知文档">
+          <el-radio-group v-model="formValue.type">
+            <el-radio :value="1">公共区域</el-radio>
+            <el-radio :value="2">非公共区域</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="上传附件责任书文档" prop="attachment">
+          <UploadFiles
+            label="上传附件"
+            :maxCount="1"
+            @uploadSuccess="handleUploadSuccess"
+            :fileList="formValue.attachment"
+          />
+        </el-form-item>
+
+        <el-form-item label="责任功能内容" prop="content">
+          <div class="editor-container">
+            <Toolbar style="border-bottom: 1px solid #dcdfe6" :editor="editorRef" />
+            <Editor
+              style="height: 300px; overflow-y: auto"
+              v-model="formValue.content"
+              mode="default"
+              :default-config="editorConfig"
+              @on-created="handleCreated"
+              @on-change="handleEditorChange"
+            />
+          </div>
+        </el-form-item>
+        <el-form-item label=" ">
+          <el-checkbox v-model="formValue.executeObject" label="创建下发执行对象" @change="handleExecuteObjectChange" />
+        </el-form-item>
+        <template v-if="Boolean(formValue.executeObject)">
+          <el-form-item label="通知区域" prop="safetyAreaData">
+            <el-button @click="handleAddArea" type="primary">添加</el-button>
+            <el-table :data="currentTableData" v-if="formValue.safetyAreaData.length" style="margin: 20px 0 0">
+              <!-- <el-table-column type="index" label="序号" width="80" /> -->
+              <el-table-column v-if="formValue.type === 2" label="楼号" prop="buildingCode" width="180" />
+              <el-table-column label="楼层" prop="buildingArea" width="100" />
+
+              <el-table-column label="楼宇/区域" prop="buildingCode" width="180" />
+              <el-table-column label="房间/区域" prop="floorRoomNo" width="180" />
+              <el-table-column label="功能名称" prop="nameFunction" width="120" />
+              <el-table-column label="安全责任所/中心" prop="safetyResponsibleCenter" width="200" />
+              <el-table-column label="安全责任所/中心负责人" prop="safetyCenterManagerName" width="200" />
+              <el-table-column label="安全责任部门" prop="safetyResponsibleDepartment" width="180" />
+              <el-table-column label="安全责任部门负责人" prop="safetyDepartmentManagerName" width="180" />
+              <el-table-column label="安全具体责任人" prop="safetySpecificPersonName" width="180" />
+              <el-table-column label="安全具体责任人联系方式" prop="safetyPersonContact;" width="230" />
+              <el-table-column label="状态" prop="statusName" width="100" />
+              <el-table-column label="操作" width="100" fixed="right">
+                <template #default="scope">
+                  <el-button type="text" size="small" @click="handleDeleteArea(scope)">删除</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+            <div class="pagination-container" v-if="formValue.safetyAreaData.length">
+              <el-pagination
+                background
+                :current-page="queryParams.pageNumber"
+                :page-size="queryParams.pageSize"
+                :total="formValue.safetyAreaData.length"
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange"
+              />
+            </div>
+          </el-form-item>
+          <el-form-item label="计划开始日期" prop="planStartTime">
+            <el-date-picker
+              v-model="formValue.planStartTime"
+              size="large"
+              placeholder="请选择日期"
+              style="width: 50%"
+            />
+          </el-form-item>
+
+          <el-form-item label="计划结束日期" prop="planEndTime">
+            <el-date-picker v-model="formValue.planEndTime" size="large" placeholder="请选择日期" style="width: 50%" />
+          </el-form-item>
+        </template>
+      </el-form>
+    </main>
+    <footer class="safety-platform-container__footer">
+      <el-button @click="$router.push({ name: 'responsibilityNoticeManage:admin' })">返回</el-button>
+      <el-button type="primary" @click="handleSubmit" @close="handleCloseAreaDialog" :loading="submiting"
+        >提交</el-button
+      >
+    </footer>
+  </div>
+  <SelectNotifyArea
+    v-if="selectNotifyAreaOpen"
+    v-model.visible="selectNotifyAreaOpen"
+    :type="formValue.type"
+    @submit="handleSelectionTableData"
+  />
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, nextTick, reactive, watch, shallowRef } from 'vue';
+  import dayjs from 'dayjs';
+  import { ElMessage } from 'element-plus';
+  import { uniqBy, omit } from 'lodash-es';
+
+  import '@wangeditor/editor/dist/css/style.css';
+  import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
+  import { useRouter, useRoute } from 'vue-router';
+  import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
+  import { safetyNoticeAdminSaveSafetyNotice } from '@/api/production-safety/responsibility-implementation';
+  import { formatAttachmentList } from '@/components/UploadFiles/utils';
+  import SelectNotifyArea from './components/SelectNotifyArea.vue';
+
+  const router = useRouter();
+  const route = useRoute();
+  const formRef = ref<any>(null);
+  const editorRef = shallowRef();
+  const submiting = ref(false);
+  const selectNotifyAreaOpen = ref(false);
+  const editorConfig = {
+    placeholder: '请输入...',
+  };
+
+  const queryParams = reactive<any>({
+    pageNumber: 1,
+    pageSize: 3,
+  });
+  const tableData = reactive<any>({
+    data: [],
+    total: 0,
+  });
+
+  const currentTableData = ref([]);
+
+  const formValue = reactive({
+    responsibilityName: '', // 责任通知名称
+    type: Number(route.query.areaType) || 1, // 1公共区域2非公共区域
+    content: '', // 责任通知内容
+    safetyAreaData: [], // 区域分组,多个用,分隔
+    attachment: [], // 附件
+    planStartTime: null, // 计划开始日期
+    planEndTime: null, // 计划结束日期
+    status: '', // 1未下发2待反馈3待审核4已完成5已作废
+    executeObject: 0, // 是否创建执行对象(0否1是)
+  });
+
+  const rules = reactive({
+    responsibilityName: [
+      {
+        required: true,
+        message: '责任书名称不能为空',
+        trigger: 'change',
+      },
+    ],
+    type: [
+      {
+        required: true,
+        message: '请选择类型',
+      },
+    ],
+    attachment: [
+      {
+        required: true,
+        message: '请上传附件',
+      },
+    ],
+
+    content: [
+      {
+        required: true,
+        message: '请输入责任书内容',
+      },
+      {
+        validator: (rule, value, callback) => {
+          const text = (value || '').replace(/<[^>]+>/g, '').trim();
+          callback(text ? undefined : new Error('责任书内容不能为空'));
+        },
+        trigger: 'change',
+      },
+    ],
+    planStartTime: [
+      {
+        required: true,
+        message: '请选择计划开始时间',
+        trigger: 'change',
+      },
+      {
+        validator: (rule, value, callback) => {
+          if (value && formValue.planEndTime) {
+            if (dayjs(value).isAfter(formValue.planEndTime)) {
+              callback(new Error('开始日期不能大于结束日期'));
+              return;
+            }
+          }
+          callback();
+        },
+      },
+    ],
+    planEndTime: [
+      {
+        required: true,
+        message: '请选择计划结束时间',
+        trigger: 'change',
+      },
+      {
+        validator: (rule, value, callback) => {
+          if (value && formValue.planStartTime) {
+            if (dayjs(value).isBefore(formValue.planStartTime)) {
+              callback(new Error('结束日期不能小于开始日期'));
+              return;
+            }
+          }
+          callback();
+        },
+      },
+    ],
+    safetyAreaData: [
+      {
+        required: true,
+        message: '请选择区域',
+        trigger: 'change',
+      },
+      {
+        validator: (rule, value, callback) => {
+          if (value.length === 0) {
+            callback(new Error('区域不能为空'));
+          }
+          callback();
+        },
+      },
+    ],
+  });
+  const handleDeleteArea = (scope) => {
+    formValue.safetyAreaData = formValue.safetyAreaData.filter((item: any) => item.id !== scope.row.id);
+    const totalPages = Math.ceil(formValue.safetyAreaData.length / queryParams.pageSize);
+    if (queryParams.pageNumber > totalPages) {
+      queryParams.pageNumber = totalPages || 1;
+    }
+    displayData(queryParams.pageNumber);
+  };
+  const displayData = (page = 1) => {
+    const start = (page - 1) * queryParams.pageSize;
+    const end = start + queryParams.pageSize;
+    currentTableData.value = formValue.safetyAreaData.slice(start, end);
+  };
+  const handleCurrentChange = (page: number) => {
+    queryParams.pageNumber = page;
+    displayData(page);
+  };
+
+  const handleSizeChange = (size: number) => {
+    queryParams.pageSize = size;
+  };
+
+  const handleSelectionTableData = (data) => {
+    tableData.total = data.length;
+    tableData.data = [...tableData.data, ...data];
+    Object.assign(formValue, {
+      safetyAreaData: uniqBy(tableData.data, 'id'),
+    });
+    formRef.value?.validateField('safetyAreaData');
+    displayData();
+  };
+  const handleAddArea = () => {
+    selectNotifyAreaOpen.value = true;
+  };
+
+  const handleCloseAreaDialog = () => {
+    selectNotifyAreaOpen.value = false;
+  };
+
+  const handleCreated = (value) => {
+    editorRef.value = value;
+  };
+
+  const handleEditorChange = (editor) => {
+    formValue.content = editor.getHtml();
+    if (!formValue.content.replace(/<[^>]+>/g, '').trim()) {
+      return;
+    }
+    nextTick(() => {
+      formRef.value?.validateField('content');
+    });
+  };
+
+  const handleUploadSuccess = (fileList) => {
+    formValue.attachment = fileList;
+    formRef.value.validateField('attachment');
+  };
+  const handleExecuteObjectChange = (value) => {
+    if (!value) {
+      Object.assign(formValue, {
+        planStartTime: null,
+        planEndTime: null,
+      });
+    }
+  };
+  const handleSubmit = () => {
+    formRef.value?.validate(async (valid) => {
+      if (valid) {
+        submiting.value = true;
+        const attachment = await formatAttachmentList(formValue.attachment);
+        safetyNoticeAdminSaveSafetyNotice({
+          ...omit(formValue, 'safetyAreaData'),
+          planStartTime: formValue.planStartTime ? dayjs(formValue.planStartTime).format('YYYY-MM-DD') : null,
+          planEndTime: formValue.planEndTime ? dayjs(formValue.planEndTime).format('YYYY-MM-DD') : null,
+          attachment: JSON.stringify(attachment),
+          executeObject: Number(formValue.executeObject),
+          safetyAreaId: formValue.safetyAreaData.map((item: any) => item.id).join(','),
+        })
+          .then(() => {
+            ElMessage.success('创建成功!');
+            router.push({
+              name: 'responsibilityNoticeManage:admin',
+            });
+          })
+          .finally(() => {
+            submiting.value = false;
+          });
+      } else {
+      }
+    });
+  };
+  watch(
+    () => [formValue.type, formValue.executeObject],
+    () => {
+      tableData.data = [];
+      formValue.safetyAreaData = [];
+      currentTableData.value = [];
+      queryParams.pageNumber = 1;
+      queryParams.pageSize = 3;
+    },
+  );
+  onMounted(() => {});
+</script>
+
+<style lang="scss" scoped>
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+  .editor-container {
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    margin-right: 20px;
+    overflow: hidden;
+  }
+  .pagination-container {
+    margin-top: 20px;
+  }
+</style>

+ 203 - 0
src/views/production-safety/implement-safety-duty/responsibility-notice-manage-admin/components/SelectNotifyArea.vue

@@ -0,0 +1,203 @@
+<template>
+  <el-dialog
+    :model-value="props.modelValue"
+    @update:model-value="$emit('update:modelValue', $event)"
+    title="安全责任通知区域选择"
+    width="700"
+    @close="clearData"
+  >
+    <el-tabs v-model="activeTab">
+      <el-tab-pane :disabled="activeTab !== 1" label="公共区域" :name="1" />
+      <el-tab-pane :disabled="activeTab !== 2" label="非公共区域" :name="2" />
+    </el-tabs>
+    <div class="main">
+      <el-form :inline="true">
+        <el-form-item>
+          <el-input v-model="queryParams.queryParam.nameFunction" placeholder="搜索功能名称" />
+        </el-form-item>
+        <el-form-item>
+          <el-select v-model="queryParams.queryParam.status" clearable placeholder="状态" style="width: 170px">
+            <el-option :value="1" label="未下发" />
+            <el-option :value="2" label="待签署" />
+            <el-option :value="3" label="待反馈材料" />
+            <el-option :value="4" label="待审核" />
+            <el-option :value="5" label="已完成" />
+            <el-option :value="6" label="已作废" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="queryTableList" type="primary">查询</el-button>
+        </el-form-item>
+      </el-form>
+      <div class="table-content">
+        <el-table
+          row-key="id"
+          :data="tableData.data"
+          @selection-change="handleSelectionChange"
+          v-if="queryParams.queryParam.type === 1"
+        >
+          <el-table-column type="selection" :reserve-selection="true" width="55" />
+
+          <el-table-column type="index" label="序号" width="80" />
+          <el-table-column label="楼宇/区域" prop="buildingCode" width="180" />
+          <el-table-column label="房间/区域" prop="floorRoomNo" width="180" />
+          <el-table-column label="功能名称" prop="nameFunction" width="120" />
+          <el-table-column label="安全责任所/中心" prop="safetyResponsibleCenter" width="200" />
+          <el-table-column label="安全责任所/中心负责人" prop="safetyCenterManagerName" width="200" />
+          <el-table-column label="安全责任部门" prop="safetyResponsibleDepartment" width="180" />
+          <el-table-column label="安全责任部门负责人" prop="safetyDepartmentManagerName" width="180" />
+          <el-table-column label="安全具体责任人" prop="safetySpecificPersonName" width="180" />
+          <el-table-column label="安全具体责任人联系方式" prop="safetyPersonContact;" width="230" />
+          <el-table-column label="状态" prop="statusName" width="100" />
+        </el-table>
+        <el-table
+          row-key="id"
+          :data="tableData.data"
+          @selection-change="handleSelectionChange"
+          v-if="queryParams.queryParam.type === 2"
+        >
+          <el-table-column type="selection" :reserve-selection="true" width="55" />
+          <el-table-column type="index" label="序号" width="80" />
+          <el-table-column label="楼号" prop="buildingCode" width="180" />
+          <el-table-column label="楼层" prop="buildingArea" width="100" />
+          <el-table-column label="房间/区域" prop="floorRoomNo" width="180" />
+          <el-table-column label="功能名称" prop="nameFunction" width="120" />
+          <el-table-column label="安全责任所/中心" prop="safetyResponsibleCenter" width="200" />
+          <el-table-column label="安全责任所/中心负责人" prop="safetyCenterManagerName" width="200" />
+          <el-table-column label="安全责任部门" prop="safetyResponsibleDepartment" width="180" />
+          <el-table-column label="安全责任部门负责人" prop="safetyDepartmentManagerName" width="180" />
+          <el-table-column label="安全具体责任人" prop="safetySpecificPersonName" width="180" />
+          <el-table-column label="安全具体责任人联系方式" prop="safetyPersonContact;" width="230" />
+          <el-table-column label="变更原因" prop="changeReason" width="170" />
+          <el-table-column label="状态" prop="statusName" width="100" />
+        </el-table>
+      </div>
+
+      <div class="pagination-container" v-if="tableData.total > 0">
+        <el-pagination
+          background
+          :current-page="queryParams.pageNumber"
+          :page-size="queryParams.pageSize"
+          :total="tableData.total"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </div>
+
+    <template #footer>
+      <div>
+        <el-button type="primary" @click="submitForm" :loading="submitLoading"> 确定 </el-button>
+        <el-button @click="handleCancel">取消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+  import { ref, reactive, onMounted, watch } from 'vue';
+  import { areaCheckListQueryPage } from '@/api/production-safety/responsibility-implementation';
+
+  const props = defineProps<{
+    type: number;
+    modelValue: boolean;
+  }>();
+  const emit = defineEmits(['close', 'submit', 'update:modelValue']);
+  const submitLoading = ref(false);
+  const activeTab = ref(props.type);
+  const selectionList = ref([]);
+  const queryParams = reactive<any>({
+    pageNumber: 1,
+    pageSize: 3,
+    queryParam: {
+      type: activeTab.value,
+      nameFunction: '',
+      status: '',
+      responsibilityPersonId: '',
+    },
+  });
+  const tableData = reactive({
+    data: [],
+    total: 0,
+  });
+
+  function dialogShow() {}
+  function dialogHide() {
+    emit('close');
+  }
+  function clearData() {}
+
+  function submitForm() {
+    emit('submit', selectionList.value);
+    emit('update:modelValue', false);
+  }
+  const handleSelectionChange = (val: []) => {
+    selectionList.value = val;
+    console.log(selectionList.value);
+  };
+
+  const queryTableList = () => {
+    console.log(queryParams.queryParam.type);
+    areaCheckListQueryPage(queryParams).then((res) => {
+      tableData.data = res.records;
+      tableData.total = res.totalRow;
+    });
+  };
+
+  const handleCancel = () => {
+    selectionList.value = [];
+    emit('update:modelValue', false);
+  };
+  const handleSizeChange = (value) => {};
+  const handleCurrentChange = (value) => {
+    queryParams.pageNumber = value;
+    queryTableList();
+  };
+  watch(
+    () => props.type,
+    (a, b) => {
+      activeTab.value = a;
+    },
+  );
+  watch(
+    () => activeTab.value,
+    (a, b) => {
+      Object.assign(queryParams, {
+        queryParam: {
+          ...queryParams.queryParam,
+          type: a,
+        },
+      });
+      queryTableList();
+    },
+  );
+
+  onMounted(() => {
+    queryTableList();
+  });
+
+  defineExpose({
+    submitLoading,
+    dialogShow,
+    dialogHide,
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+  .main {
+    display: flex;
+    flex-direction: column;
+    max-height: 400px;
+  }
+  .table-content {
+    flex: 1;
+    overflow: hidden;
+    overflow-y: auto;
+  }
+  .pagination-container {
+    margin-top: 20px;
+  }
+</style>

+ 391 - 0
src/views/production-safety/implement-safety-duty/responsibility-notice-manage-admin/edit.vue

@@ -0,0 +1,391 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title">
+        <BreadcrumbBack />
+        {{ $route?.meta?.title }}
+      </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <el-form ref="formRef" label-width="auto" :model="formValue" :rules="rules">
+        <el-form-item label="责任通知名称" prop="responsibilityName">
+          <el-input
+            v-model="formValue.responsibilityName"
+            size="large"
+            placeholder="安全责任通知名称"
+            style="width: 50%"
+          />
+        </el-form-item>
+        <el-form-item label="上传附件责任通知文档">
+          <el-radio-group v-model="formValue.type">
+            <el-radio :value="1">公共区域</el-radio>
+            <el-radio :value="2">非公共区域</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="上传附件责任书文档" prop="attachment">
+          <UploadFiles
+            label="上传附件"
+            :maxCount="1"
+            @uploadSuccess="handleUploadSuccess"
+            :fileList="formValue.attachment"
+          />
+        </el-form-item>
+
+        <el-form-item label="责任功能内容" prop="content">
+          <div class="editor-container">
+            <Toolbar style="border-bottom: 1px solid #dcdfe6" :editor="editorRef" />
+            <Editor
+              style="height: 300px; overflow-y: auto"
+              v-model="formValue.content"
+              mode="default"
+              :default-config="editorConfig"
+              @on-created="handleCreated"
+              @on-change="handleEditorChange"
+            />
+          </div>
+        </el-form-item>
+        <el-form-item label=" ">
+          <el-checkbox v-model="formValue.executeObject" label="创建下发执行对象" @change="handleExecuteObjectChange" />
+        </el-form-item>
+        <template v-if="Boolean(formValue.executeObject)">
+          <el-form-item label="通知区域" prop="safetyAreaData">
+            <el-button @click="handleAddArea" type="primary">添加</el-button>
+            <el-table :data="currentTableData" v-if="formValue.safetyAreaData.length" style="margin: 20px 0 0">
+              <!-- <el-table-column type="index" label="序号" width="80" /> -->
+              <el-table-column v-if="formValue.type === 2" label="楼号" prop="buildingCode" width="180" />
+              <el-table-column label="楼层" prop="buildingArea" width="100" />
+
+              <el-table-column label="楼宇/区域" prop="buildingCode" width="180" />
+              <el-table-column label="房间/区域" prop="floorRoomNo" width="180" />
+              <el-table-column label="功能名称" prop="nameFunction" width="120" />
+              <el-table-column label="安全责任所/中心" prop="safetyResponsibleCenter" width="200" />
+              <el-table-column label="安全责任所/中心负责人" prop="safetyCenterManagerName" width="200" />
+              <el-table-column label="安全责任部门" prop="safetyResponsibleDepartment" width="180" />
+              <el-table-column label="安全责任部门负责人" prop="safetyDepartmentManagerName" width="180" />
+              <el-table-column label="安全具体责任人" prop="safetySpecificPersonName" width="180" />
+              <el-table-column label="安全具体责任人联系方式" prop="safetyPersonContact;" width="230" />
+              <el-table-column label="状态" prop="statusName" width="100" />
+              <el-table-column label="操作" width="100" fixed="right">
+                <template #default="scope">
+                  <el-button type="text" size="small" @click="handleDeleteArea(scope)">删除</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+            <div class="pagination-container" v-if="formValue.safetyAreaData.length">
+              <el-pagination
+                background
+                :current-page="queryParams.pageNumber"
+                :page-size="queryParams.pageSize"
+                :total="formValue.safetyAreaData.length"
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange"
+              />
+            </div>
+          </el-form-item>
+          <el-form-item label="计划开始日期" prop="planStartTime">
+            <el-date-picker
+              v-model="formValue.planStartTime"
+              size="large"
+              placeholder="请选择日期"
+              style="width: 50%"
+            />
+          </el-form-item>
+
+          <el-form-item label="计划结束日期" prop="planEndTime">
+            <el-date-picker v-model="formValue.planEndTime" size="large" placeholder="请选择日期" style="width: 50%" />
+          </el-form-item>
+        </template>
+      </el-form>
+    </main>
+    <footer class="safety-platform-container__footer">
+      <el-button @click="$router.push({ name: 'responsibilityNoticeManage:admin' })">返回</el-button>
+      <el-button type="primary" @click="handleSubmit" @close="handleCloseAreaDialog" :loading="submiting"
+        >提交</el-button
+      >
+    </footer>
+  </div>
+  <SelectNotifyArea
+    v-if="selectNotifyAreaOpen"
+    v-model.visible="selectNotifyAreaOpen"
+    :type="formValue.type"
+    @submit="handleSelectionTableData"
+  />
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, nextTick, reactive, watch, shallowRef } from 'vue';
+  import dayjs from 'dayjs';
+  import { ElMessage } from 'element-plus';
+  import { uniqBy, omit } from 'lodash-es';
+
+  import '@wangeditor/editor/dist/css/style.css';
+  import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
+  import { useRouter, useRoute } from 'vue-router';
+  import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
+  import {
+    safetyNoticeAdminQueryDetail,
+    safetyNoticeAdminUpdateSafetyNotice,
+    areaCheckListQueryList,
+  } from '@/api/production-safety/responsibility-implementation';
+  import { formatAttachmentList } from '@/components/UploadFiles/utils';
+  import SelectNotifyArea from './components/SelectNotifyArea.vue';
+
+  const router = useRouter();
+  const route = useRoute();
+  const formRef = ref<any>(null);
+  const editorRef = shallowRef();
+  const submiting = ref(false);
+  const selectNotifyAreaOpen = ref(false);
+  const editorConfig = {
+    placeholder: '请输入...',
+  };
+
+  const queryParams = reactive<any>({
+    pageNumber: 1,
+    pageSize: 3,
+  });
+  const tableData = reactive<any>({
+    data: [],
+    total: 0,
+  });
+
+  const currentTableData = ref([]);
+
+  const formValue = reactive({
+    responsibilityName: '', // 责任通知名称
+    type: Number(route.query.areaType) || 1, // 1公共区域2非公共区域
+    content: '', // 责任通知内容
+    safetyAreaData: [], // 区域分组,多个用,分隔
+    attachment: [], // 附件
+    planStartTime: null, // 计划开始日期
+    planEndTime: null, // 计划结束日期
+    status: '', // 1未下发2待反馈3待审核4已完成5已作废
+    executeObject: 0, // 是否创建执行对象(0否1是)
+  });
+
+  const rules = reactive({
+    responsibilityName: [
+      {
+        required: true,
+        message: '责任书名称不能为空',
+        trigger: 'change',
+      },
+    ],
+    type: [
+      {
+        required: true,
+        message: '请选择类型',
+      },
+    ],
+    attachment: [
+      {
+        required: true,
+        message: '请上传附件',
+      },
+    ],
+
+    content: [
+      {
+        required: true,
+        message: '请输入责任书内容',
+      },
+      {
+        validator: (rule, value, callback) => {
+          const text = (value || '').replace(/<[^>]+>/g, '').trim();
+          callback(text ? undefined : new Error('责任书内容不能为空'));
+        },
+        trigger: 'change',
+      },
+    ],
+    planStartTime: [
+      {
+        required: true,
+        message: '请选择计划开始时间',
+        trigger: 'change',
+      },
+      {
+        validator: (rule, value, callback) => {
+          if (value && formValue.planEndTime) {
+            if (dayjs(value).isAfter(formValue.planEndTime)) {
+              callback(new Error('开始日期不能大于结束日期'));
+              return;
+            }
+          }
+          callback();
+        },
+      },
+    ],
+    planEndTime: [
+      {
+        required: true,
+        message: '请选择计划结束时间',
+        trigger: 'change',
+      },
+      {
+        validator: (rule, value, callback) => {
+          if (value && formValue.planStartTime) {
+            if (dayjs(value).isBefore(formValue.planStartTime)) {
+              callback(new Error('结束日期不能小于开始日期'));
+              return;
+            }
+          }
+          callback();
+        },
+      },
+    ],
+    safetyAreaData: [
+      {
+        required: true,
+        message: '请选择区域',
+        trigger: 'change',
+      },
+      {
+        validator: (rule, value, callback) => {
+          if (value.length === 0) {
+            callback(new Error('区域不能为空'));
+          }
+          callback();
+        },
+      },
+    ],
+  });
+  const handleDeleteArea = (scope) => {
+    formValue.safetyAreaData = formValue.safetyAreaData.filter((item: any) => item.id !== scope.row.id);
+    const totalPages = Math.ceil(formValue.safetyAreaData.length / queryParams.pageSize);
+    if (queryParams.pageNumber > totalPages) {
+      queryParams.pageNumber = totalPages || 1;
+    }
+    displayData(queryParams.pageNumber);
+  };
+  const displayData = (page = 1) => {
+    const start = (page - 1) * queryParams.pageSize;
+    const end = start + queryParams.pageSize;
+    currentTableData.value = formValue.safetyAreaData.slice(start, end);
+  };
+  const handleCurrentChange = (page: number) => {
+    queryParams.pageNumber = page;
+    displayData(page);
+  };
+
+  const handleSizeChange = (size: number) => {
+    queryParams.pageSize = size;
+  };
+
+  const handleSafetyNoticeAdminQueryDetail = () => {
+    safetyNoticeAdminQueryDetail({ id: Number(route.query.id) }).then((res) => {
+      handleAreaCheckListQueryList({
+        ids: res.safetyAreaId,
+      });
+      Object.assign(formValue, {
+        ...res,
+        executeObject: Boolean(res.executeObject),
+        attachment: res.attachment ? JSON.parse(res.attachment) : [],
+        planStartTime: res.planStartTime ? dayjs(res.planStartTime) : null,
+        planEndTime: res.planEndTime ? dayjs(res.planEndTime) : null,
+      });
+    });
+  };
+
+  const handleAreaCheckListQueryList = (params) => {
+    areaCheckListQueryList(params).then((res) => {
+      handleSelectionTableData(res);
+    });
+  };
+
+  const handleSelectionTableData = (data) => {
+    tableData.total = data.length;
+    tableData.data = [...tableData.data, ...data];
+    Object.assign(formValue, {
+      safetyAreaData: uniqBy(tableData.data, 'id'),
+    });
+    formRef.value?.validateField('safetyAreaData');
+    displayData();
+  };
+  const handleAddArea = () => {
+    selectNotifyAreaOpen.value = true;
+  };
+
+  const handleCloseAreaDialog = () => {
+    selectNotifyAreaOpen.value = false;
+  };
+
+  const handleCreated = (value) => {
+    editorRef.value = value;
+  };
+
+  const handleEditorChange = (editor) => {
+    formValue.content = editor.getHtml();
+    if (!formValue.content.replace(/<[^>]+>/g, '').trim()) {
+      return;
+    }
+    nextTick(() => {
+      formRef.value?.validateField('content');
+    });
+  };
+
+  const handleUploadSuccess = (fileList) => {
+    formValue.attachment = fileList;
+    formRef.value.validateField('attachment');
+  };
+  const handleExecuteObjectChange = (value) => {
+    if (!value) {
+      Object.assign(formValue, {
+        planStartTime: null,
+        planEndTime: null,
+      });
+    }
+  };
+  const handleSubmit = () => {
+    formRef.value?.validate(async (valid) => {
+      if (valid) {
+        submiting.value = true;
+        const attachment = await formatAttachmentList(formValue.attachment);
+        safetyNoticeAdminUpdateSafetyNotice({
+          ...omit(formValue, 'safetyAreaData'),
+          planStartTime: formValue.planStartTime ? dayjs(formValue.planStartTime).format('YYYY-MM-DD') : null,
+          planEndTime: formValue.planEndTime ? dayjs(formValue.planEndTime).format('YYYY-MM-DD') : null,
+          attachment: JSON.stringify(attachment),
+          executeObject: Number(formValue.executeObject),
+          safetyAreaId: formValue.safetyAreaData.map((item: any) => item.id).join(','),
+        })
+          .then(() => {
+            ElMessage.success('创建成功!');
+            router.push({
+              name: 'responsibilityNoticeManage:admin',
+            });
+          })
+          .finally(() => {
+            submiting.value = false;
+          });
+      } else {
+      }
+    });
+  };
+  watch(
+    () => [formValue.type, formValue.executeObject],
+    () => {
+      tableData.data = [];
+      formValue.safetyAreaData = [];
+      currentTableData.value = [];
+      queryParams.pageNumber = 1;
+      queryParams.pageSize = 3;
+    },
+  );
+  onMounted(async () => {
+    handleSafetyNoticeAdminQueryDetail();
+  });
+</script>
+
+<style lang="scss" scoped>
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+  .editor-container {
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    margin-right: 20px;
+    overflow: hidden;
+  }
+  .pagination-container {
+    margin-top: 20px;
+  }
+</style>

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

@@ -0,0 +1,325 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title"> 安全责任通知管理 </div>
+      <el-tabs v-model="activeTab">
+        <el-tab-pane label="全部" name="" />
+        <el-tab-pane label="公共区域" :name="1" />
+        <el-tab-pane label="非公共区域" :name="2" />
+      </el-tabs>
+    </header>
+    <main class="safety-platform-container__main">
+      <div class="search-form">
+        <el-form :inline="true">
+          <el-form-item label="安全责任书">
+            <el-input
+              v-model="queryParams.queryParam.responsibilityName"
+              placeholder="搜索安全责任书名称"
+              style="width: 170px"
+            />
+          </el-form-item>
+          <el-form-item label="状态">
+            <el-select v-model="queryParams.queryParam.status" clearable placeholder="状态" style="width: 170px">
+              <el-option :value="1" label="未下发" />
+              <el-option :value="2" label="待签署" />
+              <el-option :value="3" label="待反馈材料" />
+              <el-option :value="4" label="待审核" />
+              <el-option :value="5" label="已完成" />
+              <el-option :value="6" label="已作废" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="区域类型" v-if="activeTab === ''">
+            <el-select v-model="queryParams.queryParam.type" clearable placeholder="选择区域类型" style="width: 170px">
+              <el-option :value="1" label="公共区域" />
+              <el-option :value="2" label="非公共区域" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="计划日期">
+            <el-date-picker
+              v-model="queryParams.queryParam.date"
+              clearable
+              type="daterange"
+              start-placeholder="开始时间"
+              end-placeholder="结束时间"
+              style="width: 230px"
+            />
+          </el-form-item>
+        </el-form>
+
+        <div>
+          <el-button
+            type="primary"
+            @click="
+              $router.push({
+                name: 'responsibilityNoticeAdd:admin',
+                query: {
+                  areaType: activeTab,
+                },
+              })
+            "
+            >添加
+          </el-button>
+          <el-button type="primary" @click="queryTableList">查询</el-button>
+          <el-button @click="handleRestParams">重置</el-button>
+        </div>
+      </div>
+
+      <div class="table-content">
+        <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="safetyAreaName" width="180" />
+          <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">
+            <template #default="scope">
+              <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>
+            </template>
+          </el-table-column>
+          <el-table-column label="计划完成时间" prop="planEndTime" width="150" />
+          <el-table-column fixed="right" min-width="300" label="操作">
+            <template #default="scope">
+              <el-button
+                link
+                type="primary"
+                @click="
+                  $router.push({
+                    name: 'responsibilityNoticeEdit:admin',
+                    query: {
+                      areaType: activeTab,
+                      id: scope.row.id,
+                    },
+                  })
+                "
+                >编辑</el-button
+              >
+              <el-button
+                link
+                type="primary"
+                @click="
+                  $router.push({
+                    name: 'responsibilityNoticeView:admin',
+                    query: {
+                      areaType: activeTab,
+                      id: scope.row.id,
+                    },
+                  })
+                "
+                >通知对象</el-button
+              >
+              <el-button link type="primary">发送</el-button>
+              <el-button
+                type="primary"
+                link
+                v-if="scope.row.status === 4"
+                @click="
+                  $router.push({
+                    name: 'responsibilityNoticeView:admin',
+                    query: { id: scope.row.id, status: scope.row.status },
+                  })
+                "
+                >审核</el-button
+              >
+              <el-button v-if="scope.row.status === 1" type="primary" link @click="handleConfirmDeleteRow(scope)"
+                >删除</el-button
+              >
+              <!-- <el-button type="primary" link @click="handleScrap(scope)">作废</el-button> -->
+              <el-button type="primary" link @click="handleDownloadLink(scope)">下载</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div class="pagination-container" v-if="tableData.total > 0">
+        <el-pagination
+          background
+          :current-page="queryParams.pageNumber"
+          :page-size="queryParams.pageSize"
+          :total="tableData.total"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </main>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, reactive, watch } from 'vue';
+  import dayjs from 'dayjs';
+  import { ElMessage } from 'element-plus';
+  import { useRouter } from 'vue-router';
+  import {
+    safetyNoticeAdminQueryPage,
+    safetyNoticeAdminDelete,
+    safetyNoticeAdminScrap,
+  } from '@/api/production-safety/responsibility-implementation';
+  import { omit } from 'lodash-es';
+  import { queryUserGroupPage } from '@/api/system/person-group';
+
+  import { unformatAttachment } from '@/components/UploadFiles/utils';
+  import { downloadFile } from '@/views/disaster/utils';
+
+  const router = useRouter();
+  const activeTab = ref<any>('');
+  const groupList = ref<any[]>([]);
+  const queryParams = reactive<any>({
+    pageNumber: 1,
+    pageSize: 10,
+    queryParam: {
+      responsibilityName: '',
+      status: '',
+      date: '',
+      type: '',
+    },
+  });
+
+  const tableData = reactive({
+    data: [],
+    total: 0,
+  });
+
+  watch(
+    () => activeTab.value,
+    (a) => {
+      Object.assign(queryParams, {
+        queryParam: {
+          ...queryParams.queryParam,
+          type: a,
+        },
+      });
+      queryTableList();
+    },
+  );
+
+  const handleScrap = (scope) => {
+    safetyNoticeAdminScrap(scope.row.id).then(() => {
+      ElMessage.success('作废成功');
+      queryTableList();
+    });
+  };
+
+  const handleQueryUserGroupPage = () => {
+    queryUserGroupPage({
+      pageNumber: 1,
+      pageSize: 500,
+    }).then((res) => {
+      groupList.value = res.records;
+    });
+  };
+
+  const handleSizeChange = (value) => {};
+  const handleCurrentChange = (value) => {
+    queryParams.pageNumber = value;
+    queryTableList();
+  };
+
+  const handleDownloadLink = (scope) => {
+    const attachment = unformatAttachment(scope.row.attachment);
+    attachment?.forEach((item: any) => {
+      downloadFile(item.fileUrl, item.fileName);
+    });
+  };
+  const handleConfirmDeleteRow = (scope) => {
+    safetyNoticeAdminDelete(scope.row.id).then(() => {
+      ElMessage.success('删除成功!');
+      queryTableList();
+    });
+  };
+  const queryTableList = () => {
+    console.log(queryParams.queryParam.type);
+    safetyNoticeAdminQueryPage({
+      ...queryParams,
+      queryParam: {
+        ...omit(queryParams.queryParam, 'date'),
+        startTime: queryParams.queryParam.date?.[0]
+          ? dayjs(queryParams.queryParam.date?.[0]).format('YYYY-MM-DD')
+          : undefined,
+        endTime: queryParams.queryParam.date?.[1]
+          ? dayjs(queryParams.queryParam.date?.[1]).format('YYYY-MM-DD')
+          : undefined,
+      },
+    }).then((res) => {
+      tableData.data = res.records;
+      tableData.total = res.totalRow;
+    });
+  };
+  const handleRestParams = () => {
+    activeTab.value = '';
+    Object.assign(queryParams, {
+      pageNumber: 1,
+      pageSize: 10,
+      queryParam: {
+        responsibilityName: '',
+        status: '',
+        date: '',
+        type: '',
+      },
+    });
+    queryTableList();
+  };
+
+  onMounted(async () => {
+    await handleQueryUserGroupPage();
+    queryTableList();
+  });
+</script>
+
+<style lang="scss" scoped>
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+
+  :deep(.el-tabs__header) {
+    margin: 0;
+  }
+  :deep(.el-tabs__item) {
+    font-size: 14px !important;
+  }
+  :deep(.flexContent) {
+    display: flex;
+  }
+  :deep(.breadcrumb .title) {
+    margin-left: 0;
+  }
+
+  :deep(.el-form) {
+    flex: 1;
+    display: flex;
+    row-gap: 15px;
+    flex-wrap: wrap;
+  }
+  :deep(.el-form-item) {
+    margin-bottom: 0;
+  }
+  :deep(main) {
+    display: flex;
+    flex-direction: column;
+  }
+  .search-form {
+    min-width: 800px;
+    display: flex;
+
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+  }
+
+  .button-content {
+    margin-bottom: 20px;
+  }
+  .table-content {
+    flex: 1;
+    overflow: hidden;
+    overflow-y: auto;
+  }
+  .page-content {
+    display: flex;
+    justify-content: flex-end;
+  }
+</style>

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

@@ -0,0 +1,333 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title">
+        <BreadcrumbBack />
+        {{ detailData?.responsibilityName }}
+      </div>
+      <div class="detail-content">
+        <span>类别名称:{{ detailData?.safetyAreaTypeName }} </span>
+        <span>创建人:{{ detailData?.createdByName }} </span>
+        <span>创建时间:{{ detailData?.createdAt }} </span>
+      </div>
+      <el-tabs v-model="activeTab">
+        <el-tab-pane label="全部" name="" />
+        <el-tab-pane label="待反馈材料" name="3" />
+        <el-tab-pane label="待审核" name="4" />
+        <el-tab-pane label="已完成" name="5" />
+        <el-tab-pane label="已作废" name="6" />
+      </el-tabs>
+    </header>
+    <main class="safety-platform-container__main">
+      <div class="search-form">
+        <el-form :inline="true">
+          <el-form-item label="状态" v-if="activeTab">
+            <el-select v-model="queryParams.queryParam.status" clearable placeholder="状态" style="width: 170px">
+              <el-option :value="1" label="未下发" />
+              <el-option :value="2" label="待签署" />
+              <el-option :value="3" label="待反馈材料" />
+              <el-option :value="4" label="待审核" />
+              <el-option :value="5" label="已完成" />
+              <el-option :value="6" label="已作废" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="区域类型">
+            <el-select v-model="queryParams.queryParam.type" clearable placeholder="选择区域类型" style="width: 170px">
+              <el-option :value="1" label="公共区域" />
+              <el-option :value="2" label="非公共区域" />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="所属部门">
+            <el-select v-model="queryParams.queryParam.userGroupId" placeholder="分组名称" style="width: 170px">
+              <el-option v-for="group in groupList" :key="group.id" :label="group.name" :value="group.id" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="计划日期">
+            <el-date-picker
+              v-model="queryParams.queryParam.date"
+              clearable
+              type="daterange"
+              start-placeholder="开始时间"
+              end-placeholder="结束时间"
+              style="width: 230px"
+            />
+          </el-form-item>
+        </el-form>
+
+        <div>
+          <el-button type="primary" @click="handleExport">导出 </el-button>
+          <el-button type="primary" @click="queryTableList">查询</el-button>
+          <el-button @click="handleRestParams">重置</el-button>
+        </div>
+      </div>
+
+      <div class="table-content">
+        <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="userGroupName" />
+          <el-table-column label="计划完成时间" prop="planEndTime" />
+          <el-table-column fixed="right" width="200" label="操作">
+            <template #default="scope">
+              <el-popconfirm
+                v-if="scope.row.status === 1 || scope.row.status === 6"
+                title="确定要删除吗?"
+                @confirm="handleConfirmDeleteRow(scope)"
+              >
+                <template #reference>
+                  <el-button type="primary" link>删除</el-button>
+                </template>
+              </el-popconfirm>
+              <el-popconfirm v-if="scope.row.status === 6">
+                <el-button type="primary" link @click="handleScrap(scope)">作废</el-button>
+              </el-popconfirm>
+              <el-button
+                v-if="scope.row.status === 4"
+                type="primary"
+                link
+                @click="
+                  $router.push({
+                    name: 'agreeDocumentReview',
+                    query: { id: scope.row.id },
+                  })
+                "
+                >审核
+              </el-button>
+              <el-button type="primary" link @click="handleDownloadLink(scope)">下载</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div class="pagination-container">
+        <el-pagination
+          background
+          :current-page="queryParams.pageNumber"
+          :page-size="queryParams.pageSize"
+          :total="tableData.total"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </main>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, reactive, watch } from 'vue';
+  import dayjs from 'dayjs';
+  import { ElMessage } from 'element-plus';
+  import { useRouter, useRoute } from 'vue-router';
+  import { omit } from 'lodash-es';
+
+  import { queryUserGroupPage } from '@/api/system/person-group';
+  import {
+    safetyNoticeAdminQueryIssuedObject,
+    safetyNoticeAdminExportIssuedObject,
+    safetyResponsibilityAdminDeleteIssuedObject,
+    safetyNoticeAdminScrap,
+  } from '@/api/production-safety/responsibility-implementation';
+  import { downloadByData } from '@/utils/file/download';
+  import { unformatAttachment } from '@/components/UploadFiles/utils';
+  import { downloadFile } from '@/views/disaster/utils';
+
+  const router = useRouter();
+  const route = useRoute();
+  const activeTab = ref<any>('');
+  const groupList = ref<any>([]);
+
+  const queryParams = reactive<any>({
+    pageNumber: 1,
+    pageSize: 10,
+    queryParam: {
+      adminId: route.query.id,
+      status: '',
+      date: '',
+      type: route.query.type || '',
+    },
+  });
+
+  const detailData = reactive({
+    createdAt: '',
+    createdByName: '',
+    safetyAreaTypeName: '',
+    responsibilityName: '',
+  });
+
+  const tableData = reactive({
+    data: [],
+    total: 0,
+  });
+
+  const handleQueryUserGroupPage = () => {
+    queryUserGroupPage({
+      pageNumber: 1,
+      pageSize: 500,
+    }).then((res) => {
+      groupList.value = res.records;
+    });
+  };
+
+  const handleSizeChange = () => {};
+  const handleCurrentChange = () => {};
+  const handleDownloadLink = (scope) => {
+    const attachment = unformatAttachment(scope.row.attachment);
+    attachment?.forEach((item: any) => {
+      downloadFile(item.fileUrl, item.fileName);
+    });
+  };
+
+  const handleScrap = (scope) => {
+    safetyNoticeAdminScrap(scope.row.id).then(() => {
+      ElMessage.success('作废成功');
+      queryTableList();
+    });
+  };
+
+  const handleExport = () => {
+    safetyNoticeAdminExportIssuedObject({
+      ...queryParams,
+      queryParam: {
+        ...omit(queryParams.queryParam, 'date'),
+        adminId: Number(queryParams.queryParam.adminId),
+        status: queryParams.queryParam.status,
+        startTime: queryParams.queryParam.date?.[0]
+          ? dayjs(queryParams.queryParam.date?.[0]).format('YYYY-MM-DD')
+          : undefined,
+        endTime: queryParams.queryParam.date?.[1]
+          ? dayjs(queryParams.queryParam.date?.[1]).format('YYYY-MM-DD')
+          : undefined,
+      },
+    }).then((res) => {
+      if (!res) {
+        throw new Error('下载文件失败');
+      }
+      downloadByData(res, `${Date.now()}.xlsx`);
+      ElMessage.success('导出文件成功');
+    });
+  };
+  const handleConfirmDeleteRow = (scope) => {
+    safetyResponsibilityAdminDeleteIssuedObject(scope.row.id).then(() => {
+      ElMessage.success('删除成功!');
+      queryTableList();
+    });
+  };
+  const queryTableList = () => {
+    safetyNoticeAdminQueryIssuedObject({
+      ...queryParams,
+      queryParam: {
+        ...omit(queryParams.queryParam, 'date'),
+        adminId: queryParams.queryParam.adminId,
+        startTime: queryParams.queryParam.date?.[0]
+          ? dayjs(queryParams.queryParam.date?.[0]).format('YYYY-MM-DD')
+          : undefined,
+        endTime: queryParams.queryParam.date?.[1]
+          ? dayjs(queryParams.queryParam.date?.[1]).format('YYYY-MM-DD')
+          : undefined,
+      },
+    }).then((res) => {
+      tableData.data = res.pages.records;
+      tableData.total = res.pages.totalRow;
+      Object.keys(detailData).forEach((item) => {
+        if (item in res) {
+          detailData[item] = res[item];
+        }
+      });
+      console.log(detailData);
+    });
+  };
+  const handleRestParams = () => {
+    Object.assign(queryParams, {
+      pageNumber: 1,
+      pageSize: 10,
+      queryParam: {
+        ...queryParams.queryParam,
+        status: '',
+        date: '',
+        userGroupId: '',
+      },
+    });
+    queryTableList();
+  };
+
+  watch(route.query, (a, b) => {
+    console.log('a:', a, b);
+    //
+  });
+
+  onMounted(async () => {
+    activeTab.value = route.query.status;
+    Object.assign(queryParams, {
+      queryParam: {
+        ...queryParams.queryParam,
+        status: Number(activeTab.value),
+      },
+    });
+    await handleQueryUserGroupPage();
+
+    queryTableList();
+  });
+</script>
+
+<style lang="scss" scoped>
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+  .safety-platform-container__header {
+    padding-bottom: 0 !important;
+  }
+  :deep(.el-tabs__header) {
+    margin: 0;
+  }
+  :deep(.el-tabs__item) {
+    font-size: 14px !important;
+  }
+  :deep(.flexContent) {
+    display: flex;
+  }
+  :deep(.breadcrumb .title) {
+    margin-left: 0;
+  }
+
+  :deep(.el-form) {
+    flex: 1;
+    display: flex;
+    row-gap: 15px;
+    flex-wrap: wrap;
+  }
+  :deep(.el-form-item) {
+    margin-bottom: 0;
+  }
+  :deep(main) {
+    display: flex;
+    flex-direction: column;
+  }
+  .detail-content {
+    display: flex;
+    gap: 30px;
+    margin: 10px 0;
+    font-size: 14px;
+  }
+  .search-form {
+    min-width: 800px;
+    display: flex;
+
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+  }
+
+  .button-content {
+    margin-bottom: 20px;
+  }
+  .table-content {
+    flex: 1;
+    overflow: hidden;
+    overflow-y: auto;
+  }
+  .page-content {
+    display: flex;
+    justify-content: flex-end;
+  }
+</style>

+ 280 - 0
src/views/production-safety/implement-safety-duty/responsibility-notice-manage-admin/review.vue

@@ -0,0 +1,280 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title"> {{ formData?.safetyAreaName }} </div>
+      <div class="detail-content">
+        <span>类型:{{ formData?.safetyAreaName }} </span>
+        <span>创建人:{{ formData?.createdByName }} </span>
+        <span>创建时间:{{ formData?.createdAt }} </span>
+      </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <el-form ref="formRef" label-width="auto" :model="formData" :rules="rules">
+        <h4>基本信息</h4>
+        <div class="detail-ct">
+          <div class="row">
+            <div class="col">
+              <div class="label">责任书名称:</div>
+              <div class="value">{{ formData.responsibilityName }}</div>
+            </div>
+            <div class="col">
+              <div class="label">状态:</div>
+              <div class="value">{{ formData.statusName }}</div>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col">
+              <div class="label">签署人数:</div>
+              <div class="value">{{ formData.signedQuantity }}</div>
+            </div>
+            <div class="col">
+              <div class="label">下发对象:</div>
+              <div class="value">{{ formData.userGroupName }}</div>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col">
+              <div class="label">计划开始日期:</div>
+              <div class="value">{{ formData.planStartTime }}</div>
+            </div>
+            <div class="col">
+              <div class="label">计划结束日期:</div>
+              <div class="value">{{ formData.planEndTime }}</div>
+            </div>
+          </div>
+        </div>
+        <h4>责任通知内容</h4>
+        <div class="detail-ct">
+          <div class="row">
+            <div class="col">
+              <div class="label">责任通知文档:</div>
+              <div class="value value-s1">
+                <div class="file-list">
+                  <div class="file-item" v-for="item in formData.attachment" :key="item.fileId">
+                    <span class="file-item--name">{{ item.fileName }}</span>
+                    <div class="file-item--footer">
+                      <!-- <div class="info">
+                        <span>{{ item.fileSize }}</span>
+                      </div> -->
+                      <el-button link type="primary" @click="previewOnline(item.fileUrl, item.fileType)"
+                        >预览</el-button
+                      >
+                      <el-button link type="primary" @click.stop="downloadFile(item.fileUrl, item.fileName)"
+                        >下载</el-button
+                      >
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <h4>材料上传</h4>
+        <div class="detail-ct">
+          <div class="row">
+            <div class="col">
+              <div class="label">上传附件:</div>
+              <div class="value value-s1">
+                <div class="file-list">
+                  <div class="file-item" v-for="item in formData.feedback" :key="item.fileId">
+                    <span class="file-item--name">{{ item.fileName }}</span>
+                    <div class="file-item--footer">
+                      <!-- <div class="info">
+                        <span>{{ item.fileSize }}</span>
+                      </div> -->
+                      <el-button link type="primary" @click="previewOnline(item.fileUrl, item.fileType)"
+                        >预览</el-button
+                      >
+                      <el-button link type="primary" @click.stop="downloadFile(item.fileUrl, item.fileName)"
+                        >下载</el-button
+                      >
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </el-form>
+    </main>
+    <footer class="safety-platform-container__footer">
+      <el-button @click="$router.push({ name: 'responsibilityNoticeManage:admin' })">返回</el-button>
+      <el-button type="primary" @click="handleReviewFailed">审核不通过</el-button>
+      <el-button type="primary" @click="handleReviewApproved" :loading="submiting">审核通过</el-button>
+    </footer>
+  </div>
+  <PreviewOnline ref="previewOnlineRef" />
+  <RefuseReason ref="reviewFeedBackRef" @submit="handleReviewFailedSubmit" />
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, nextTick, reactive, shallowRef } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import '@wangeditor/editor/dist/css/style.css';
+  import { useRouter, useRoute } from 'vue-router';
+  import { downloadFile } from '@/views/disaster/utils';
+
+  import {
+    safetyNoticeAdminQueryDetail,
+    safetyNoticeAdminApprove,
+  } from '@/api/production-safety/responsibility-implementation';
+  import RefuseReason from '../components/RefuseReason.vue';
+
+  import { unformatAttachment, formatAttachmentList } from '@/components/UploadFiles/utils';
+  import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
+
+  const router = useRouter();
+  const route = useRoute();
+  const formRef = ref<any>(null);
+  const submiting = ref(false);
+
+  const formData = reactive<any>({
+    safetyAreaName: '',
+    departmentName: '',
+    createdByName: '',
+    createdAt: '',
+    attachment: [],
+    feedback: [],
+    signsUpload: [],
+    responsibilityName: '',
+    statusName: '',
+    signedQuantity: 0,
+    userGroupName: '',
+    planStartTime: null,
+    planEndTime: null,
+  });
+
+  const rules = reactive({});
+  const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
+  const reviewFeedBackRef = ref<any>(null);
+  const previewOnline = (url: string | undefined, type) => {
+    if (url) {
+      previewOnlineRef.value?.open(url, type);
+    }
+  };
+
+  const handleUploadSuccess = (fileList) => {
+    formData.attachment = fileList;
+    formRef.value.validateField('attachment');
+  };
+
+  const handleReviewFailedSubmit = (formData) => {
+    reviewFeedBackRef.value.submitLoading = true;
+    safetyNoticeAdminApprove({
+      approveType: 0,
+      refuseReason: formData.refuseReason,
+      id: route.query.id,
+    })
+      .then(() => {
+        reviewFeedBackRef.value.dialogHide();
+        ElMessage.success('提交成功');
+      })
+      .finally(() => {
+        reviewFeedBackRef.value.submitLoading = false;
+      });
+  };
+  const handleReviewApproved = () => {
+    submiting.value = true;
+    safetyNoticeAdminApprove({
+      approveType: 1,
+      refuseReason: null,
+      id: route.query.id,
+    })
+      .then(() => {
+        ElMessage.success('审核成功');
+        router.push({
+          name: 'responsibilityNoticeManage:admin',
+        });
+      })
+      .finally(() => {
+        submiting.value = false;
+      });
+  };
+
+  const handleQueryDetail = () => {
+    safetyNoticeAdminQueryDetail({
+      id: route.query.id,
+    }).then((res) => {
+      Object.keys(formData).forEach((name) => {
+        if (name in res) {
+          Object.assign(formData, {
+            [name]: res[name],
+            attachment: unformatAttachment(res.attachment),
+            signsUpload: unformatAttachment(res.signsUpload),
+            feedback: unformatAttachment(res.feedback),
+          });
+        }
+      });
+      console.log(formData);
+    });
+  };
+
+  const handleReviewFailed = () => {
+    reviewFeedBackRef.value.dialogShow();
+  };
+
+  onMounted(() => {
+    handleQueryDetail();
+  });
+</script>
+
+<style lang="scss" scoped>
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+  h4 {
+    margin-bottom: 10px;
+  }
+  .detail-content {
+    display: flex;
+    gap: 30px;
+    margin: 10px 0;
+    font-size: 14px;
+  }
+  .detail-ct {
+    display: flex;
+    flex-direction: column;
+    border: 1px solid rgb(220, 223, 230);
+    font-size: 14px;
+    margin-bottom: 25px;
+    .row {
+      display: flex;
+
+      border-bottom: 1px solid rgb(220, 223, 230);
+    }
+    .row:last-child {
+      border-bottom: none;
+    }
+    .col {
+      display: flex;
+      flex: 1;
+      min-height: 40px;
+      align-items: stretch;
+      .label {
+        display: flex;
+        align-items: center;
+        justify-content: flex-end;
+        width: 160px;
+        background-color: rgb(245, 247, 250);
+        border-right: 1px solid rgb(220, 223, 230);
+      }
+
+      .value {
+        display: flex;
+        flex: 1;
+        padding-left: 20px;
+        align-items: center;
+        border-right: 1px solid rgb(220, 223, 230);
+      }
+      .value-s1 {
+        min-height: 60px;
+      }
+      .value:nth-child(2) {
+        border-right: 0;
+      }
+    }
+    .col:nth-child(2) .label {
+      border-left: 1px solid rgb(220, 223, 230);
+    }
+  }
+</style>

+ 364 - 0
src/views/production-safety/implement-safety-duty/responsibility-notice-manage-dept/feedback.vue

@@ -0,0 +1,364 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title"> {{ formData?.responsibilityName }} </div>
+      <div class="detail-content">
+        <span>类别名称:{{ formData?.departmentName }} </span>
+        <span>创建人:{{ formData?.createdByName }} </span>
+        <span>创建时间:{{ formData?.createdAt }} </span>
+      </div>
+    </header>
+    <el-alert
+      v-if="showAlertBar"
+      title="您提交的反馈材料审核不通过,请尽快查看并修改,再次提交进行审核,谢谢!"
+      type="warning"
+    />
+
+    <main class="safety-platform-container__main">
+      <el-form ref="formRef" label-width="auto" :model="formData" :rules="rules">
+        <h4>基本信息</h4>
+        <div class="detail-ct">
+          <div class="row">
+            <div class="col">
+              <div class="label">责任书名称:</div>
+              <div class="value">{{ formData.responsibilityName }}</div>
+            </div>
+            <div class="col">
+              <div class="label">状态:</div>
+              <div class="value">{{ formData.statusName }}</div>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col">
+              <div class="label">签署人数:</div>
+              <div class="value">{{ formData.signedQuantity }}</div>
+            </div>
+            <div class="col">
+              <div class="label">下发对象:</div>
+              <div class="value">{{ formData.userGroupName }}</div>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col">
+              <div class="label">计划开始日期:</div>
+              <div class="value">{{ formData.planStartTime }}</div>
+            </div>
+            <div class="col">
+              <div class="label">计划结束日期:</div>
+              <div class="value">{{ formData.planEndTime }}</div>
+            </div>
+          </div>
+        </div>
+        <h4>责任书内容</h4>
+        <div class="detail-ct">
+          <div class="row">
+            <div class="col">
+              <div class="label">责任书文档:</div>
+              <div class="value value-s1">
+                <div class="file-list">
+                  <div class="file-item" v-for="item in formData.attachment" :key="item.fileId">
+                    <span class="file-item--name">{{ item.fileName }}</span>
+                    <div class="file-item--footer">
+                      <!-- <div class="info">
+                        <span>{{ item.fileSize }}</span>
+                      </div> -->
+                      <el-button link type="primary" @click="previewOnline(item.fileUrl, item.fileType)"
+                        >预览</el-button
+                      >
+                      <el-button link type="primary" @click.stop="downloadFile(item.fileUrl, item.fileName)"
+                        >下载</el-button
+                      >
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <template v-if="!isSignsUpload">
+          <h4>责任书签署文件</h4>
+          <div class="detail-ct">
+            <div class="row">
+              <div class="col">
+                <div class="label">责任书文档:</div>
+                <div class="value value-s1">
+                  <div class="file-list">
+                    <div class="file-item" v-for="item in formData.signsUpload" :key="item.fileId">
+                      <span class="file-item--name">{{ item.fileName }}</span>
+                      <div class="file-item--footer">
+                        <!-- <div class="info">
+                        <span>{{ item.fileSize }}</span>
+                      </div> -->
+                        <el-button link type="primary" @click="previewOnline(item.fileUrl, item.fileType)"
+                          >预览</el-button
+                        >
+                        <el-button link type="primary" @click.stop="downloadFile(item.fileUrl, item.fileName)"
+                          >下载</el-button
+                        >
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </template>
+        <template v-if="isSignsUpload">
+          <h4>签署材料上传</h4>
+
+          <div class="detail-ct">
+            <div class="row">
+              <div class="col">
+                <div class="label">上传附件:</div>
+                <div class="value value-s1">
+                  <el-form-item prop="signsUpload">
+                    <UploadFiles
+                      label="上传附件"
+                      @uploadSuccess="handleUploadSignsUploadSuccess"
+                      :fileList="formData.signsUpload"
+                    />
+                  </el-form-item>
+                  <!-- <div class="file-list" v-if="formData.signsUpload.length">
+                  <div class="file-item" v-for="item in formData.signsUpload" :key="item.fileId">
+                    <span class="file-item--name">{{ item.fileName }}</span>
+                    <div class="file-item--footer">
+                      <el-button link type="primary" @click="previewOnline(item.fileUrl, item.fileType)"
+                        >预览</el-button
+                      >
+                      <el-button link type="primary" @click.stop="downloadFile(item.fileUrl, item.fileName)"
+                        >下载</el-button
+                      >
+                    </div>
+                  </div>
+                </div> -->
+                </div>
+              </div>
+            </div>
+          </div>
+        </template>
+        <template v-else>
+          <h4>材料上传</h4>
+          <div class="detail-ct">
+            <div class="row">
+              <div class="col">
+                <div class="label">上传附件:</div>
+                <div class="value value-s1">
+                  <el-form-item prop="feedback">
+                    <UploadFiles
+                      label="上传附件"
+                      @uploadSuccess="handleUploadFeedbackUploadSuccess"
+                      :fileList="formData.feedback"
+                    />
+                  </el-form-item>
+                  <!-- <div class="file-list" v-if="formData.signsUpload.length">
+                  <div class="file-item" v-for="item in formData.signsUpload" :key="item.fileId">
+                    <span class="file-item--name">{{ item.fileName }}</span>
+                    <div class="file-item--footer">
+                      <el-button link type="primary" @click="previewOnline(item.fileUrl, item.fileType)"
+                        >预览</el-button
+                      >
+                      <el-button link type="primary" @click.stop="downloadFile(item.fileUrl, item.fileName)"
+                        >下载</el-button
+                      >
+                    </div>
+                  </div>
+                </div> -->
+                </div>
+              </div>
+            </div>
+          </div>
+        </template>
+      </el-form>
+    </main>
+    <footer class="safety-platform-container__footer">
+      <el-button @click="$router.push({ name: 'responsibilityAgreeManageDept' })">返回</el-button>
+      <el-button type="primary" @click="handleSubmit" :loading="submiting">提交</el-button>
+    </footer>
+  </div>
+  <PreviewOnline ref="previewOnlineRef" />
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, computed, reactive } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import '@wangeditor/editor/dist/css/style.css';
+  import { useRouter, useRoute } from 'vue-router';
+  import { downloadFile } from '@/views/disaster/utils';
+
+  import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
+  import {
+    safetyNoticeDeptQueryDetail,
+    safetyNoticeDeptFeedback,
+  } from '@/api/production-safety/responsibility-implementation';
+
+  import { unformatAttachment, formatAttachmentList } from '@/components/UploadFiles/utils';
+  import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
+
+  const router = useRouter();
+  const route = useRoute();
+  const formRef = ref<any>(null);
+  const submiting = ref(false);
+
+  const formData = reactive<any>({
+    departmentName: '',
+    createdByName: '',
+    createdAt: '',
+    attachment: [],
+    feedback: [],
+    signsUpload: [],
+    responsibilityName: '',
+    statusName: '',
+    signedQuantity: 0,
+    userGroupName: '',
+    planStartTime: null,
+    planEndTime: null,
+    rejection: '',
+  });
+
+  const rules = reactive({
+    signsUpload: [
+      {
+        required: true,
+        message: '请上传签署材料',
+      },
+    ],
+  });
+  const showAlertBar = computed(() => {
+    if (!formData.rejection) {
+      return false;
+    }
+    return route.query.status === '3' && formData.rejection != null;
+  });
+  const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
+  const isSignsUpload = computed(() => route.query.status === '2');
+
+  const previewOnline = (url: string | undefined, type) => {
+    if (url) {
+      previewOnlineRef.value?.open(url, type);
+    }
+  };
+  const handleSubmit = () => {
+    let signsUpload: any[] = [];
+    let feedback: any[] = [];
+
+    formRef.value?.validate(async (valid) => {
+      if (valid) {
+        submiting.value = true;
+        if (isSignsUpload.value) {
+          signsUpload = await formatAttachmentList(formData.signsUpload);
+        } else {
+          feedback = await formatAttachmentList(formData.feedback);
+        }
+        const params = {
+          id: route.query.id,
+          updateType: isSignsUpload.value ? 0 : 1,
+          signsUpload: isSignsUpload.value ? JSON.stringify(signsUpload) : undefined,
+          feedback: !isSignsUpload.value ? JSON.stringify(feedback) : undefined,
+        };
+        safetyNoticeDeptFeedback(params)
+          .then(() => {
+            ElMessage.success('责任书签署成功');
+            router.push({
+              name: 'responsibilityAgreeManageDept',
+            });
+          })
+          .finally(() => {
+            submiting.value = false;
+          });
+      }
+    });
+  };
+
+  const handleUploadSignsUploadSuccess = (fileList) => {
+    formData.signsUpload = fileList;
+    formRef.value.validateField('signsUpload');
+  };
+
+  const handleUploadFeedbackUploadSuccess = (fileList) => {
+    formData.feedback = fileList;
+    formRef.value.validateField('feedback');
+  };
+
+  const handleQueryDetail = () => {
+    safetyNoticeDeptQueryDetail({
+      id: route.query.id,
+    }).then((res) => {
+      Object.keys(formData).forEach((name) => {
+        if (name in res) {
+          Object.assign(formData, {
+            [name]: res[name],
+            attachment: unformatAttachment(res.attachment),
+            signsUpload: unformatAttachment(res.signsUpload),
+            feedback: unformatAttachment(res.feedback),
+          });
+        }
+      });
+      console.log(formData);
+    });
+  };
+
+  onMounted(() => {
+    handleQueryDetail();
+  });
+</script>
+
+<style lang="scss" scoped>
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+  h4 {
+    margin-bottom: 10px;
+  }
+  .detail-content {
+    display: flex;
+    gap: 30px;
+    margin: 10px 0;
+    font-size: 14px;
+  }
+  .detail-ct {
+    display: flex;
+    flex-direction: column;
+    border: 1px solid rgb(220, 223, 230);
+    font-size: 14px;
+    margin-bottom: 25px;
+    .row {
+      display: flex;
+
+      border-bottom: 1px solid rgb(220, 223, 230);
+    }
+    .row:last-child {
+      border-bottom: none;
+    }
+    .col {
+      display: flex;
+      flex: 1;
+      min-height: 40px;
+      align-items: stretch;
+      .label {
+        display: flex;
+        align-items: center;
+        justify-content: flex-end;
+        width: 160px;
+        background-color: rgb(245, 247, 250);
+        border-right: 1px solid rgb(220, 223, 230);
+      }
+
+      .value {
+        display: flex;
+        justify-content: space-between;
+        flex: 1;
+        padding: 10px 20px;
+        align-items: center;
+        border-right: 1px solid rgb(220, 223, 230);
+      }
+      .value-s1 {
+        min-height: 60px;
+      }
+      .value:nth-child(2) {
+        border-right: 0;
+      }
+    }
+    .col:nth-child(2) .label {
+      border-left: 1px solid rgb(220, 223, 230);
+    }
+  }
+</style>

+ 241 - 0
src/views/production-safety/implement-safety-duty/responsibility-notice-manage-dept/list.vue

@@ -0,0 +1,241 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title"> 安全责任书管理 (部门侧)</div>
+      <!-- <el-tabs v-model="activeTab">
+        <el-tab-pane label="全部" name="" />
+        <el-tab-pane label="院领导" name="院领导" />
+        <el-tab-pane label="部门" name="部门" />
+        <el-tab-pane label="科室" name="科室" />
+        <el-tab-pane label="员工" name="员工" />
+        <el-tab-pane label="常驻供应商" name="常驻供应商" />
+      </el-tabs> -->
+    </header>
+    <main class="safety-platform-container__main">
+      <div class="search-form">
+        <el-form :inline="true">
+          <el-form-item label="安全责任书通知名称">
+            <el-input
+              v-model="queryParams.queryParam.responsibilityName"
+              placeholder="搜索安全责任书通知名称"
+              style="width: 170px"
+            />
+          </el-form-item>
+          <el-form-item label="状态">
+            <el-select v-model="queryParams.queryParam.status" clearable placeholder="状态" style="width: 170px">
+              <el-option :value="1" label="未下发" />
+              <el-option :value="2" label="待反馈" />
+              <el-option :value="3" label="待审核" />
+              <el-option :value="4" label="已完成" />
+              <el-option :value="5" label="已作废" />
+            </el-select>
+          </el-form-item>
+          <el-form-item v-if="activeTab" label="所属部门">
+            <el-select
+              v-model="queryParams.queryParam.departmentName"
+              clearable
+              placeholder="所属部门"
+              style="width: 170px"
+            >
+              <el-option value="院领导">院领导</el-option>
+              <el-option value="部门">部门</el-option>
+              <el-option value="科室">科室</el-option>
+              <el-option value="员工">员工</el-option>
+              <el-option value="常驻供应商">常驻供应商</el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="计划日期">
+            <el-date-picker
+              v-model="queryParams.queryParam.date"
+              clearable
+              type="daterange"
+              start-placeholder="开始时间"
+              end-placeholder="结束时间"
+              style="width: 230px"
+            />
+          </el-form-item>
+        </el-form>
+
+        <div>
+          <el-button type="primary" @click="queryTableList">查询</el-button>
+          <el-button @click="handleRestParams">重置</el-button>
+        </div>
+      </div>
+
+      <div class="table-content">
+        <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="safetyAreaName" width="130" />
+          <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="planStartTime" width="200" />
+          <el-table-column label="计划完成时间" prop="planEndTime" width="200" />
+          <el-table-column fixed="right" min-width="300" label="操作">
+            <template #default="scope">
+              <el-button
+                type="primary"
+                link
+                @click="
+                  $router.push({
+                    name: 'responsibilityNoticeFeedback',
+                    query: {
+                      id: scope.row.id,
+                    },
+                  })
+                "
+                >反馈</el-button
+              >
+              <el-button type="primary" link @click="handleDownloadLink(scope)">下载</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div class="pagination-container" v-if="tableData.total > 0">
+        <el-pagination
+          background
+          :current-page="queryParams.pageNumber"
+          :page-size="queryParams.pageSize"
+          :total="tableData.total"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </main>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, reactive } from 'vue';
+  import dayjs from 'dayjs';
+  import { ElMessage } from 'element-plus';
+  import { useRouter } from 'vue-router';
+  import { safetyNoticeDeptQueryPage } from '@/api/production-safety/responsibility-implementation';
+  import { omit } from 'lodash-es';
+
+  import { unformatAttachment } from '@/components/UploadFiles/utils';
+  import { downloadFile } from '@/views/disaster/utils';
+  import { useUserInfoHook } from '@/views/disaster/hooks';
+
+  const { id } = useUserInfoHook();
+  const router = useRouter();
+  const activeTab = ref('');
+  const groupList = ref<any[]>([]);
+  const queryParams = reactive<any>({
+    pageNumber: 1,
+    pageSize: 10,
+    queryParam: {
+      responsibilityName: '',
+      status: '',
+      date: '',
+      responsibilityPersonId: id,
+    },
+  });
+
+  const tableData = reactive({
+    data: [],
+    total: 0,
+  });
+
+  const handleSizeChange = (value) => {};
+  const handleCurrentChange = (value) => {
+    queryParams.pageNumber = value;
+    queryTableList();
+  };
+
+  const handleDownloadLink = (scope) => {
+    const attachment = unformatAttachment(scope.row.attachment);
+    attachment?.forEach((item: any) => {
+      downloadFile(item.fileUrl, item.fileName);
+    });
+  };
+
+  const queryTableList = () => {
+    safetyNoticeDeptQueryPage({
+      ...queryParams,
+      queryParam: {
+        ...omit(queryParams.queryParam, 'date'),
+        startTime: queryParams.queryParam.date?.[0]
+          ? dayjs(queryParams.queryParam.date?.[0]).format('YYYY-MM-DD')
+          : undefined,
+        endTime: queryParams.queryParam.date?.[1]
+          ? dayjs(queryParams.queryParam.date?.[1]).format('YYYY-MM-DD')
+          : undefined,
+      },
+    }).then((res) => {
+      tableData.data = res.records;
+      tableData.total = res.totalRow;
+    });
+  };
+  const handleRestParams = () => {
+    Object.assign(queryParams, {
+      pageNumber: 1,
+      pageSize: 10,
+      queryParam: {
+        responsibilityName: '',
+        status: '',
+        date: '',
+        responsibilityPersonId: '',
+      },
+    });
+    queryTableList();
+  };
+
+  onMounted(async () => {
+    queryTableList();
+  });
+</script>
+
+<style lang="scss" scoped>
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+
+  :deep(.el-tabs__header) {
+    margin: 0;
+  }
+  :deep(.el-tabs__item) {
+    font-size: 14px !important;
+  }
+  :deep(.flexContent) {
+    display: flex;
+  }
+  :deep(.breadcrumb .title) {
+    margin-left: 0;
+  }
+
+  :deep(.el-form) {
+    flex: 1;
+    display: flex;
+    row-gap: 15px;
+    flex-wrap: wrap;
+  }
+  :deep(.el-form-item) {
+    margin-bottom: 0;
+  }
+  :deep(main) {
+    display: flex;
+    flex-direction: column;
+  }
+  .search-form {
+    min-width: 800px;
+    display: flex;
+
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+  }
+
+  .button-content {
+    margin-bottom: 20px;
+  }
+  .table-content {
+    flex: 1;
+    overflow: hidden;
+    overflow-y: auto;
+  }
+  .page-content {
+    display: flex;
+    justify-content: flex-end;
+  }
+</style>

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

@@ -0,0 +1 @@
+<template>view</template>