Explorar o código

feat: 责任清单相关

sunqijun hai 3 meses
pai
achega
f5812f9ea5
Modificáronse 17 ficheiros con 2770 adicións e 13 borrados
  1. 115 2
      src/api/production-safety/responsibility-implementation/index.ts
  2. 2 0
      src/router/full-routes.ts
  3. 2 0
      src/router/routers/index.ts
  4. 128 2
      src/router/routers/production-safety.ts
  5. 329 2
      src/views/production-safety/implement-safety-duty/agree-document-review.vue
  6. 1 1
      src/views/production-safety/implement-safety-duty/components/IssueSafetyResponsibility.vue
  7. 74 0
      src/views/production-safety/implement-safety-duty/components/RefuseReason.vue
  8. 260 0
      src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/add.vue
  9. 250 0
      src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/change.vue
  10. 284 0
      src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/edit.vue
  11. 313 0
      src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/list.vue
  12. 1 0
      src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/review.vue
  13. 287 0
      src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/view.vue
  14. 329 0
      src/views/production-safety/implement-safety-duty/responsibility-agree-manage-dept.vue
  15. 1 4
      src/views/production-safety/implement-safety-duty/responsibility-agree-manage.vue
  16. 390 0
      src/views/production-safety/implement-safety-duty/sign-agree-dept.vue
  17. 4 2
      src/views/production-safety/implement-safety-duty/view-recipients.vue

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

@@ -1,6 +1,13 @@
 import { http } from '@/utils/http/axios';
 import type { QueryPageRequest, QueryPageResponse } from '@/types/basic-query';
 
+export function queryUserPageByUsername(params) {
+  return http.request({
+    url: `/admin/user/queryUserPageByUsername`,
+    method: 'post',
+    params
+  });
+}
 export function safetyResponsibilityAdminqueryPage(params) {
   return http.request({
     url: '/safetyResponsibility/admin/queryPage',
@@ -77,8 +84,114 @@ export function safetyResponsibilityAdminDeleteIssuedObject(id) {
 
 export function safetyResponsibilityAdminIssuedSafety(params) {
   return http.request({
-    url: `/safetyResponsibility/admin/issuedSafety`,
+    url: '/safetyResponsibility/admin/issuedSafety',
     method: 'post',
     params
   });
-}
+}
+
+export function safetyResponsibilityDeptQueryDetail(params) {
+  return http.request({
+    url: '/safetyResponsibility/dept/queryDetail',
+    method: 'get',
+    params
+  });
+}
+
+export function safetyResponsibilityDeptSignOrFeedback(params) {
+  return http.request({
+    url: '/safetyResponsibility/dept/signOrFeedback',
+    method: 'put',
+    params
+  });
+}
+
+export function safetyResponsibilityDeptQueryPage(params) {
+  return http.request({
+    url: '/safetyResponsibility/dept/queryPage',
+    method: 'post',
+    params
+  });
+}
+
+export function safetyResponsibilityAdminApprove(params) {
+  return http.request({
+    url: '/safetyResponsibility/admin/approve',
+    method: 'put',
+    params
+  });
+}
+
+// 分页查询区域
+
+export function areaCheckListQueryPage(params) {
+  return http.request({
+    url: '/areaCheckList/queryPage',
+    method: 'post',
+    params
+  });
+
+}
+// 区域详情
+
+export function areaCheckListQueryDetail(params) {
+  return http.request({
+    url: '/areaCheckList/queryDetail',
+    method: 'get',
+    params
+  });
+}
+
+// 正常状态可删除
+
+export function areaCheckListDelete(id) {
+  return http.request({
+    url: `/areaCheckList/delete?id=${id}`,
+    method: 'delete',
+  });
+}
+
+export function areaCheckListSavaArea(params) {
+  return http.request({
+    url: `/areaCheckList/saveArea`,
+    method: 'post',
+    params
+  });
+}
+
+
+export function areaCheckListUpdateArea(params) {
+  return http.request({
+    url: `/areaCheckList/updateArea`,
+    method: 'put',
+    params
+  });
+}
+
+export function areaCheckListChange(params) {
+  return http.request({
+    url: `/areaCheckList/change`,
+    method: 'put',
+    params
+  });
+}
+export function areaCheckListApprove(params) {
+  return http.request({
+    url: `/areaCheckList/approve`,
+    method: 'put',
+    params
+  });
+}
+
+
+export function areaCheckListExportArea(params) {
+  return http.request({
+    url: `/areaCheckList/exportArea`,
+    method: 'post',
+    params,
+    responseType: 'blob',
+  },
+    {
+      isTransformResponse: false,
+    });
+}

+ 2 - 0
src/router/full-routes.ts

@@ -16,6 +16,7 @@ import {
   trafficRoutes,
   securityConfidentialityRoutes,
   campusRoutes,
+  productionSafetyRoutes
 } from './routers';
 
 export type RouteRecordString = Omit<AppRouteRecordRaw, 'component'> & { component?: string };
@@ -48,6 +49,7 @@ export const fullRoutes: AppRouteRecordRaw[] = [
   trafficRoutes,
   securityConfidentialityRoutes,
   campusRoutes,
+  productionSafetyRoutes
 ] as const;
 
 /**

+ 2 - 0
src/router/routers/index.ts

@@ -4,6 +4,7 @@ import emergencyManagementRoute from './emergency';
 import exceptionRouters from './exception';
 import largeScreenRoutes from './large-screen';
 import trafficRoutes from './traffic';
+import productionSafetyRoutes from './production-safety';
 import securityConfidentialityRoutes from './security-confidentiality';
 import campusRoutes from './campus';
 
@@ -16,4 +17,5 @@ export {
   trafficRoutes,
   securityConfidentialityRoutes,
   campusRoutes,
+  productionSafetyRoutes,
 };

+ 128 - 2
src/router/routers/production-safety.ts

@@ -110,8 +110,134 @@ const productionSafetyRoutes = {
           hidden: false,
           noCache: false,
         }
-      }
-      ]
+      },
+      {
+        id: 90006,
+        parentId: 9001,
+        name: 'responsibilityAgreeManageDept',
+        path: 'responsibility-agree-manage-dept',
+        component: '/production-safety/implement-safety-duty/responsibility-agree-manage-dept',
+        meta: {
+          title: '安全责任书管理',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+        }
+
+      },
+      {
+        id: 90007,
+        parentId: 9001,
+        name: 'signAgreeDept',
+        path: 'sign-agree-dept',
+        component: '/production-safety/implement-safety-duty/sign-agree-dept',
+        meta: {
+          title: '责任书签署',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+        }
+
+      },
+      // 责任清单
+      {
+        id: 90008,
+        parentId: 9001,
+        name: 'listResponsibilities:nonPublic',
+        path: 'non-public-list-responsibilities',
+        component: '/production-safety/implement-safety-duty/non-public-list-responsibilities/list',
+        meta: {
+          title: '责任清单',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+
+      {
+        id: 90009,
+        parentId: 90008,
+        name: 'addResponsibilities:nonPublic',
+        path: 'non-public-add-responsibilities',
+        component: '/production-safety/implement-safety-duty/non-public-list-responsibilities/add',
+        meta: {
+          title: '新增非区域责任清单',
+          activeMenu: '/work-safety/responsibility-implementation/non-public-list-responsibilities',
+          // icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+        }
+      },
+      {
+
+        id: 90010,
+        parentId: 90008,
+        name: 'editResponsibilities:nonPublic',
+        path: 'non-public-edit-responsibilities',
+        component: '/production-safety/implement-safety-duty/non-public-list-responsibilities/edit',
+        meta: {
+          title: '编辑非区域责任清单',
+          activeMenu: '/work-safety/responsibility-implementation/non-public-list-responsibilities',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+      {
+
+        id: 90011,
+        parentId: 9001,
+        name: 'changeResponsibilities:nonPublic',
+        path: 'non-public-change-responsibilities',
+        component: '/production-safety/implement-safety-duty/non-public-list-responsibilities/change',
+        meta: {
+          title: '变更非区域责任清单',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+      {
+
+        id: 90012,
+        parentId: 9001,
+        name: 'viewResponsibilities:nonPublic',
+        path: 'non-public-view-responsibilities',
+        component: '/production-safety/implement-safety-duty/non-public-list-responsibilities/view',
+        meta: {
+          title: '查看非区域责任清单',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      },
+      {
+
+        id: 90013,
+        parentId: 9001,
+        name: 'reviewResponsibilities:nonPublic',
+        path: 'non-public-review-responsibilities',
+        component: '/production-safety/implement-safety-duty/non-public-list-responsibilities/review',
+        meta: {
+          title: '审核非区域责任清单',
+          icon: 'OverviewIcon',
+          isRoot: false,
+          hidden: false,
+          noCache: false,
+
+        }
+      }]
     }
   ],
 };

+ 329 - 2
src/views/production-safety/implement-safety-duty/agree-document-review.vue

@@ -1,4 +1,331 @@
 <template>
-  <div>agree document review </div>
+  <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>
+    <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.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>
+        <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: 'responsibilityAgreeManage' })">返回</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></script>
+<script lang="ts" setup>
+  import { onMounted, ref, nextTick, reactive, shallowRef } from 'vue';
+  import dayjs from 'dayjs';
+  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 {
+    safetyResponsibilityDeptQueryDetail,
+    safetyResponsibilityAdminApprove,
+  } 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>({
+    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;
+    safetyResponsibilityAdminApprove({
+      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;
+    safetyResponsibilityAdminApprove({
+      approveType: 1,
+      refuseReason: null,
+      id: route.query.id,
+    })
+      .then(() => {
+        ElMessage.success('审核成功');
+        router.push({
+          name: 'responsibilityAgreeManage',
+        });
+      })
+      .finally(() => {
+        submiting.value = false;
+      });
+  };
+
+  const handleQueryDetail = () => {
+    safetyResponsibilityDeptQueryDetail({
+      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);
+    }
+  }
+  // :deep(.breadcrumb .title) {
+  //   margin-left: 0;
+  // }
+
+  // .main {
+  //   display: flex;
+  //   flex-direction: column;
+  //   padding: 20px;
+  //   flex: 1;
+  //   overflow: hidden;
+  //   background-color: #fff;
+  // }
+  // .button-content {
+  //   margin-bottom: 20px;
+  // }
+
+  // .page-content {
+  //   display: flex;
+  //   justify-content: flex-end;
+  // }
+  // // :deep(.el-form) {
+  // //   flex: 1;
+  // //   overflow: hidden;
+  // //   overflow-y: auto;
+  // // }
+</style>

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

@@ -43,7 +43,7 @@
     groupList: any[];
     currentRowData: { [key: string]: any };
   }>();
-  const emit = defineEmits<{ (e: 'emit-team-name', teamName: string) }>();
+  const emit = defineEmits<{ (e: 'submit', formData: {}) }>();
   const submitLoading = ref(false);
   const showDialog = ref(false);
   const formData = reactive<any>({

+ 74 - 0
src/views/production-safety/implement-safety-duty/components/RefuseReason.vue

@@ -0,0 +1,74 @@
+<template>
+  <el-dialog v-model="showDialog" title="审核不通过" width="700" class="add-team-dialog" @close="clearData">
+    <p style="margin: 15px 0">审核不通过的记录,需要填写驳回原因 </p>
+    <el-form label-width="auto" :model="formData" ref="ruleFormRef" :rules="rules">
+      <el-form-item label="" prop="refuseReason">
+        <el-input
+          v-model="formData.refuseReason"
+          placeholder="请填写驳回审批原因"
+          type="textarea"
+          show-word-limit
+          :rows="7"
+          size="large"
+        />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <div>
+        <el-button type="primary" @click="submitForm" :loading="submitLoading"> 确定 </el-button>
+        <el-button @click="showDialog = false">取消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+  import { ref, reactive } from 'vue';
+  import type { FormInstance, FormRules } from 'element-plus';
+
+  const emit = defineEmits<{ (e: 'submit', formData: {}) }>();
+  const submitLoading = ref(false);
+  const showDialog = ref(false);
+  const formData = reactive<any>({
+    refuseReason: '',
+  });
+  const ruleFormRef = ref<FormInstance>();
+  const rules = ref({
+    refuseReason: [{ required: true, message: '请填写驳回审批原因' }],
+  });
+
+  function dialogShow() {
+    showDialog.value = true;
+  }
+  function dialogHide() {
+    showDialog.value = false;
+  }
+  function clearData() {
+    ruleFormRef.value?.resetFields();
+    Object.assign(formData, {
+      refuseReason: '',
+    });
+  }
+
+  async function submitForm() {
+    await ruleFormRef.value!.validate((valid) => {
+      if (valid) {
+        emit('submit', formData);
+        // showDialog.value = false;
+      }
+    });
+  }
+
+  defineExpose({
+    submitLoading,
+    dialogShow,
+    dialogHide,
+  });
+</script>
+
+<style scoped lang="scss"></style>
+<style>
+  .add-team-dialog {
+    min-height: 0;
+  }
+</style>

+ 260 - 0
src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/add.vue

@@ -0,0 +1,260 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title">
+        <BreadcrumbBack />
+        创建非公共区域责任清单
+      </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <el-form ref="formRef" label-width="auto" :model="formValue" :rules="rules">
+        <el-form-item label="楼号" prop="buildingNo">
+          <el-input v-model="formValue.buildingNo" size="large" placeholder="请输入楼号" style="width: 50%" />
+        </el-form-item>
+        <el-form-item label="楼宇/区域" prop="buildingArea">
+          <el-input v-model="formValue.buildingArea" size="large" placeholder="请输入楼宇/区域" style="width: 50%" />
+        </el-form-item>
+        <el-form-item label="楼层/房号" prop="floorRoomNo">
+          <el-input v-model="formValue.floorRoomNo" size="large" placeholder="请输入楼层/房号" style="width: 50%" />
+        </el-form-item>
+        <el-form-item label="名称/功能" prop="nameFunction">
+          <el-input v-model="formValue.nameFunction" size="large" placeholder="请输入名称/功能" style="width: 50%" />
+        </el-form-item>
+
+        <el-form-item label="安全责任所/中心" prop="safetyResponsibleCenter">
+          <el-select
+            v-model="formValue.safetyResponsibleCenter"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+          >
+            <el-option
+              v-for="item in firstLevelDepts"
+              :key="item.hrIdtOrgId"
+              :label="item.deptName"
+              :value="item.deptName"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全责任所/中心负责人" prop="safetyCenterManager">
+          <el-select
+            v-model="formValue.safetyCenterManager"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+            @change="(val) => syncUserName(val, 'safetyCenterManagerName')"
+          >
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全责任部门" prop="safetyResponsibleDepartment">
+          <el-select
+            v-model="formValue.safetyResponsibleDepartment"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+          >
+            <el-option
+              v-for="item in firstLevelDepts"
+              :key="item.hrIdtOrgId"
+              :label="item.deptName"
+              :value="item.deptName"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全责任部门负责人" prop="safetyDepartmentManager">
+          <el-select
+            v-model="formValue.safetyDepartmentManager"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+            @change="(val) => syncUserName(val, 'safetyDepartmentManagerName')"
+          >
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全具体责任人" prop="safetySpecificPerson">
+          <el-select
+            v-model="formValue.safetySpecificPerson"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+            @change="handleSpecificPersonChange"
+          >
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全具体责任人联系方式" prop="safetyPersonContact">
+          <el-input
+            v-model="formValue.safetyPersonContact"
+            size="large"
+            placeholder="选择责任人后自动填充"
+            style="width: 50%"
+          />
+        </el-form-item>
+      </el-form>
+    </main>
+    <footer class="safety-platform-container__footer">
+      <el-button @click="$router.push({ name: 'listResponsibilities:nonPublic' })">返回</el-button>
+      <el-button type="primary" :loading="submiting" @click="handleSubmit">提交</el-button>
+    </footer>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, reactive, onMounted } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
+  import {
+    queryUserPageByUsername,
+    areaCheckListSavaArea,
+  } from '@/api/production-safety/responsibility-implementation';
+
+  const router = useRouter();
+  const formRef = ref<any>(null);
+  const submiting = ref(false);
+
+  const userOptions = ref<any[]>([]);
+  const firstLevelDepts = ref<any[]>([]);
+
+  const formValue = reactive({
+    buildingNo: '',
+    buildingArea: '',
+    floorRoomNo: '',
+    nameFunction: '',
+    safetyResponsibleCenter: '',
+    safetyCenterManager: null as number | null,
+    safetyCenterManagerName: '',
+    safetyResponsibleDepartment: '',
+    safetyDepartmentManager: null as number | null,
+    safetyDepartmentManagerName: '',
+    safetySpecificPerson: null as number | null,
+    safetySpecificPersonName: '',
+    safetyPersonContact: '',
+  });
+
+  const rules = reactive({
+    buildingNo: [{ required: true, message: '请输入楼号' }],
+    buildingArea: [{ required: true, message: '请输入楼宇/区域' }],
+    floorRoomNo: [{ required: true, message: '请输入楼层/房号' }],
+    nameFunction: [{ required: true, message: '请输入名称/功能' }],
+
+    safetyResponsibleCenter: [{ required: true, message: '请选择安全责任所/中心', trigger: 'change' }],
+    safetyCenterManager: [{ required: true, message: '请选择安全责任所/中心负责人', trigger: 'change' }],
+
+    safetyResponsibleDepartment: [{ required: true, message: '请选择安全责任部门', trigger: 'change' }],
+    safetyDepartmentManager: [{ required: true, message: '请选择安全责任部门负责人', trigger: 'change' }],
+
+    safetySpecificPerson: [{ required: true, message: '请选择安全具体责任人', trigger: 'change' }],
+    safetyPersonContact: [
+      { required: true, message: '请输入安全具体责任人联系方式' },
+      { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码格式' },
+    ],
+  });
+
+  // 获取部门数据
+  const getDeptData = () => {
+    getAllDepartments().then((res) => {
+      firstLevelDepts.value = formatDeptTree(res);
+    });
+  };
+
+  const getUserData = () => {
+    queryUserPageByUsername({ pageNumber: 1, pageSize: 20, queryParam: '' }).then((res: any) => {
+      userOptions.value = (res.records || []).map((u: any) => ({
+        id: u.userId || u.id,
+        name: u.realName || u.username,
+      }));
+    });
+  };
+
+  const syncUserName = (id: number, nameField: string) => {
+    const user = userOptions.value.find((u) => u.id === id);
+    if (user) formValue[nameField] = user.name;
+  };
+
+  const handleSpecificPersonChange = (id: number) => {
+    const user = userOptions.value.find((u) => u.id === id);
+    if (user) {
+      formValue.safetySpecificPersonName = user.name;
+      formValue.safetyPersonContact = user.mobile;
+    }
+  };
+
+  onMounted(() => {
+    getDeptData();
+    getUserData();
+  });
+
+  const handleSubmit = () => {
+    formRef.value?.validate((valid: boolean) => {
+      if (valid) {
+        submiting.value = true;
+        areaCheckListSavaArea({
+          ...formValue,
+          type: 2,
+        })
+          .then(() => {
+            ElMessage.success('创建成功!');
+            router.push({ name: 'listResponsibilities:nonPublic' });
+          })
+          .finally(() => {
+            submiting.value = false;
+          });
+      }
+    });
+  };
+</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;
+
+    // :deep(.w-e-text-container) {
+    //   min-height: 400px;
+    //   overflow-y: auto;
+    // }
+  }
+  // :deep(.breadcrumb .title) {
+  //   margin-left: 0;
+  // }
+
+  // .main {
+  //   display: flex;
+  //   flex-direction: column;
+  //   padding: 20px;
+  //   flex: 1;
+  //   overflow: hidden;
+  //   background-color: #fff;
+  // }
+  // .button-content {
+  //   margin-bottom: 20px;
+  // }
+
+  // .page-content {
+  //   display: flex;
+  //   justify-content: flex-end;
+  // }
+  // // :deep(.el-form) {
+  // //   flex: 1;
+  // //   overflow: hidden;
+  // //   overflow-y: auto;
+  // // }
+</style>

+ 250 - 0
src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/change.vue

@@ -0,0 +1,250 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title">
+        <BreadcrumbBack />
+        变更非公共区域责任清单
+      </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <el-form ref="formRef" label-width="auto" :model="formValue" :rules="rules">
+        <el-form-item label="楼号" prop="buildingNo">
+          <el-input v-model="formValue.buildingNo" disabled size="large" style="width: 50%" />
+        </el-form-item>
+        <el-form-item label="楼宇/区域" prop="buildingArea">
+          <el-input v-model="formValue.buildingArea" disabled size="large" style="width: 50%" />
+        </el-form-item>
+        <el-form-item label="楼层/房号" prop="floorRoomNo">
+          <el-input v-model="formValue.floorRoomNo" disabled size="large" style="width: 50%" />
+        </el-form-item>
+        <el-form-item label="名称/功能" prop="nameFunction">
+          <el-input v-model="formValue.nameFunction" disabled size="large" style="width: 50%" />
+        </el-form-item>
+
+        <el-form-item label="安全责任所/中心" prop="safetyResponsibleCenter">
+          <el-select v-model="formValue.safetyResponsibleCenter" disabled size="large" style="width: 50%">
+            <el-option
+              v-for="item in firstLevelDepts"
+              :key="item.hrIdtOrgId"
+              :label="item.deptName"
+              :value="item.deptName"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全责任所/中心负责人" prop="safetyCenterManager">
+          <el-select v-model="formValue.safetyCenterManager" disabled size="large" style="width: 50%">
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <h5 style="margin: 30px 0">信息变更</h5>
+
+        <el-form-item label="变更后的安全责任部门" prop="safetyResponsibleDepartmentTodo">
+          <el-select
+            v-model="formValue.safetyResponsibleDepartmentTodo"
+            placeholder="请选择新的责任部门"
+            size="large"
+            style="width: 50%"
+            filterable
+            clearable
+          >
+            <el-option
+              v-for="item in firstLevelDepts"
+              :key="item.hrIdtOrgId"
+              :label="item.deptName"
+              :value="item.deptName"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="变更后的安全具体责任人" prop="safetySpecificPersonTodo">
+          <el-select
+            v-model="formValue.safetySpecificPersonTodo"
+            placeholder="请选择新的具体责任人"
+            size="large"
+            style="width: 50%"
+            filterable
+            clearable
+            @change="(val) => syncUserName(val, 'safetySpecificPersonTodo')"
+          >
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="变更原因" prop="changeReason">
+          <el-input
+            v-model="formValue.changeReason"
+            type="textarea"
+            :rows="7"
+            placeholder="请详细描述变更原因"
+            style="width: 70%"
+            maxlength="200"
+            show-word-limit
+          />
+        </el-form-item>
+      </el-form>
+    </main>
+
+    <footer class="safety-platform-container__footer">
+      <el-button @click="$router.push({ name: 'listResponsibilities:nonPublic' })">返回</el-button>
+      <el-button type="primary" :loading="submiting" @click="handleSubmit">提交变更</el-button>
+    </footer>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, reactive, nextTick, onMounted } from 'vue';
+  import { useRouter, useRoute } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
+  import {
+    queryUserPageByUsername,
+    areaCheckListQueryDetail,
+    areaCheckListChange,
+  } from '@/api/production-safety/responsibility-implementation';
+
+  const router = useRouter();
+  const route = useRoute();
+  const formRef = ref<any>(null);
+  const submiting = ref(false);
+
+  const userOptions = ref<any[]>([]);
+  const firstLevelDepts = ref<any[]>([]);
+
+  const formValue = reactive({
+    buildingNo: '',
+    buildingArea: '',
+    floorRoomNo: '',
+    nameFunction: '',
+    safetyResponsibleCenter: '',
+    safetyCenterManager: null as number | null,
+    safetyResponsibleDepartment: '',
+    safetyDepartmentManager: null as number | null,
+    safetySpecificPerson: null as number | null,
+    safetyPersonContact: '',
+
+    safetyResponsibleDepartmentTodo: '',
+    safetySpecificPersonTodo: null as number | null,
+    safetySpecificPersonNameTodo: '',
+    changeReason: '',
+  });
+
+  const rules = reactive({
+    safetyResponsibleDepartmentTodo: [{ required: true, message: '请选择变更后的责任部门', trigger: 'blur' }],
+    safetySpecificPersonTodo: [{ required: true, message: '请选择变更后的具体责任人', trigger: 'change' }],
+    changeReason: [{ required: true, message: '请输入变更原因', trigger: 'blur' }],
+  });
+
+  const getDeptData = async () => {
+    const res = await getAllDepartments();
+    firstLevelDepts.value = formatDeptTree(res);
+  };
+
+  // 获取用户列表
+  const getUserData = async () => {
+    const res: any = await queryUserPageByUsername({ pageNumber: 1, pageSize: 30, queryParam: '' });
+    userOptions.value = (res.records || []).map((u: any) => ({
+      id: u.userId || u.id,
+      name: u.realName || u.username,
+    }));
+  };
+
+  const syncUserName = (id: number, field: string) => {
+    const user = userOptions.value.find((u) => u.id === id);
+    if (user && field === 'safetySpecificPersonTodo') {
+      formValue.safetySpecificPersonNameTodo = user.name;
+    }
+  };
+
+  const handlAreaCheckListQueryDetail = async () => {
+    const id = route.query.id;
+    if (!id) return;
+    try {
+      const res: any = await areaCheckListQueryDetail({ id });
+      if (res) {
+        Object.assign(formValue, res);
+
+        if (formValue.safetyCenterManager) formValue.safetyCenterManager = Number(formValue.safetyCenterManager);
+        if (formValue.safetySpecificPerson) formValue.safetySpecificPerson = Number(formValue.safetySpecificPerson);
+      }
+    } catch (err) {
+      ElMessage.error('详情加载失败');
+    }
+  };
+
+  onMounted(async () => {
+    submiting.value = true;
+    await getDeptData();
+    await getUserData();
+    await handlAreaCheckListQueryDetail();
+    submiting.value = false;
+  });
+
+  const handleSubmit = () => {
+    formRef.value?.validate((valid: boolean) => {
+      if (valid) {
+        submiting.value = true;
+
+        const submitData = {
+          id: Number(route.query.id),
+          safetyResponsibleDepartmentTodo: formValue.safetyResponsibleDepartmentTodo,
+          safetySpecificPersonTodo: formValue.safetySpecificPersonTodo,
+          safetySpecificPersonNameTodo: formValue.safetySpecificPersonNameTodo,
+          changeReason: formValue.changeReason,
+        };
+
+        areaCheckListChange(submitData)
+          .then(() => {
+            ElMessage.success('变更申请提交成功!');
+            router.push({ name: 'listResponsibilities:nonPublic' });
+          })
+          .finally(() => {
+            submiting.value = false;
+          });
+      }
+    });
+  };
+</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;
+
+    // :deep(.w-e-text-container) {
+    //   min-height: 400px;
+    //   overflow-y: auto;
+    // }
+  }
+  // :deep(.breadcrumb .title) {
+  //   margin-left: 0;
+  // }
+
+  // .main {
+  //   display: flex;
+  //   flex-direction: column;
+  //   padding: 20px;
+  //   flex: 1;
+  //   overflow: hidden;
+  //   background-color: #fff;
+  // }
+  // .button-content {
+  //   margin-bottom: 20px;
+  // }
+
+  // .page-content {
+  //   display: flex;
+  //   justify-content: flex-end;
+  // }
+  // // :deep(.el-form) {
+  // //   flex: 1;
+  // //   overflow: hidden;
+  // //   overflow-y: auto;
+  // // }
+</style>

+ 284 - 0
src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/edit.vue

@@ -0,0 +1,284 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title">
+        <BreadcrumbBack />
+        编辑非公共区域责任清单
+      </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <el-form ref="formRef" label-width="auto" :model="formValue" :rules="rules">
+        <el-form-item label="楼号" prop="buildingNo">
+          <el-input v-model="formValue.buildingNo" size="large" placeholder="请输入楼号" style="width: 50%" />
+        </el-form-item>
+        <el-form-item label="楼宇/区域" prop="buildingArea">
+          <el-input v-model="formValue.buildingArea" size="large" placeholder="请输入楼宇/区域" style="width: 50%" />
+        </el-form-item>
+        <el-form-item label="楼层/房号" prop="floorRoomNo">
+          <el-input v-model="formValue.floorRoomNo" size="large" placeholder="请输入楼层/房号" style="width: 50%" />
+        </el-form-item>
+        <el-form-item label="名称/功能" prop="nameFunction">
+          <el-input v-model="formValue.nameFunction" size="large" placeholder="请输入名称/功能" style="width: 50%" />
+        </el-form-item>
+
+        <el-form-item label="安全责任所/中心" prop="safetyResponsibleCenter">
+          <el-select
+            v-model="formValue.safetyResponsibleCenter"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+          >
+            <el-option
+              v-for="item in firstLevelDepts"
+              :key="item.hrIdtOrgId"
+              :label="item.deptName"
+              :value="item.deptName"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全责任所/中心负责人" prop="safetyCenterManager">
+          <el-select
+            v-model="formValue.safetyCenterManager"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+            @change="(val) => syncUserName(val, 'safetyCenterManagerName')"
+          >
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全责任部门" prop="safetyResponsibleDepartment">
+          <el-select
+            v-model="formValue.safetyResponsibleDepartment"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+          >
+            <el-option
+              v-for="item in firstLevelDepts"
+              :key="item.hrIdtOrgId"
+              :label="item.deptName"
+              :value="item.deptName"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全责任部门负责人" prop="safetyDepartmentManager">
+          <el-select
+            v-model="formValue.safetyDepartmentManager"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+            @change="(val) => syncUserName(val, 'safetyDepartmentManagerName')"
+          >
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全具体责任人" prop="safetySpecificPerson">
+          <el-select
+            v-model="formValue.safetySpecificPerson"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+            @change="handleSpecificPersonChange"
+          >
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全具体责任人联系方式" prop="safetyPersonContact">
+          <el-input
+            v-model="formValue.safetyPersonContact"
+            size="large"
+            placeholder="选择责任人后自动填充"
+            style="width: 50%"
+          />
+        </el-form-item>
+      </el-form>
+    </main>
+    <footer class="safety-platform-container__footer">
+      <el-button @click="$router.push({ name: 'listResponsibilities:nonPublic' })">返回</el-button>
+      <el-button type="primary" :loading="submiting" @click="handleSubmit">提交</el-button>
+    </footer>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, reactive, onMounted } from 'vue';
+  import { useRouter, useRoute } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
+  import {
+    queryUserPageByUsername,
+    areaCheckListSavaArea,
+    areaCheckListQueryDetail,
+    areaCheckListUpdateArea,
+  } from '@/api/production-safety/responsibility-implementation';
+
+  const router = useRouter();
+  const route = useRoute();
+  const formRef = ref<any>(null);
+  const submiting = ref(false);
+
+  const userOptions = ref<any[]>([]);
+  const firstLevelDepts = ref<any[]>([]);
+
+  const formValue = reactive({
+    buildingNo: '',
+    buildingArea: '',
+    floorRoomNo: '',
+    nameFunction: '',
+    safetyResponsibleCenter: '',
+    safetyCenterManager: null as number | null,
+    safetyCenterManagerName: '',
+    safetyResponsibleDepartment: '',
+    safetyDepartmentManager: null as number | null,
+    safetyDepartmentManagerName: '',
+    safetySpecificPerson: null as number | null,
+    safetySpecificPersonName: '',
+    safetyPersonContact: '',
+  });
+
+  const rules = reactive({
+    buildingNo: [{ required: true, message: '请输入楼号' }],
+    buildingArea: [{ required: true, message: '请输入楼宇/区域' }],
+    floorRoomNo: [{ required: true, message: '请输入楼层/房号' }],
+    nameFunction: [{ required: true, message: '请输入名称/功能' }],
+
+    safetyResponsibleCenter: [{ required: true, message: '请选择安全责任所/中心', trigger: 'change' }],
+    safetyCenterManager: [{ required: true, message: '请选择安全责任所/中心负责人', trigger: 'change' }],
+
+    safetyResponsibleDepartment: [{ required: true, message: '请选择安全责任部门', trigger: 'change' }],
+    safetyDepartmentManager: [{ required: true, message: '请选择安全责任部门负责人', trigger: 'change' }],
+
+    safetySpecificPerson: [{ required: true, message: '请选择安全具体责任人', trigger: 'change' }],
+    safetyPersonContact: [
+      { required: true, message: '请输入安全具体责任人联系方式' },
+      { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码格式' },
+    ],
+  });
+
+  // 获取部门数据
+  const getDeptData = () => {
+    getAllDepartments().then((res) => {
+      firstLevelDepts.value = formatDeptTree(res);
+    });
+  };
+
+  const getUserData = () => {
+    queryUserPageByUsername({ pageNumber: 1, pageSize: 20, queryParam: '' }).then((res: any) => {
+      userOptions.value = (res.records || []).map((u: any) => ({
+        id: u.userId || u.id,
+        name: u.realName || u.username,
+      }));
+    });
+  };
+
+  const syncUserName = (id: number, nameField: string) => {
+    const user = userOptions.value.find((u) => u.id === id);
+    if (user) formValue[nameField] = user.name;
+  };
+
+  const handleSpecificPersonChange = (id: number) => {
+    const user = userOptions.value.find((u) => u.id === id);
+    if (user) {
+      formValue.safetySpecificPersonName = user.name;
+      formValue.safetyPersonContact = user.mobile;
+    }
+  };
+
+  onMounted(() => {
+    getDeptData();
+    getUserData();
+    handlAreaCheckListQueryDetail();
+  });
+
+  const handleSubmit = () => {
+    formRef.value?.validate((valid: boolean) => {
+      if (valid) {
+        submiting.value = true;
+        areaCheckListSavaArea({
+          ...formValue,
+          type: 2,
+        })
+          .then(() => {
+            ElMessage.success('编辑成功!');
+            router.push({ name: 'listResponsibilities:nonPublic' });
+          })
+          .finally(() => {
+            submiting.value = false;
+          });
+      }
+    });
+  };
+
+  const handlAreaCheckListQueryDetail = () => {
+    const id = route.query.id;
+    if (!id) return;
+
+    areaCheckListQueryDetail({ id })
+      .then((res: any) => {
+        if (res) {
+          Object.assign(formValue, res);
+          if (formValue.safetyCenterManager) formValue.safetyCenterManager = Number(formValue.safetyCenterManager);
+          if (formValue.safetyDepartmentManager)
+            formValue.safetyDepartmentManager = Number(formValue.safetyDepartmentManager);
+          if (formValue.safetySpecificPerson) formValue.safetySpecificPerson = Number(formValue.safetySpecificPerson);
+        }
+      })
+      .catch((err) => {
+        ElMessage.error('获取详情失败');
+        console.error(err);
+      });
+  };
+</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;
+
+    // :deep(.w-e-text-container) {
+    //   min-height: 400px;
+    //   overflow-y: auto;
+    // }
+  }
+  // :deep(.breadcrumb .title) {
+  //   margin-left: 0;
+  // }
+
+  // .main {
+  //   display: flex;
+  //   flex-direction: column;
+  //   padding: 20px;
+  //   flex: 1;
+  //   overflow: hidden;
+  //   background-color: #fff;
+  // }
+  // .button-content {
+  //   margin-bottom: 20px;
+  // }
+
+  // .page-content {
+  //   display: flex;
+  //   justify-content: flex-end;
+  // }
+  // // :deep(.el-form) {
+  // //   flex: 1;
+  // //   overflow: hidden;
+  // //   overflow-y: auto;
+  // // }
+</style>

+ 313 - 0
src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/list.vue

@@ -0,0 +1,313 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+        <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.nameFunction"
+              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-select>
+          </el-form-item>
+        </el-form>
+
+        <div>
+          <el-button @click="batchImportVisible = true">导入</el-button>
+          <el-button type="primary" @click="handleDownload">导出</el-button>
+          <el-button
+            type="primary"
+            @click="
+              $router.push({
+                name: 'addResponsibilities:nonPublic',
+              })
+            "
+            >添加
+          </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 type="index" label="序号" width="80" />
+          <el-table-column label="楼号" prop="buildingCode" width="80" />
+          <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-column fixed="right" min-width="240" label="操作">
+            <template #default="scope">
+              <div v-if="scope.row.status === 1">
+                <el-button
+                  type="primary"
+                  link
+                  @click="
+                    $router.push({
+                      name: 'editResponsibilities:nonPublic',
+                      query: {
+                        id: scope.row.id,
+                      },
+                    })
+                  "
+                  >编辑
+                </el-button>
+                <el-popconfirm title="确定要删除吗?" @confirm="handleConfirmDeleteRow(scope)">
+                  <template #reference>
+                    <el-button type="primary" link>删除</el-button>
+                  </template>
+                </el-popconfirm>
+
+                <el-button
+                  type="primary"
+                  link
+                  @click="
+                    $router.push({
+                      name: 'viewResponsibilities:nonPublic',
+                      query: {
+                        id: scope.row.id,
+                      },
+                    })
+                  "
+                  >查看</el-button
+                >
+                <el-button
+                  type="primary"
+                  link
+                  @click="
+                    $router.push({
+                      name: 'changeResponsibilities:nonPublic',
+                      query: {
+                        id: scope.row.id,
+                      },
+                    })
+                  "
+                  >变更</el-button
+                >
+              </div>
+              <div v-if="scope.row.canChange === true">
+                <el-button type="primary" link @click="handleAreaCheckListApprove(scope, 1)">确认</el-button>
+                <el-button type="primary" link @click="handleAreaCheckListApprove(scope, 0)">拒绝</el-button>
+              </div>
+              <div v-if="scope.row.status === 3">
+                <el-button type="primary" link>变更</el-button>
+                <el-button type="primary" link>查看</el-button>
+              </div>
+            </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>
+  <BatchImport
+    :visible="batchImportVisible"
+    :importApiUrl="importApiUrl"
+    :templateUrl="templateUrl"
+    :templateName="'责任清单-批量导入模版'"
+    @close="() => (batchImportVisible = false)"
+    @update="handleUpdate"
+  />
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, reactive } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import { useRouter } from 'vue-router';
+  import {
+    areaCheckListQueryPage,
+    areaCheckListApprove,
+    areaCheckListDelete,
+    areaCheckListExportArea,
+  } from '@/api/production-safety/responsibility-implementation';
+  import urlJoin from 'url-join';
+  import { BatchImport } from '@/components/batch-import';
+
+  import { unformatAttachment } from '@/components/UploadFiles/utils';
+  import { downloadFile } from '@/views/disaster/utils';
+  import { useGlobSetting } from '@/hooks/setting';
+
+  const router = useRouter();
+  const activeTab = ref(2);
+  const queryParams = reactive<any>({
+    pageNumber: 1,
+    pageSize: 10,
+    queryParam: {
+      type: 2,
+      nameFunction: '',
+      status: '',
+    },
+  });
+
+  // 批量导入
+  const batchImportVisible = ref(false);
+  const { urlPrefix } = useGlobSetting();
+  const importApiUrl = ref(urlJoin(urlPrefix, '/areaCheckList/importArea?type=2'));
+  const templateUrl = ref('');
+
+  const handleUpdate = () => {
+    batchImportVisible.value = false;
+    queryTableList();
+  };
+
+  const tableData = reactive({
+    data: [],
+    total: 0,
+  });
+  const handleTabChange = (tab) => {
+    console.log(typeof tab);
+    if (tab === 1) {
+      //todo
+      router.push({
+        name: 'listResponsibilities:public',
+      });
+    } else if (tab === 2) {
+      router.push({
+        name: 'listResponsibilities:nonPublic',
+      });
+    }
+  };
+
+  async function handleDownload() {
+    // getQuery();
+    try {
+      const res = await areaCheckListExportArea(queryParams.queryParam);
+      if (res.size === 0) return;
+      const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+      const url = window.URL.createObjectURL(blob);
+      downloadFile(url, '责任清单.xlsx');
+    } catch (e) {
+      ElMessage.error('下载失败');
+      console.log(e);
+    }
+  }
+
+  const handleAreaCheckListApprove = (scope, approveType) => {
+    areaCheckListApprove({
+      id: scope.row.id,
+      approveType,
+      refuseReason: null,
+    }).then(() => {
+      ElMessage.success('操作成功!');
+      queryTableList();
+    });
+  };
+
+  const handleSizeChange = (value) => {};
+  const handleCurrentChange = (value) => {
+    queryParams.pageNumber = value;
+    queryTableList();
+  };
+
+  const handleConfirmDeleteRow = (scope) => {
+    areaCheckListDelete(scope.row.id).then(() => {
+      ElMessage.success('删除成功!');
+      queryTableList();
+    });
+  };
+  const queryTableList = () => {
+    areaCheckListQueryPage(queryParams).then((res) => {
+      tableData.data = res.records;
+      tableData.total = res.totalRow;
+    });
+  };
+  const handleRestParams = () => {
+    Object.assign(queryParams, {
+      pageNumber: 1,
+      pageSize: 10,
+      queryParam: {
+        ...queryParams.queryParam,
+        status: '',
+        nameFunction: '',
+      },
+    });
+    queryTableList();
+  };
+
+  onMounted(() => {
+    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/non-public-list-responsibilities/review.vue

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

+ 287 - 0
src/views/production-safety/implement-safety-duty/non-public-list-responsibilities/view.vue

@@ -0,0 +1,287 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title">
+        <BreadcrumbBack />
+        查看非公共区域责任清单
+      </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <el-form ref="formRef" label-width="auto" :model="formValue" :rules="rules">
+        <el-form-item label="楼号" prop="buildingNo">
+          <el-input disabled v-model="formValue.buildingNo" size="large" placeholder="请输入楼号" style="width: 50%" />
+        </el-form-item>
+        <el-form-item label="楼宇/区域" prop="buildingArea">
+          <el-input
+            disabled
+            v-model="formValue.buildingArea"
+            size="large"
+            placeholder="请输入楼宇/区域"
+            style="width: 50%"
+          />
+        </el-form-item>
+        <el-form-item label="楼层/房号" prop="floorRoomNo">
+          <el-input
+            disabled
+            v-model="formValue.floorRoomNo"
+            size="large"
+            placeholder="请输入楼层/房号"
+            style="width: 50%"
+          />
+        </el-form-item>
+        <el-form-item label="名称/功能" prop="nameFunction">
+          <el-input
+            disabled
+            v-model="formValue.nameFunction"
+            size="large"
+            placeholder="请输入名称/功能"
+            style="width: 50%"
+          />
+        </el-form-item>
+
+        <el-form-item label="安全责任所/中心" prop="safetyResponsibleCenter">
+          <el-select
+            disabled
+            v-model="formValue.safetyResponsibleCenter"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+          >
+            <el-option
+              v-for="item in firstLevelDepts"
+              :key="item.hrIdtOrgId"
+              :label="item.deptName"
+              :value="item.deptName"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全责任所/中心负责人" prop="safetyCenterManager">
+          <el-select
+            disabled
+            v-model="formValue.safetyCenterManager"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+            @change="(val) => syncUserName(val, 'safetyCenterManagerName')"
+          >
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全责任部门" prop="safetyResponsibleDepartment">
+          <el-select
+            disabled
+            v-model="formValue.safetyResponsibleDepartment"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+          >
+            <el-option
+              v-for="item in firstLevelDepts"
+              :key="item.hrIdtOrgId"
+              :label="item.deptName"
+              :value="item.deptName"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全责任部门负责人" prop="safetyDepartmentManager">
+          <el-select
+            disabled
+            v-model="formValue.safetyDepartmentManager"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+            @change="(val) => syncUserName(val, 'safetyDepartmentManagerName')"
+          >
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全具体责任人" prop="safetySpecificPerson">
+          <el-select
+            disabled
+            v-model="formValue.safetySpecificPerson"
+            placeholder="请选择"
+            size="large"
+            style="width: 50%"
+            filterable
+            @change="handleSpecificPersonChange"
+          >
+            <el-option v-for="item in userOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="安全具体责任人联系方式" prop="safetyPersonContact">
+          <el-input
+            disabled
+            v-model="formValue.safetyPersonContact"
+            size="large"
+            placeholder="选择责任人后自动填充"
+            style="width: 50%"
+          />
+        </el-form-item>
+      </el-form>
+    </main>
+    <footer class="safety-platform-container__footer">
+      <el-button type="primary" @click="$router.push({ name: 'listResponsibilities:nonPublic' })">返回</el-button>
+    </footer>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, reactive, onMounted } from 'vue';
+  import { useRouter, useRoute } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
+  import {
+    queryUserPageByUsername,
+    areaCheckListSavaArea,
+    areaCheckListQueryDetail,
+  } from '@/api/production-safety/responsibility-implementation';
+
+  const router = useRouter();
+  const route = useRoute();
+  const formRef = ref<any>(null);
+  const submiting = ref(false);
+
+  const userOptions = ref<any[]>([]);
+  const firstLevelDepts = ref<any[]>([]);
+
+  const formValue = reactive({
+    buildingNo: '',
+    buildingArea: '',
+    floorRoomNo: '',
+    nameFunction: '',
+    safetyResponsibleCenter: '',
+    safetyCenterManager: null as number | null,
+    safetyCenterManagerName: '',
+    safetyResponsibleDepartment: '',
+    safetyDepartmentManager: null as number | null,
+    safetyDepartmentManagerName: '',
+    safetySpecificPerson: null as number | null,
+    safetySpecificPersonName: '',
+    safetyPersonContact: '',
+  });
+
+  const rules = reactive({
+    buildingNo: [{ required: true, message: '请输入楼号' }],
+    buildingArea: [{ required: true, message: '请输入楼宇/区域' }],
+    floorRoomNo: [{ required: true, message: '请输入楼层/房号' }],
+    nameFunction: [{ required: true, message: '请输入名称/功能' }],
+
+    safetyResponsibleCenter: [{ required: true, message: '请选择安全责任所/中心', trigger: 'change' }],
+    safetyCenterManager: [{ required: true, message: '请选择安全责任所/中心负责人', trigger: 'change' }],
+
+    safetyResponsibleDepartment: [{ required: true, message: '请选择安全责任部门', trigger: 'change' }],
+    safetyDepartmentManager: [{ required: true, message: '请选择安全责任部门负责人', trigger: 'change' }],
+
+    safetySpecificPerson: [{ required: true, message: '请选择安全具体责任人', trigger: 'change' }],
+    safetyPersonContact: [
+      { required: true, message: '请输入安全具体责任人联系方式' },
+      { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码格式' },
+    ],
+  });
+
+  // 获取部门数据
+  const getDeptData = () => {
+    getAllDepartments().then((res) => {
+      firstLevelDepts.value = formatDeptTree(res);
+    });
+  };
+
+  const getUserData = () => {
+    queryUserPageByUsername({ pageNumber: 1, pageSize: 20, queryParam: '' }).then((res: any) => {
+      userOptions.value = (res.records || []).map((u: any) => ({
+        id: u.userId || u.id,
+        name: u.realName || u.username,
+      }));
+    });
+  };
+
+  const syncUserName = (id: number, nameField: string) => {
+    const user = userOptions.value.find((u) => u.id === id);
+    if (user) formValue[nameField] = user.name;
+  };
+
+  const handleSpecificPersonChange = (id: number) => {
+    const user = userOptions.value.find((u) => u.id === id);
+    if (user) {
+      formValue.safetySpecificPersonName = user.name;
+      formValue.safetyPersonContact = user.mobile;
+    }
+  };
+
+  onMounted(() => {
+    getDeptData();
+    getUserData();
+    handlAreaCheckListQueryDetail();
+  });
+
+  const handlAreaCheckListQueryDetail = () => {
+    const id = route.query.id;
+    if (!id) return;
+
+    areaCheckListQueryDetail({ id })
+      .then((res: any) => {
+        if (res) {
+          Object.assign(formValue, res);
+          if (formValue.safetyCenterManager) formValue.safetyCenterManager = Number(formValue.safetyCenterManager);
+          if (formValue.safetyDepartmentManager)
+            formValue.safetyDepartmentManager = Number(formValue.safetyDepartmentManager);
+          if (formValue.safetySpecificPerson) formValue.safetySpecificPerson = Number(formValue.safetySpecificPerson);
+        }
+      })
+      .catch((err) => {
+        ElMessage.error('获取详情失败');
+        console.error(err);
+      });
+  };
+</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;
+
+    // :deep(.w-e-text-container) {
+    //   min-height: 400px;
+    //   overflow-y: auto;
+    // }
+  }
+  // :deep(.breadcrumb .title) {
+  //   margin-left: 0;
+  // }
+
+  // .main {
+  //   display: flex;
+  //   flex-direction: column;
+  //   padding: 20px;
+  //   flex: 1;
+  //   overflow: hidden;
+  //   background-color: #fff;
+  // }
+  // .button-content {
+  //   margin-bottom: 20px;
+  // }
+
+  // .page-content {
+  //   display: flex;
+  //   justify-content: flex-end;
+  // }
+  // // :deep(.el-form) {
+  // //   flex: 1;
+  // //   overflow: hidden;
+  // //   overflow-y: auto;
+  // // }
+</style>

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

@@ -0,0 +1,329 @@
+<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-option :value="6" 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="departmentName" 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="userGroupName" width="150" />
+          <el-table-column label="计划完成时间" prop="planEndTime" width="150" />
+          <el-table-column fixed="right" min-width="300" label="操作">
+            <template #default="scope">
+              <div style="display: flex">
+                <el-button
+                  v-if="scope.row.status === 2"
+                  type="primary"
+                  link
+                  @click="
+                    $router.push({
+                      name: 'signAgreeDept',
+                      query: {
+                        id: scope.row.id,
+                        status: scope.row.status,
+                      },
+                    })
+                  "
+                  >签署</el-button
+                >
+                <el-dropdown trigger="click">
+                  <el-button type="primary" link>反馈</el-button>
+                  <template #dropdown>
+                    <el-dropdown-menu>
+                      <el-dropdown-item
+                        @click="
+                          $router.push({
+                            name: 'signAgreeDept',
+                            query: {
+                              id: scope.row.id,
+                              status: scope.row.status,
+                            },
+                          })
+                        "
+                        >待反馈材料</el-dropdown-item
+                      >
+                      <el-dropdown-item
+                        @click="
+                          $router.push({
+                            name: 'signAgreeDept',
+                            query: {
+                              id: scope.row.id,
+                              status: scope.row.status,
+                            },
+                          })
+                        "
+                        >审核不通过</el-dropdown-item
+                      >
+                    </el-dropdown-menu>
+                  </template>
+                </el-dropdown>
+                <el-button type="primary" link @click="handleDownloadLink(scope)">下载</el-button>
+              </div>
+            </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>
+  <IssueSafetyResponsibility
+    :groupList="groupList"
+    :currentRowData="currentRowData"
+    ref="issueSafetyResponsibilityRef"
+    @submit="handleSubmit"
+  />
+</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 {
+    safetyResponsibilityDeptQueryPage,
+    safetyResponsibilityAdminDelete,
+    safetyResponsibilityAdminIssuedSafety,
+  } 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';
+  import IssueSafetyResponsibility from './components/IssueSafetyResponsibility.vue';
+  const router = useRouter();
+  const issueSafetyResponsibilityRef = ref<any>(null);
+  const currentRowData = ref<any>(null);
+  const activeTab = ref('');
+  const groupList = ref<any[]>([]);
+  const queryParams = reactive<any>({
+    pageNumber: 1,
+    pageSize: 10,
+    queryParam: {
+      responsibilityName: '',
+      departmentName: '',
+      status: '',
+      date: '',
+      responsibilityPersonId: '',
+    },
+  });
+
+  const tableData = reactive({
+    data: [],
+    total: 0,
+  });
+
+  const handleQueryUserGroupPage = () => {
+    queryUserGroupPage({
+      pageNumber: 1,
+      pageSize: 500,
+    }).then((res) => {
+      groupList.value = res.records;
+    });
+  };
+
+  const handleIssueSafetyResponsibility = (scope) => {
+    currentRowData.value = scope.row;
+    issueSafetyResponsibilityRef.value.dialogShow();
+  };
+
+  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) => {
+    safetyResponsibilityAdminDelete(scope.row.id).then(() => {
+      ElMessage.success('删除成功!');
+      queryTableList();
+    });
+  };
+  const queryTableList = () => {
+    safetyResponsibilityDeptQueryPage({
+      ...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: '',
+        departmentName: '',
+        status: '',
+        date: '',
+        responsibilityPersonId: '',
+      },
+    });
+    queryTableList();
+  };
+
+  const handleSubmit = (formData) => {
+    issueSafetyResponsibilityRef.value.submitLoading = true;
+    safetyResponsibilityAdminIssuedSafety({
+      ...formData,
+      userGroupId: formData.userGroupId.join(','),
+      planStartTime: dayjs(formData.planStartTime).format('YYYY-MM-DD'),
+      planEndTime: dayjs(formData.planEndTime).format('YYYY-MM-DD'),
+      id: currentRowData.value.id,
+    })
+      .then(() => {
+        queryTableList();
+        issueSafetyResponsibilityRef.value.dialogHide();
+        currentRowData.value = {};
+      })
+      .finally(() => {
+        issueSafetyResponsibilityRef.value.submitLoading = false;
+      });
+  };
+
+  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>

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

@@ -174,7 +174,6 @@
   import { omit } from 'lodash-es';
   import { queryUserGroupPage } from '@/api/system/person-group';
 
-  import { FILE_TYPE_ICON } from '@/components/UploadFiles/constants';
   import { unformatAttachment } from '@/components/UploadFiles/utils';
   import { downloadFile } from '@/views/disaster/utils';
   import IssueSafetyResponsibility from './components/IssueSafetyResponsibility.vue';
@@ -293,9 +292,7 @@
   @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;
   }

+ 390 - 0
src/views/production-safety/implement-safety-duty/sign-agree-dept.vue

@@ -0,0 +1,390 @@
+<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 dayjs from 'dayjs';
+  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 {
+    safetyResponsibilityDeptQueryDetail,
+    safetyResponsibilityDeptSignOrFeedback,
+  } 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,
+        };
+        safetyResponsibilityDeptSignOrFeedback(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 = () => {
+    safetyResponsibilityDeptQueryDetail({
+      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);
+    }
+  }
+  // :deep(.breadcrumb .title) {
+  //   margin-left: 0;
+  // }
+
+  // .main {
+  //   display: flex;
+  //   flex-direction: column;
+  //   padding: 20px;
+  //   flex: 1;
+  //   overflow: hidden;
+  //   background-color: #fff;
+  // }
+  // .button-content {
+  //   margin-bottom: 20px;
+  // }
+
+  // .page-content {
+  //   display: flex;
+  //   justify-content: flex-end;
+  // }
+  // // :deep(.el-form) {
+  // //   flex: 1;
+  // //   overflow: hidden;
+  // //   overflow-y: auto;
+  // // }
+</style>

+ 4 - 2
src/views/production-safety/implement-safety-duty/view-recipients.vue

@@ -82,10 +82,11 @@
                 @click="
                   $router.push({
                     name: 'agreeDocumentReview',
+                    query: { id: scope.row.id },
                   })
                 "
-                >审核</el-button
-              >
+                >审核
+              </el-button>
               <el-button type="primary" link @click="handleDownloadLink(scope)">下载</el-button>
             </template>
           </el-table-column>
@@ -292,6 +293,7 @@
   .detail-content {
     display: flex;
     gap: 30px;
+    margin: 10px 0;
     font-size: 14px;
   }
   .search-form {