Sfoglia il codice sorgente

完成大部分预防检查

chauncey 11 mesi fa
parent
commit
ad3c00535f
22 ha cambiato i file con 1107 aggiunte e 243 eliminazioni
  1. 48 0
      src/api/disaster-precaution/index.ts
  2. 10 0
      src/api/system/person-group.ts
  3. 24 0
      src/components/BasicTable.vue
  4. 20 0
      src/router/full-routes.ts
  5. 32 1
      src/types/disaster-precaution/index.ts
  6. 8 0
      src/types/person-group/type.ts
  7. 1 0
      src/views/disaster/disaster-control/PageDisposalManagement.vue
  8. 1 0
      src/views/disaster/disaster-control/PageDisposalRectification.vue
  9. 1 6
      src/views/disaster/disaster-control/src/components/LossRecord.vue
  10. 6 0
      src/views/disaster/disaster-control/src/style/collapse.scss
  11. 26 0
      src/views/disaster/disaster-control/src/style/pagination.scss
  12. 30 5
      src/views/disaster/disaster-precaution/PageTaskExecution.vue
  13. 210 0
      src/views/disaster/disaster-precaution/PageTaskExecutionDetail.vue
  14. 1 1
      src/views/disaster/disaster-precaution/PageTaskTemplateDetail.vue
  15. 25 5
      src/views/disaster/disaster-precaution/src/components/ActualSituation.vue
  16. 18 5
      src/views/disaster/disaster-precaution/src/components/Compliance.vue
  17. 251 0
      src/views/disaster/disaster-precaution/src/components/TemplateTableMerge Copy.vue
  18. 198 27
      src/views/disaster/disaster-precaution/src/components/TemplateTableMerge.vue
  19. 187 187
      src/views/disaster/disaster-precaution/src/constants/template-detail.ts
  20. 5 3
      src/views/disaster/disaster-precaution/src/type.ts
  21. 3 3
      src/views/disaster/disaster-precaution/src/utils/format-data.ts
  22. 2 0
      src/views/disaster/hooks/userInfo.ts

+ 48 - 0
src/api/disaster-precaution/index.ts

@@ -4,6 +4,8 @@ import type {
   TaskExecutionListQuery,
   TaskManagementListResponse,
   TaskExecutionListResponse,
+  TaskExecutionDetailResponse,
+  SaveTaskDetailRequest,
 } from '@/types/disaster-precaution';
 import type { UserInfo } from '@/types/push-object';
 import type { QueryPageRequest, QueryPageResponse } from '@/types/disaster';
@@ -106,3 +108,49 @@ export const getTaskInspectorList = (preventInspectTaskId: number) => {
     },
   });
 };
+
+/**
+ * 查询执行任务详情
+ */
+export const getTaskExecutionDetail = (preventInspectTaskId: number) => {
+  return http.request<TaskExecutionDetailResponse>({
+    url: '/preventInspectTask/queryTaskExecuteDetail',
+    method: 'get',
+    params: { preventInspectTaskId },
+  });
+};
+
+/**
+ * 执行任务检查
+ */
+export const saveTaskDetail = (data: SaveTaskDetailRequest) => {
+  return http.request({
+    url: '/preventInspectTask/updateTaskInspectDetail',
+    method: 'post',
+    data,
+  });
+};
+
+/**
+ * 撤回检查任务
+ */
+export const withdrawTaskInspect = (preventInspectTaskId: number) => {
+  return http.request({
+    url: '/preventInspectTask/updateTaskInspectState',
+    method: 'get',
+    params: {
+      preventInspectTaskId,
+    },
+  });
+};
+
+/**
+ * 执行任务审批
+ */
+export const saveTaskApproval = (data: SaveTaskDetailRequest) => {
+  return http.request({
+    url: '/preventInspectTask/updateTaskReviewDetail',
+    method: 'post',
+    data,
+  });
+};

+ 10 - 0
src/api/system/person-group.ts

@@ -94,3 +94,13 @@ export function queryAvailableUserList(params: QueryAvailablePersonPageParams):
     data: params,
   });
 }
+
+/**
+ * 查询用户所在一级部门的组织树
+ */
+export const getUserFirstLevelTree = () => {
+  return http.request({
+    url: '/admin/dept/queryFirstLevelDeptTreeById',
+    method: 'get',
+  });
+};

+ 24 - 0
src/components/BasicTable.vue

@@ -95,6 +95,30 @@
     position: absolute;
     bottom: 30cpx;
     right: 40cpx;
+    :deep(.btn-prev),
+    :deep(.btn-next),
+    :deep(.el-pager li) {
+      width: 32cpx;
+      height: 32cpx;
+    }
+    :deep(.el-pager li),
+    :deep(.el-pagination__jump),
+    :deep(.el-pagination__total),
+    :deep(.el-input__wrapper),
+    :deep(.el-select__wrapper),
+    :deep(.el-select__suffix) {
+      font-size: 14cpx;
+    }
+    :deep(.el-input__wrapper) {
+      padding: 1cpx 11cpx;
+    }
+    :deep(.el-select__wrapper) {
+      padding: 4cpx 12cpx;
+    }
+    :deep(.el-input__inner){
+      width: 34cpx;
+      height: 30cpx;
+    }
   }
   :deep(.el-loading-mask) {
     background-color: rgba(0, 0, 0, 0.05);

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

@@ -219,6 +219,26 @@ export const disasterPreventionRoute = {
           path: 'task-execution',
           redirect: '',
         },
+        {
+          component: '/disaster/disaster-precaution/PageTaskExecutionDetail',
+          id: 1034,
+          meta: {
+            activeMenu: '/disaster-prevention/disaster-precaution/task-execution',
+            alwaysShow: false,
+            frameSrc: '',
+            hidden: false,
+            icon: '',
+            isFrame: 0,
+            isRoot: false,
+            noCache: false,
+            query: '',
+            title: '任务执行详情',
+          },
+          name: 'disaster-precaution-task-execution-detail',
+          parentId: 1028,
+          path: 'task-execution-detail/:id',
+          redirect: '',
+        },
         {
           component: '/disaster/disaster-precaution/PageTaskTemplate',
           id: 1031,

+ 32 - 1
src/types/disaster-precaution/index.ts

@@ -1,4 +1,5 @@
 import type { USER_TYPE } from '@/views/disaster/disaster-precaution/src/constants/task-execution';
+import type { SpanTableData } from '@/views/disaster/disaster-precaution/src/type';
 interface BasicQuery {
   inspectType?: string;
   taskState?: string;
@@ -30,7 +31,10 @@ export interface TaskExecutionListResponse extends BasicResponse {
 }
 
 export interface TaskManagementItemResponse
-  extends Omit<TaskManagementListResponse, 'deptName' | 'inspectType' | 'overdue' | 'effectStatus' | 'taskState' | 'updatedAt'> {
+  extends Omit<
+    TaskManagementListResponse,
+    'deptName' | 'inspectType' | 'overdue' | 'effectStatus' | 'taskState' | 'updatedAt'
+  > {
   inspectType: number | null;
   deptIdList: string[] | string;
   inspectRequirement: string;
@@ -38,3 +42,30 @@ export interface TaskManagementItemResponse
   isPush: boolean | null;
   templateId: number | null;
 }
+export interface ContentItem {
+  executeImages?: string[];
+  executeName: string;
+  executeResult: string;
+  inspectTime: string;
+}
+
+export interface TemplateDetailResponse {
+  deptOpinion: ContentItem;
+  inspectResult: ContentItem;
+  inspectTemplateDetailVOs: SpanTableData[];
+}
+
+export interface TaskExecutionDetailResponse {
+  deptName: string;
+  detail: string;
+  name: string;
+  reviewerId: number;
+}
+
+export interface SaveTaskDetailRequest {
+  id: number;
+  deptOpinion: ContentItem;
+  inspectResult: ContentItem;
+  inspectDetails: Pick<SpanTableData, 'id' | 'compliance' | 'actualSituation' | 'inspectImages'>[];
+  reviewerId: number;
+}

+ 8 - 0
src/types/person-group/type.ts

@@ -112,3 +112,11 @@ export interface UserGroupInfo extends QueryPersonGroupDetailRes {
 
 /** 查询可选用户列表后端返回data */
 export type QueryAvailablePersonPageRes = PaginationResponse<PersonGroupItem>;
+
+/** 查询用户所在一级部门的组织树后端返回data */
+export interface QueryUserFirstLevelTreeRes {
+  id: number;
+  deptName: string;
+  userList: PersonGroupItem[];
+  children: QueryUserFirstLevelTreeRes[];
+}

+ 1 - 0
src/views/disaster/disaster-control/PageDisposalManagement.vue

@@ -346,6 +346,7 @@
   @use '../style/disaster.scss' as *;
   @use './src/style/collapse.scss' as *;
   @use './src/style/common.scss' as *;
+  @use './src/style/pagination.scss' as *;
   $collapse-container-height-default: calc(68vh - 13cpx);
   $collapse-container-height-permission: calc(63vh - 15cpx);
   .collapse-container {

+ 1 - 0
src/views/disaster/disaster-control/PageDisposalRectification.vue

@@ -205,6 +205,7 @@
   @use '../style/disaster.scss' as *;
   @use './src/style/collapse.scss' as *;
   @use './src/style/common.scss' as *;
+  @use './src/style/pagination.scss' as *;
   $collapse-container-height-default: calc(68vh - 13cpx);
   .collapse-container {
     height: $collapse-container-height-default;

+ 1 - 6
src/views/disaster/disaster-control/src/components/LossRecord.vue

@@ -190,6 +190,7 @@
   @use '@/views/disaster/style/disaster.scss' as *;
   @use '../style/collapse.scss' as *;
   @use '../style/common.scss' as *;
+  @use '../style/pagination.scss' as *;
   $collapse-container-height: calc(60vh - 20cpx);
   .collapse-container {
     height: $collapse-container-height;
@@ -200,10 +201,4 @@
     color: #999;
     font-weight: 400;
   }
-  .affected-items-container {
-    width: 100%;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-  }
 </style>

+ 6 - 0
src/views/disaster/disaster-control/src/style/collapse.scss

@@ -26,3 +26,9 @@ $priority-urgent-color: #ff4d4f;
     color: $priority-urgent-color;
   }
 }
+.affected-items-container {
+  width: 100%;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}

+ 26 - 0
src/views/disaster/disaster-control/src/style/pagination.scss

@@ -0,0 +1,26 @@
+.el-pagination {
+  :deep(.btn-prev),
+  :deep(.btn-next),
+  :deep(.el-pager li) {
+    width: 32cpx;
+    height: 32cpx;
+  }
+  :deep(.el-pager li),
+  :deep(.el-pagination__jump),
+  :deep(.el-pagination__total),
+  :deep(.el-input__wrapper),
+  :deep(.el-select__wrapper),
+  :deep(.el-select__suffix) {
+    font-size: 14cpx;
+  }
+  :deep(.el-input__wrapper) {
+    padding: 1cpx 11cpx;
+  }
+  :deep(.el-select__wrapper) {
+    padding: 4cpx 12cpx;
+  }
+  :deep(.el-input__inner) {
+    width: 34cpx;
+    height: 30cpx;
+  }
+}

+ 30 - 5
src/views/disaster/disaster-precaution/PageTaskExecution.vue

@@ -36,7 +36,7 @@
           <template #action="scope">
             <!-- 检查任务操作入口 -->
             <div class="action-container" v-if="scope.row.taskState === TASK_STAGE.PENDING_CHECK">
-              <ActionButton text="去检查" />
+              <ActionButton text="去检查" @click="handleCheckItem(scope.row.id, 'check')" />
               <!-- 仅检查责任人可以看到 -->
               <ActionButton
                 text="添加检查人"
@@ -47,7 +47,11 @@
             <!-- 审批任务操作入口 -->
             <div class="action-container" v-else-if="scope.row.taskState === TASK_STAGE.PENDING_APPROVAL">
               <!-- 审批人员可以看到 -->
-              <ActionButton text="去审批" v-if="scope.row.userTypeList.includes(USER_TYPE.APPROVER)" />
+              <ActionButton
+                text="去审批"
+                v-if="scope.row.userTypeList.includes(USER_TYPE.APPROVER)"
+                @click="handleCheckItem(scope.row.id, 'approve')"
+              />
               <!-- 检查责任人、检查执行人员可以看到 -->
               <ActionButton
                 v-else
@@ -55,11 +59,12 @@
                 :popconfirm="{
                   title: '确定撤回吗?',
                 }"
+                @confirm="handleWithdrawTask(scope.row.id)"
               />
             </div>
             <!-- 完成任务操作入口 -->
             <div class="action-container" v-else-if="scope.row.taskState === TASK_STAGE.COMPLETED">
-              <ActionButton text="查看" />
+              <ActionButton text="查看" @click="handleCheckItem(scope.row.id, 'view')" />
               <!-- 仅审批人员可以看到 -->
               <ActionButton
                 v-if="scope.row.userTypeList.includes(USER_TYPE.APPROVER)"
@@ -82,7 +87,7 @@
   import ActionButton from '@/components/ActionButton.vue';
   import Search from '@/views/disaster/components/Search.vue';
   import useTableConfig from '@/hooks/useTableConfigHook';
-  import { getTaskExecutionList, getTaskInspectorList } from '@/api/disaster-precaution';
+  import { getTaskExecutionList, getTaskInspectorList, withdrawTaskInspect } from '@/api/disaster-precaution';
   import type { TaskExecutionListResponse } from '@/types/disaster-precaution';
   import OverdueIcon from '@/assets/svg/overdue.svg';
   import { TASK_STAGE } from './src/constants/task-execution';
@@ -92,7 +97,10 @@
   import type { QueryPageRequest } from '@/types/disaster';
   import { USER_TYPE } from './src/constants/task-execution';
   import type { UserInfo } from '@/types/push-object';
+  import { useRouter } from 'vue-router';
+  import { ElMessage } from 'element-plus';
 
+  const router = useRouter();
   const userInfo = ref(false);
   const searchData = reactive({
     inspectType: '',
@@ -128,7 +136,7 @@
   };
   const currentTaskId = ref<number>();
   const currentTaskInspectorList = ref<UserInfo[]>([]);
-  const handleAddCheckUser = async(row: TaskExecutionListResponse) => {
+  const handleAddCheckUser = async (row: TaskExecutionListResponse) => {
     currentTaskId.value = row.id;
     const res = await getTaskInspectorList(currentTaskId.value);
     currentTaskInspectorList.value = res.map((item) => {
@@ -139,6 +147,23 @@
     });
     userInfo.value = true;
   };
+  const defaultRouterName = 'disaster-precaution-task-execution-detail';
+  const handleCheckItem = (id: number, operationType: 'check' | 'approve' | 'view') => {
+    router.push({
+      name: defaultRouterName,
+      params: {
+        id,
+      },
+      query: {
+        operationType,
+      },
+    });
+  };
+  const handleWithdrawTask = async (id: number) => {
+    await withdrawTaskInspect(id);
+    ElMessage.success('撤回成功');
+    getTableData();
+  };
   const getTableData = async () => {
     tableConfig.loading = true;
     const res = await getTaskExecutionList(taskManagementListQuery);

+ 210 - 0
src/views/disaster/disaster-precaution/PageTaskExecutionDetail.vue

@@ -0,0 +1,210 @@
+<template>
+  <div class="disaster-precaution-container">
+    <header class="disaster-precaution-container__header">
+      <img :src="BackIcon" alt="back" class="back-icon" @click="router.back()" />
+      <span class="disaster-precaution-container__title">灾害预防检查任务</span>
+    </header>
+    <main class="disaster-precaution-container__main">
+      <div class="disaster-precaution">
+        <p class="title">
+          任务名称:<span class="content">{{ taskExecutionDetailList?.name }}</span>
+        </p>
+        <p class="title">
+          被检查自查单位:<span class="content">{{ taskExecutionDetailList?.deptName }}</span>
+        </p>
+        <TemplateTableMerge
+          ref="templateTableMergeRef"
+          :operation-type="operationType"
+          :main-table="templateDetailList?.inspectTemplateDetailVOs || []"
+          :opinion-data="templateDetailList?.deptOpinion || {} as ContentItem"
+          :result-data="templateDetailList?.inspectResult || {} as ContentItem"
+          :height="
+            operationType === 'check' ? 'calc(50vh - 65px)' : operationType === 'view' ? 'calc(70vh - 100px)' : '50vh'
+          "
+          @validate-change="handleValidateChange"
+        />
+        <el-form style="max-width: 400px" :model="formModel" label-width="auto" v-if="operationType === 'check'">
+          <el-form-item label="选择审批领导" prop="reviewerId" :rules="[{ required: true, message: '请选择审批领导' }]">
+            <el-tree-select
+              v-model="formModel.reviewerId"
+              :data="treeData"
+              node-key="id"
+              :props="{
+                label: 'label',
+                children: 'children',
+                disabled: (data) => !data.isUser || (data.staffNo && disabledStaffNos.includes(data.staffNo)),
+              }"
+              placeholder="请选择审批领导"
+              check-strictly
+              filterable
+              default-expand-all
+            />
+          </el-form-item>
+        </el-form>
+      </div>
+    </main>
+    <footer class="disaster-precaution-container__footer" v-if="operationType !== 'view'">
+      <el-button @click="router.back()">取消</el-button>
+      <el-button type="primary" @click="handleSubmit" :disabled="submitDisabled"> 提交 </el-button>
+    </footer>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { onMounted, ref, computed, reactive } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import { useRoute, useRouter } from 'vue-router';
+  import BackIcon from 'assets/svg/back.svg';
+  import TemplateTableMerge from './src/components/TemplateTableMerge.vue';
+  import { getTaskExecutionDetail, saveTaskDetail, saveTaskApproval } from '@/api/disaster-precaution';
+  import { getUserFirstLevelTree } from '@/api/system/person-group';
+  import { useUserInfoHook } from '@/views/disaster/hooks/userInfo';
+  import type { QueryUserFirstLevelTreeRes } from '@/types/person-group/type';
+  import type {
+    TaskExecutionDetailResponse,
+    TemplateDetailResponse,
+    ContentItem,
+    SaveTaskDetailRequest,
+  } from '@/types/disaster-precaution';
+
+  const { staffNo } = useUserInfoHook();
+
+  // 需要禁用的工号列表
+  const disabledStaffNos = computed(() => {
+    const staffNos = [staffNo];
+    return staffNos;
+  });
+
+  interface TreeNode {
+    id: number;
+    label: string;
+    children?: TreeNode[];
+    isUser: boolean;
+    staffNo?: string;
+  }
+
+  interface FormModel {
+    reviewerId: number | undefined;
+  }
+
+  type OperationType = 'view' | 'approve' | 'check';
+  const templateTableMergeRef = ref<InstanceType<typeof TemplateTableMerge>>();
+  const isFormValid = ref(false); // 表单验证状态
+  const formModel = reactive<FormModel>({
+    reviewerId: undefined,
+  });
+
+  const route = useRoute();
+  const router = useRouter();
+  const id = Number(route.params.id);
+  const operationType = String(route.query.operationType) as OperationType;
+  const taskExecutionDetailList = ref<TaskExecutionDetailResponse>();
+  const templateDetailList = ref<TemplateDetailResponse>();
+  const userFirstLevelTreeList = ref<QueryUserFirstLevelTreeRes>();
+
+  // 将组织树数据转换为树形选择器所需的格式
+  const treeData = computed<TreeNode[]>(() => {
+    if (!userFirstLevelTreeList.value) return [];
+
+    // 将组织树转换为树形选择器需要的格式
+    const convertToTreeData = (tree: QueryUserFirstLevelTreeRes): TreeNode => {
+      const node: TreeNode = {
+        id: tree.id,
+        label: tree.deptName,
+        isUser: false,
+        children: [],
+      };
+
+      // 添加部门下的用户
+      if (tree.userList && tree.userList.length) {
+        const userNodes: TreeNode[] = tree.userList.map((user) => ({
+          id: user.id,
+          label: user.realname,
+          isUser: true,
+          staffNo: user.staffNo,
+        }));
+        node.children = node.children || [];
+        node.children = [...node.children, ...userNodes];
+      }
+
+      // 添加子部门
+      if (tree.children && tree.children.length) {
+        const childNodes = tree.children.map((child) => convertToTreeData(child));
+        node.children = node.children || [];
+        node.children = [...node.children, ...childNodes];
+      }
+
+      return node;
+    };
+
+    return [convertToTreeData(userFirstLevelTreeList.value)];
+  });
+
+  const getTaskExecutionDetailList = async () => {
+    const res = await getTaskExecutionDetail(id);
+    taskExecutionDetailList.value = res;
+    if (res.reviewerId) {
+      formModel.reviewerId = res.reviewerId;
+    }
+    templateDetailList.value = JSON.parse(res.detail) as TemplateDetailResponse;
+  };
+
+  const getUserFirstLevelTreeList = async () => {
+    const res = await getUserFirstLevelTree();
+    userFirstLevelTreeList.value = res;
+  };
+
+  // 处理验证状态变化
+  const handleValidateChange = (isValid: boolean) => {
+    isFormValid.value = Boolean(isValid);
+  };
+
+  const submitDisabled = computed(() => {
+    return !Boolean(formModel.reviewerId) || !isFormValid.value;
+  });
+
+  const handleSubmit = async () => {
+    if (!templateTableMergeRef.value) return;
+
+    const { mainTable, opinionData, resultData } = templateTableMergeRef.value.getTableData();
+    const saveTaskDetailRequest: SaveTaskDetailRequest = {
+      id,
+      deptOpinion: opinionData,
+      inspectResult: resultData,
+      inspectDetails: mainTable,
+      reviewerId: formModel.reviewerId as number,
+    };
+    // 执行提交逻辑
+    if (operationType === 'check') {
+      await saveTaskDetail(saveTaskDetailRequest);
+    } else {
+      await saveTaskApproval(saveTaskDetailRequest);
+    }
+    ElMessage.success('提交成功');
+    router.back();
+  };
+
+  onMounted(() => {
+    getTaskExecutionDetailList();
+    getUserFirstLevelTreeList();
+  });
+</script>
+
+<style lang="scss" scoped>
+  @use '../style/disaster.scss' as *;
+  .disaster-precaution-container__header {
+    flex-direction: row !important;
+    justify-content: flex-start !important;
+    gap: 8cpx !important;
+  }
+  .title,
+  .content {
+    font-size: 14cpx;
+  }
+  .title {
+    color: rgba($text-color, 0.85);
+  }
+  .content {
+    color: rgba($text-color, 0.65);
+  }
+</style>

+ 1 - 1
src/views/disaster/disaster-precaution/PageTaskTemplateDetail.vue

@@ -15,7 +15,7 @@
   import { useRoute, useRouter } from 'vue-router';
   import BackIcon from 'assets/svg/back.svg';
   import { TASK_TEMPLATE_LIST } from './src/constants/template-detail';
-  import TemplateTableMerge from './src/components/TemplateTableMerge.vue';
+  import TemplateTableMerge from './src/components/TemplateTableMerge Copy.vue';
 
   const route = useRoute();
   const router = useRouter();

+ 25 - 5
src/views/disaster/disaster-precaution/src/components/ActualSituation.vue

@@ -1,9 +1,8 @@
 <template>
   <el-input
-    v-model="textarea"
+    v-model="textareaModel"
     v-bind="$attrs"
     maxlength="200"
-    style="width: 100%"
     placeholder="必填,请输入实际情况,1-200字"
     type="textarea"
     :rows="3"
@@ -11,11 +10,32 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref } from 'vue';
+  import { ref, watch } from 'vue';
   const props = defineProps<{
     actualSituation: string;
   }>();
-  const textarea = ref(props.actualSituation);
+
+  const emit = defineEmits(['update:actualSituation']);
+
+  const textareaModel = ref(props.actualSituation);
+
+  // 监听输入值变化,向父组件发送更新事件
+  watch(textareaModel, (newValue) => {
+    emit('update:actualSituation', newValue);
+  });
+
+  // 监听props变化,更新本地值
+  watch(
+    () => props.actualSituation,
+    (newValue) => {
+      textareaModel.value = newValue;
+    },
+  );
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+  .el-input {
+    width: 100%;
+    border: 1px solid red;
+  }
+</style>

+ 18 - 5
src/views/disaster/disaster-precaution/src/components/Compliance.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-radio-group v-model="radioValue" class="compliance">
+  <el-radio-group v-model="radioValueModel" class="compliance">
     <div class="compliance-radio-container">
       <el-radio
         v-bind="$attrs"
@@ -15,16 +15,29 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref } from 'vue';
+  import { ref, watch } from 'vue';
   const radioOptions = [
     { label: '符合', value: 1 },
-    { label: '不符合', value: 0 },
-    { label: '不涉及', value: 2 },
+    { label: '不符合', value: 2 },
+    { label: '不涉及', value: 3 },
   ];
   const props = defineProps<{
     radioValue: number;
   }>();
-  const radioValue = ref(props.radioValue);
+  
+  const emit = defineEmits(['update:radioValue']);
+  
+  const radioValueModel = ref(props.radioValue);
+  
+  // 监听输入值变化,向父组件发送更新事件
+  watch(radioValueModel, (newValue) => {
+    emit('update:radioValue', newValue);
+  });
+  
+  // 监听props变化,更新本地值
+  watch(() => props.radioValue, (newValue) => {
+    radioValueModel.value = newValue;
+  });
 </script>
 
 <style lang="scss" scoped>

+ 251 - 0
src/views/disaster/disaster-precaution/src/components/TemplateTableMerge Copy.vue

@@ -0,0 +1,251 @@
+<template>
+  <div class="el-table-page">
+    <el-table :data="TASK_TEMPLATE_HEADER" :show-header="false" border>
+      <el-table-column prop="title" align="center" width="60" />
+      <el-table-column prop="content" align="center" />
+    </el-table>
+    <el-table
+      v-bind="$attrs"
+      :data="mergedTableData"
+      :span-method="handleSpanMethod"
+      border
+      class="custom-table"
+      :row-class-name="tableRowClassName"
+    >
+      <el-table-column v-for="column in TASK_TEMPLATE_MAIN_COLUMNS" :key="column.prop" v-bind="column" align="center">
+        <template #default="scope">
+          <!-- 底部表格内容 - 第一列 -->
+          <template v-if="scope.row.rowType === 'footer' && column.prop === 'inspectItem'">
+            <div v-if="scope.row.footerType === 'inspectionAndSelftest'" class="inspection-result--div">
+              <span>检查结论及存在问题:</span>
+              <el-input
+                v-model="scope.row.inspectionResult"
+                maxlength="200"
+                style="width: 100%"
+                placeholder="必填,输入检查结论及存在问题,1-200字"
+                type="textarea"
+                :rows="2"
+                v-show="operationType === 'edit'"
+              />
+            </div>
+          </template>
+
+          <!-- 底部表格内容 - 第二列 -->
+          <template v-else-if="scope.row.rowType === 'footer' && column.prop === 'inspectStandard'">
+            <div v-if="scope.row.footerType === 'inspectionAndSelftest'" class="inspection-result--div">
+              <span>被检查(自查)单位意见:</span>
+              <el-input
+                v-model="scope.row.reviewResult"
+                maxlength="200"
+                style="width: 100%"
+                placeholder="必填,输入被检查(自查)单位意见,1-200字"
+                type="textarea"
+                :rows="2"
+                v-show="operationType === 'edit'"
+              />
+            </div>
+          </template>
+        </template>
+      </el-table-column>
+
+      <!-- 自检检查结果列 -->
+      <el-table-column label="自检检查结果" align="center" fixed="right">
+        <el-table-column prop="compliance" label="符合" align="center" width="150">
+          <template #default="scope">
+            <Compliance :radio-value="scope.row.compliance" :disabled="operationType === 'view'" />
+          </template>
+        </el-table-column>
+        <el-table-column prop="actualSituation" label="实际情况" align="center" width="410">
+          <template #default="scope">
+            <ActualSituation v-show="operationType === 'edit'" :actual-situation="scope.row.actualSituation" />
+          </template>
+        </el-table-column>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, computed, onMounted } from 'vue';
+  import type { SpanTableData, MainRow, MergedRow } from '../type';
+  import { formatSpanTableData } from '../utils/format-data';
+  import {
+    TASK_TEMPLATE_HEADER,
+    TASK_TEMPLATE_FOOTER,
+    TASK_TEMPLATE_MAIN_COLUMNS,
+    MERGE_FIELDS,
+  } from '../constants/template-detail';
+  import Compliance from './Compliance.vue';
+  import ActualSituation from './ActualSituation.vue';
+
+  const props = defineProps<{
+    operationType: 'view' | 'edit';
+    mainTable: SpanTableData[];
+  }>();
+
+  // 创建一个合并的数据源
+  const mergedTableData = computed<MergedRow[]>(() => {
+    const formattedMainData = formatSpanTableData(props.mainTable).map((item) => ({
+      ...item,
+      rowType: 'main' as const,
+    }));
+    // 为底部添加类型标记
+    const footerData = [
+      {
+        ...TASK_TEMPLATE_FOOTER[0],
+        rowType: 'footer' as const,
+        footerType: 'inspectionAndSelftest' as const, // 合并为一行
+      },
+    ];
+
+    return [...formattedMainData, ...footerData];
+  });
+
+  // 存储每列的合并状态
+  const spanMaps = ref<{ [key: string]: number[][] }>({});
+
+  // 根据列索引获取对应的属性名
+  const getPropNameByColumnIndex = (columnIndex: number) => {
+    return TASK_TEMPLATE_MAIN_COLUMNS[columnIndex]?.prop || '';
+  };
+
+  // 初始化合并信息
+  const initSpanMaps = () => {
+    const mainData = mergedTableData.value.filter((item): item is MainRow => item.rowType === 'main');
+    if (!mainData.length) return;
+
+    MERGE_FIELDS.forEach((field) => {
+      // 每个字段用于存储 [行的起始位置, 合并的行数] 的二维数组
+      const cellSpans: number[][] = [];
+      let currentValue = mainData[0][field];
+      let currentIndex = mainData[0].index;
+      let startIndex = 0;
+      let spanCount = 1;
+
+      for (let i = 1; i < mainData.length; i++) {
+        // 只有当当前值与前一个值相同,且index也相同时才合并
+        if (mainData[i][field] === currentValue && mainData[i].index === currentIndex) {
+          spanCount++;
+        } else {
+          cellSpans.push([startIndex, spanCount]);
+          currentValue = mainData[i][field];
+          currentIndex = mainData[i].index;
+          startIndex = i;
+          spanCount = 1;
+        }
+      }
+      // 处理最后一组数据
+      cellSpans.push([startIndex, spanCount]);
+      spanMaps.value[field] = cellSpans;
+    });
+  };
+
+  // 自定义合并方法
+  const handleSpanMethod = ({
+    row,
+    column,
+    rowIndex,
+    columnIndex,
+  }: {
+    row: MergedRow;
+    column: { property: string };
+    rowIndex: number;
+    columnIndex: number;
+  }) => {
+    // 处理底部行
+    if (row.rowType === 'footer') {
+      if (column.property === 'inspectItem') {
+        if (row.footerType === 'inspectionAndSelftest') {
+          // 第一个单元格占据左半部分
+          return { rowspan: 1, colspan: 3 };
+        }
+      } else if (column.property === 'inspectStandard' && row.footerType === 'inspectionAndSelftest') {
+        // 第二个单元格占据右半部分
+        return { rowspan: 1, colspan: 3 };
+      }
+      // 隐藏其余单元格
+      return { rowspan: 0, colspan: 0 };
+    }
+
+    // 处理主表数据的合并
+    if (row.rowType === 'main') {
+      const mainDataStartIndex = mergedTableData.value.findIndex((item) => item.rowType === 'main');
+      const adjustedRowIndex = rowIndex - mainDataStartIndex;
+
+      const propName = getPropNameByColumnIndex(columnIndex);
+
+      // 如果不在需要合并的字段列表中,直接返回不合并
+      if (!MERGE_FIELDS.includes(propName)) {
+        return { rowspan: 1, colspan: 1 };
+      }
+
+      // 获取当前字段的合并信息
+      const cellSpans = spanMaps.value[propName];
+      if (!cellSpans) return { rowspan: 1, colspan: 1 };
+
+      // 查找当前行是否是需要合并的起始行
+      for (const [startIndex, spanCount] of cellSpans) {
+        if (adjustedRowIndex === startIndex) {
+          // 是起始行,显示并合并
+          return { rowspan: spanCount, colspan: 1 };
+        } else if (adjustedRowIndex > startIndex && adjustedRowIndex < startIndex + spanCount) {
+          // 不是起始行,隐藏
+          return { rowspan: 0, colspan: 0 };
+        }
+      }
+    }
+
+    return { rowspan: 1, colspan: 1 };
+  };
+
+  // 新增:底部行粘性定位 class
+  const tableRowClassName = ({ row }: { row: MergedRow }) => {
+    if (row.rowType !== 'footer') return;
+    if (row.footerType === 'inspectionAndSelftest') {
+      return 'sticky-footer-inspection';
+    }
+    return;
+  };
+
+  // 监听数据变化,重新计算合并状态
+  onMounted(() => {
+    initSpanMaps();
+  });
+</script>
+
+<style lang="scss" scoped>
+  .el-table-page {
+    width: 100%;
+    height: 100%;
+  }
+
+  .inspection-result--div {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 5cpx;
+    padding: 5cpx;
+    height: 90cpx;
+  }
+
+  :deep(.custom-table .el-table__inner-wrapper:after) {
+    height: 0;
+  }
+
+  :deep(.custom-table th) {
+    background-color: $white-color !important;
+    color: #606266 !important;
+    font-weight: 500 !important;
+  }
+
+  .sticky-footer {
+    position: sticky;
+    z-index: 2;
+  }
+
+  :deep(.sticky-footer-inspection) {
+    @extend .sticky-footer;
+    bottom: 0;
+    box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1);
+  }
+</style>

+ 198 - 27
src/views/disaster/disaster-precaution/src/components/TemplateTableMerge.vue

@@ -15,34 +15,48 @@
       <el-table-column v-for="column in TASK_TEMPLATE_MAIN_COLUMNS" :key="column.prop" v-bind="column" align="center">
         <template #default="scope">
           <!-- 底部表格内容 - 第一列 -->
-          <template v-if="scope.row.rowType === 'footer' && column.prop === 'checkItem'">
+          <template v-if="scope.row.rowType === 'footer' && column.prop === 'inspectItem'">
             <div v-if="scope.row.footerType === 'inspectionAndSelftest'" class="inspection-result--div">
-              <span>检查结论及存在问题:</span>
+              <span class="inspection-result--title">检查结论及存在问题:</span>
               <el-input
-                v-model="scope.row.inspectionResult"
+                v-model="resultData.executeResult"
                 maxlength="200"
                 style="width: 100%"
                 placeholder="必填,输入检查结论及存在问题,1-200字"
                 type="textarea"
-                :rows="2"
-                v-show="operationType === 'edit'"
+                :rows="3"
+                v-if="props.operationType === 'check'"
               />
+              <div class="inspection-result-approve" v-else="props.operationType === 'approve'">
+                <div class="execute-result">{{ resultData.executeResult }}</div>
+                <div class="execute-person">
+                  <span>检查人员:{{ resultData.executeName }}</span>
+                  <span>时间:{{ resultData.inspectTime }}</span>
+                </div>
+              </div>
             </div>
           </template>
 
           <!-- 底部表格内容 - 第二列 -->
-          <template v-else-if="scope.row.rowType === 'footer' && column.prop === 'checkStandard'">
+          <template v-else-if="scope.row.rowType === 'footer' && column.prop === 'inspectStandard'">
             <div v-if="scope.row.footerType === 'inspectionAndSelftest'" class="inspection-result--div">
-              <span>被检查(自查)单位意见:</span>
+              <span class="inspection-result--title">被检查(自查)单位意见:</span>
               <el-input
-                v-model="scope.row.reviewResult"
+                v-model="opinionData.executeResult"
                 maxlength="200"
                 style="width: 100%"
                 placeholder="必填,输入被检查(自查)单位意见,1-200字"
                 type="textarea"
-                :rows="2"
-                v-show="operationType === 'edit'"
+                :rows="3"
+                v-if="props.operationType === 'approve'"
               />
+              <div class="inspection-result-approve" v-else-if="props.operationType === 'view'">
+                <div class="execute-result">{{ opinionData.executeResult }}</div>
+                <div class="execute-person">
+                  <span>审批人员:{{ opinionData.executeName }}</span>
+                  <span>时间:{{ opinionData.inspectTime }}</span>
+                </div>
+              </div>
             </div>
           </template>
         </template>
@@ -52,12 +66,20 @@
       <el-table-column label="自检检查结果" align="center" fixed="right">
         <el-table-column prop="compliance" label="符合" align="center" width="150">
           <template #default="scope">
-            <Compliance :radio-value="scope.row.compliance" :disabled="operationType === 'view'" />
+            <Compliance
+              v-model:radio-value="scope.row.compliance"
+              @update:radio-value="triggerValidate()"
+              :disabled="operationType !== 'check'"
+            />
           </template>
         </el-table-column>
         <el-table-column prop="actualSituation" label="实际情况" align="center" width="410">
           <template #default="scope">
-            <ActualSituation v-show="operationType === 'edit'" :actual-situation="scope.row.actualSituation" />
+            <ActualSituation
+              v-model:actual-situation="scope.row.actualSituation"
+              @update:actual-situation="triggerValidate()"
+              :disabled="operationType !== 'check'"
+            />
           </template>
         </el-table-column>
       </el-table-column>
@@ -66,35 +88,47 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref, computed, onMounted } from 'vue';
+  import { ref, computed, watch } from 'vue';
   import type { SpanTableData, MainRow, MergedRow } from '../type';
   import { formatSpanTableData } from '../utils/format-data';
-  import {
-    TASK_TEMPLATE_HEADER,
-    TASK_TEMPLATE_FOOTER,
-    TASK_TEMPLATE_MAIN_COLUMNS,
-    MERGE_FIELDS,
-  } from '../constants/template-detail';
+  import { TASK_TEMPLATE_HEADER, TASK_TEMPLATE_MAIN_COLUMNS, MERGE_FIELDS } from '../constants/template-detail';
   import Compliance from './Compliance.vue';
   import ActualSituation from './ActualSituation.vue';
+  import type { ContentItem } from '@/types/disaster-precaution';
+  import { useUserInfoHook } from '@/views/disaster/hooks/userInfo';
+
+  const { realname } = useUserInfoHook();
 
   const props = defineProps<{
-    operationType: 'view' | 'edit';
+    operationType: 'view' | 'approve' | 'check';
     mainTable: SpanTableData[];
+    opinionData: ContentItem;
+    resultData: ContentItem;
   }>();
 
+  // 定义事件
+  const emit = defineEmits(['validate-change']);
+
+  // 表单是否有效
+  const isValid = ref(false);
+
   // 创建一个合并的数据源
   const mergedTableData = computed<MergedRow[]>(() => {
     const formattedMainData = formatSpanTableData(props.mainTable).map((item) => ({
       ...item,
       rowType: 'main' as const,
     }));
+
     // 为底部添加类型标记
     const footerData = [
       {
-        ...TASK_TEMPLATE_FOOTER[0],
         rowType: 'footer' as const,
         footerType: 'inspectionAndSelftest' as const, // 合并为一行
+        inspectItem: '检查结论及存在问题',
+        inspectStandard: '被检查(自查)单位意见',
+        // 这些字段仅用于保持布局,实际输入通过直接绑定props进行
+        inspectionResult: '',
+        reviewResult: '',
       },
     ];
 
@@ -154,12 +188,12 @@
   }) => {
     // 处理底部行
     if (row.rowType === 'footer') {
-      if (column.property === 'checkItem') {
+      if (column.property === 'inspectItem') {
         if (row.footerType === 'inspectionAndSelftest') {
           // 第一个单元格占据左半部分
           return { rowspan: 1, colspan: 3 };
         }
-      } else if (column.property === 'checkStandard' && row.footerType === 'inspectionAndSelftest') {
+      } else if (column.property === 'inspectStandard' && row.footerType === 'inspectionAndSelftest') {
         // 第二个单元格占据右半部分
         return { rowspan: 1, colspan: 3 };
       }
@@ -207,9 +241,115 @@
     return;
   };
 
-  // 监听数据变化,重新计算合并状态
-  onMounted(() => {
-    initSpanMaps();
+  // 校验规则
+  const checkRule = (): boolean => {
+    const mainData = mergedTableData.value.filter((item): item is MainRow => item.rowType === 'main');
+
+    // 检查所有行是否都选择了符合项
+    const allComplianceSelected = mainData.every((item) => item.compliance !== null && item.compliance !== undefined);
+
+    // 检查所有行是否都填写了实际情况
+    const allActualSituationFilled = mainData.every(
+      (item) => item.actualSituation && item.actualSituation.trim() !== '',
+    );
+
+    // 检查结论是否已填写
+    const resultFilled = props.resultData.executeResult && props.resultData.executeResult.trim() !== '';
+
+    // 检查意见是否已填写(如果需要填写的话)
+    const opinionFilled =
+      props.operationType === 'check' ||
+      (props.opinionData.executeResult && props.opinionData.executeResult.trim() !== '');
+
+    // 所有条件都满足时返回true
+    return Boolean(allComplianceSelected && allActualSituationFilled && resultFilled && opinionFilled);
+  };
+
+  // 触发校验的函数
+  const triggerValidate = () => {
+    const valid = checkRule();
+    isValid.value = valid;
+    emit('validate-change', valid);
+  };
+
+  // 监听主表数据变化
+  watch(
+    () => props.mainTable,
+    () => {
+      initSpanMaps();
+      triggerValidate();
+    },
+    { immediate: true, deep: true },
+  );
+
+  // 监听结论和意见数据变化
+  watch(
+    () => props.resultData.executeResult,
+    () => {
+      triggerValidate();
+    },
+    { immediate: true },
+  );
+
+  // 监听意见数据变化
+  watch(
+    () => props.opinionData.executeResult,
+    () => {
+      triggerValidate();
+    },
+    { immediate: true },
+  );
+
+  // 获取表格数据的方法,用于父组件通过ref调用
+  const getOpinionData = (operationType: 'view' | 'approve' | 'check') => {
+    if (operationType !== 'approve') {
+      return props.opinionData;
+    }
+    const time = new Date().toLocaleString();
+    return {
+      executeResult: props.opinionData.executeResult || '',
+      inspectTime: time,
+      executeName: realname,
+    };
+  };
+
+  const getResultData = (operationType: 'view' | 'approve' | 'check') => {
+    if (operationType !== 'check') {
+      return props.resultData;
+    }
+    const time = new Date().toLocaleString();
+    return {
+      executeResult: props.resultData.executeResult || '',
+      inspectTime: time,
+      executeName: realname,
+    };
+  };
+
+  const getTableData = () => {
+    // 获取主表数据
+    const mainData = mergedTableData.value
+      .filter((item): item is MainRow => item.rowType === 'main')
+      .map((item) => ({
+        id: item.id,
+        inspectItem: item.inspectItem,
+        inspectStandard: item.inspectStandard,
+        inspectMethod: item.inspectMethod,
+        compliance: item.compliance,
+        actualSituation: item.actualSituation,
+        inspectImages: item.inspectImages,
+        index: item.index,
+      }));
+
+    return {
+      mainTable: mainData,
+      opinionData: getOpinionData(props.operationType),
+      resultData: getResultData(props.operationType),
+    };
+  };
+
+  // 暴露方法给父组件,使其可以通过ref调用
+  defineExpose({
+    getTableData,
   });
 </script>
 
@@ -225,7 +365,12 @@
     align-items: flex-start;
     gap: 5cpx;
     padding: 5cpx;
-    height: 90cpx;
+    height: 120px;
+  }
+
+  .inspection-result--title {
+    font-weight: 600;
+    color: $text-color;
   }
 
   :deep(.custom-table .el-table__inner-wrapper:after) {
@@ -248,4 +393,30 @@
     bottom: 0;
     box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1);
   }
+  .inspection-result-approve {
+    width: 100%;
+    height: 80px;
+    .execute-result {
+      text-align: left;
+      width: 100%;
+      height: 60%;
+      max-height: 60%;
+      overflow: auto;
+      &:hover {
+        &::-webkit-scrollbar {
+          position: relative;
+          width: 2px;
+        }
+        &::-webkit-scrollbar-thumb {
+          background-color: #d3d3d3;
+        }
+      }
+    }
+    .execute-person {
+      @include flex-center;
+      justify-content: space-between;
+      width: 100%;
+      flex: 1;
+    }
+  }
 </style>

+ 187 - 187
src/views/disaster/disaster-precaution/src/constants/template-detail.ts

@@ -8,100 +8,100 @@ import { INSPECT_TYPE } from './task-execution';
 // 模板前、中、后期数据
 export const PREVIOUS_TEMPLATE_DATA = [
   {
-    checkItem: '组织管理',
-    checkStandard: '1.是否成立专项组织机构(领导小组、应急办公室、抢险队伍),明确工作职责。',
-    checkMethod: '查文件资料',
+    inspectItem: '组织管理',
+    inspectStandard: '1.是否成立专项组织机构(领导小组、应急办公室、抢险队伍),明确工作职责。',
+    inspectMethod: '查文件资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '组织管理',
-    checkStandard: '2.是否召开工作动员会,传达上级会议精神,落实专项工作。',
-    checkMethod: '查文件资料',
+    inspectItem: '组织管理',
+    inspectStandard: '2.是否召开工作动员会,传达上级会议精神,落实专项工作。',
+    inspectMethod: '查文件资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '组织管理',
-    checkStandard: '3.是否按照要求组织进行专项安全检查,是否有整改计划及落实整改。',
-    checkMethod: '查检查记录',
+    inspectItem: '组织管理',
+    inspectStandard: '3.是否按照要求组织进行专项安全检查,是否有整改计划及落实整改。',
+    inspectMethod: '查检查记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '制度措施',
-    checkStandard: '1.是否建立和修订完善有关规章制度、岗位责任及各类案。',
-    checkMethod: '查文件资料',
+    inspectItem: '制度措施',
+    inspectStandard: '1.是否建立和修订完善有关规章制度、岗位责任及各类案。',
+    inspectMethod: '查文件资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '制度措施',
-    checkStandard: '2.是否及时制订专项预案、应急预案等,措施是否有效。',
-    checkMethod: '查文件资料',
+    inspectItem: '制度措施',
+    inspectStandard: '2.是否及时制订专项预案、应急预案等,措施是否有效。',
+    inspectMethod: '查文件资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '制度措施',
-    checkStandard: '3.是否有计划开展专项应急演练。',
-    checkMethod: '查演练记录',
+    inspectItem: '制度措施',
+    inspectStandard: '3.是否有计划开展专项应急演练。',
+    inspectMethod: '查演练记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '设施设备',
-    checkStandard: '1.抽排水设备检查、维修是否完成,排水设备是否完好。',
-    checkMethod: '查现场、有关记录',
+    inspectItem: '设施设备',
+    inspectStandard: '1.抽排水设备检查、维修是否完成,排水设备是否完好。',
+    inspectMethod: '查现场、有关记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '设施设备',
-    checkStandard: '2.是否对建筑、厂房、仓库等地易漏水漏雨的地方对进行全面排查统计,是否有应急情况的封堵措施。',
-    checkMethod: '查现场、有关记录',
+    inspectItem: '设施设备',
+    inspectStandard: '2.是否对建筑、厂房、仓库等地易漏水漏雨的地方对进行全面排查统计,是否有应急情况的封堵措施。',
+    inspectMethod: '查现场、有关记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '设施设备',
-    checkStandard: '3.重点建筑、厂房、飞机型号设施设备仓库防雷接地设备是否有效。',
-    checkMethod: '查现场、有关记录',
+    inspectItem: '设施设备',
+    inspectStandard: '3.重点建筑、厂房、飞机型号设施设备仓库防雷接地设备是否有效。',
+    inspectMethod: '查现场、有关记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '通信报讯',
-    checkStandard: '1.通信是否有保证,通讯联络是否畅通,是否备有应急电话。',
-    checkMethod: '查现场、有关记录',
+    inspectItem: '通信报讯',
+    inspectStandard: '1.通信是否有保证,通讯联络是否畅通,是否备有应急电话。',
+    inspectMethod: '查现场、有关记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '通信报讯',
-    checkStandard: '2.与当地应急部门联络方式和报讯是否明确。',
-    checkMethod: '查现场、有关记录',
+    inspectItem: '通信报讯',
+    inspectStandard: '2.与当地应急部门联络方式和报讯是否明确。',
+    inspectMethod: '查现场、有关记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '物资保障',
-    checkStandard: '1.是否有应急物资采购计划,应急物资是否备足,是否建立物资台账。',
-    checkMethod: '查现场、有关记录',
+    inspectItem: '物资保障',
+    inspectStandard: '1.是否有应急物资采购计划,应急物资是否备足,是否建立物资台账。',
+    inspectMethod: '查现场、有关记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '物资保障',
-    checkStandard: '2.车辆后勤、应急救援是否有保障。',
-    checkMethod: '查现场、有关记录',
+    inspectItem: '物资保障',
+    inspectStandard: '2.车辆后勤、应急救援是否有保障。',
+    inspectMethod: '查现场、有关记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '往年情况',
-    checkStandard: '历年受灾和新近发现问题处理情况,存在问题整改计划,措施落实',
-    checkMethod: '查现场、有关记录',
+    inspectItem: '往年情况',
+    inspectStandard: '历年受灾和新近发现问题处理情况,存在问题整改计划,措施落实',
+    inspectMethod: '查现场、有关记录',
     compliance: null,
     actualSituation: '',
   },
@@ -109,256 +109,256 @@ export const PREVIOUS_TEMPLATE_DATA = [
 
 export const MIDTERM_TEMPLATE_DATA = [
   {
-    checkItem: '防台防汛组织机构运作情况',
-    checkStandard: '1.领导小组和应急办公室是否按照应急信息要求进行值班。',
-    checkMethod: '查记录',
+    inspectItem: '防台防汛组织机构运作情况',
+    inspectStandard: '1.领导小组和应急办公室是否按照应急信息要求进行值班。',
+    inspectMethod: '查记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '防台防汛组织机构运作情况',
-    checkStandard: '2.领导小组是否有效开展工作进行检查、督促、及时落实和传达上级工作要求。',
-    checkMethod: '查记录',
+    inspectItem: '防台防汛组织机构运作情况',
+    inspectStandard: '2.领导小组是否有效开展工作进行检查、督促、及时落实和传达上级工作要求。',
+    inspectMethod: '查记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '防台防汛组织机构运作情况',
-    checkStandard: '3.是否召开台汛前部署会议,是否有会议纪要。',
-    checkMethod: '查文件、记录',
+    inspectItem: '防台防汛组织机构运作情况',
+    inspectStandard: '3.是否召开台汛前部署会议,是否有会议纪要。',
+    inspectMethod: '查文件、记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '措施落实情况',
-    checkStandard: '1.对台汛前检查查遗留问题是否有措施,落实整改。',
-    checkMethod: '查报告、记录',
+    inspectItem: '措施落实情况',
+    inspectStandard: '1.对台汛前检查查遗留问题是否有措施,落实整改。',
+    inspectMethod: '查报告、记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '措施落实情况',
-    checkStandard: '2.人员防范知识、风险信息、预警等宣传教育工作落实措施情况。',
-    checkMethod: '记录',
+    inspectItem: '措施落实情况',
+    inspectStandard: '2.人员防范知识、风险信息、预警等宣传教育工作落实措施情况。',
+    inspectMethod: '记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '措施落实情况',
-    checkStandard: '3.工地简易房、板房、低洼地、临时建筑、简陋屋棚等人员疏散措施落实情况。',
-    checkMethod: '现场',
+    inspectItem: '措施落实情况',
+    inspectStandard: '3.工地简易房、板房、低洼地、临时建筑、简陋屋棚等人员疏散措施落实情况。',
+    inspectMethod: '现场',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '措施落实情况',
-    checkStandard: '4.日常防台汛自查中发现问题是否进行了处理或落实措施。',
-    checkMethod: '查报告、记录',
+    inspectItem: '措施落实情况',
+    inspectStandard: '4.日常防台汛自查中发现问题是否进行了处理或落实措施。',
+    inspectMethod: '查报告、记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '措施落实情况',
-    checkStandard: '5.是否按计划开展防台汛预案演练。',
-    checkMethod: '查记录',
+    inspectItem: '措施落实情况',
+    inspectStandard: '5.是否按计划开展防台汛预案演练。',
+    inspectMethod: '查记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '措施落实情况',
-    checkStandard: '6.预案修订完善后的宣贯措施落实情况。',
-    checkMethod: '查记录',
+    inspectItem: '措施落实情况',
+    inspectStandard: '6.预案修订完善后的宣贯措施落实情况。',
+    inspectMethod: '查记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '措施落实情况',
-    checkStandard: '7.各类相关的临时标识是否明确清晰。',
-    checkMethod: '现场',
+    inspectItem: '措施落实情况',
+    inspectStandard: '7.各类相关的临时标识是否明确清晰。',
+    inspectMethod: '现场',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '1.建筑、仓库、厂房检查门窗、玻璃是否完好,并全部关闭。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '1.建筑、仓库、厂房检查门窗、玻璃是否完好,并全部关闭。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '2.建筑、仓库、厂房屋顶无漏水,(硝盐炉)重点部位是否有应急防护措施。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '2.建筑、仓库、厂房屋顶无漏水,(硝盐炉)重点部位是否有应急防护措施。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '3.建筑、仓库、厂房面积较大的门,要在里面设置档杆或斜撑,防被吹坏措施。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '3.建筑、仓库、厂房面积较大的门,要在里面设置档杆或斜撑,防被吹坏措施。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '4.建筑、仓库、厂房检查大门遮雨棚是否有锈蚀、松动、断裂等异常,并及时排除加固。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '4.建筑、仓库、厂房检查大门遮雨棚是否有锈蚀、松动、断裂等异常,并及时排除加固。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '5.建筑、仓库、厂房关闭通风窗、排风孔,防止雨水进入厂房措施是否有效。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '5.建筑、仓库、厂房关闭通风窗、排风孔,防止雨水进入厂房措施是否有效。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '6.建筑、仓库、厂房顶部物料、垃圾等是否清理完毕;悬挂物清理或加固。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '6.建筑、仓库、厂房顶部物料、垃圾等是否清理完毕;悬挂物清理或加固。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard:
+    inspectItem: '各类设施设备',
+    inspectStandard:
       '7.防止库房积水(地势低洼的库房门口应设置足够高度防洪沙袋,沙袋间需加添黄土),地势低洼处库房内的电器设备要离开地面。',
-    checkMethod: '现场、统计资料',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '8.交配电系统固定线路支架是否松动、断裂。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '8.交配电系统固定线路支架是否松动、断裂。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '9.交配电系统临时线路拆除,电线收好。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '9.交配电系统临时线路拆除,电线收好。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '10.室外配电箱(柜)门锁牢,并做好防雨措施,检查基座是否牢固,防止被风吹倒。配电板全部收回仓库。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '10.室外配电箱(柜)门锁牢,并做好防雨措施,检查基座是否牢固,防止被风吹倒。配电板全部收回仓库。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '11.切断公司全部动力设备(照明电源除外)的电源。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '11.切断公司全部动力设备(照明电源除外)的电源。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '12.危险品库房、油库油罐、液化气站、天然气站等重点区域防雷接地装置完好可靠。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '12.危险品库房、油库油罐、液化气站、天然气站等重点区域防雷接地装置完好可靠。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '13.建筑物和构筑物的防雷接地装置连接可靠。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '13.建筑物和构筑物的防雷接地装置连接可靠。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '14.增加保安值班岗亭配重,棚顶四个角落采用绳索斜拉固定。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '14.增加保安值班岗亭配重,棚顶四个角落采用绳索斜拉固定。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '15.检查车棚、风雨棚是否有锈蚀、松动、断裂等异常,并及时排除加固。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '15.检查车棚、风雨棚是否有锈蚀、松动、断裂等异常,并及时排除加固。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施设备',
-    checkStandard: '16.飞机等露天大型设施设备是否按要求采取系流等固定措施。',
-    checkMethod: '现场、统计资料',
+    inspectItem: '各类设施设备',
+    inspectStandard: '16.飞机等露天大型设施设备是否按要求采取系流等固定措施。',
+    inspectMethod: '现场、统计资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '周边环境',
-    checkStandard:
+    inspectItem: '周边环境',
+    inspectStandard:
       '1.台风来临前48小时前开始对建筑物、构筑物(仓库、车间为重点)周围排水设施进行检查,做好清淤疏通作业,保证排水设施可靠运行。',
-    checkMethod: '现场记录',
+    inspectMethod: '现场记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '周边环境',
-    checkStandard: '2.台风来临前24小时开始对电子主播设备的排水设备进行检查,做好清淤疏通作业,保证排水设施可靠运行。',
-    checkMethod: '现场记录',
+    inspectItem: '周边环境',
+    inspectStandard: '2.台风来临前24小时开始对电子主播设备的排水设备进行检查,做好清淤疏通作业,保证排水设施可靠运行。',
+    inspectMethod: '现场记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '周边环境',
-    checkStandard: '3.排水困难的地势低洼区域、重点区域、重点设备附近安置排水泵,做好应急准备。',
-    checkMethod: '现场记录',
+    inspectItem: '周边环境',
+    inspectStandard: '3.排水困难的地势低洼区域、重点区域、重点设备附近安置排水泵,做好应急准备。',
+    inspectMethod: '现场记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '周边环境',
-    checkStandard: '4.检查区域内指示牌、广告牌文采是否牢固,必要时做好拆除处理。',
-    checkMethod: '现场记录',
+    inspectItem: '周边环境',
+    inspectStandard: '4.检查区域内指示牌、广告牌文采是否牢固,必要时做好拆除处理。',
+    inspectMethod: '现场记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '周边环境',
-    checkStandard: '5.已倾倒的移动指示牌、栏杆等放倒处理。',
-    checkMethod: '现场记录',
+    inspectItem: '周边环境',
+    inspectStandard: '5.已倾倒的移动指示牌、栏杆等放倒处理。',
+    inspectMethod: '现场记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '周边环境',
-    checkStandard: '6.外场物品堆放高度要安全符合要求。',
-    checkMethod: '现场记录',
+    inspectItem: '周边环境',
+    inspectStandard: '6.外场物品堆放高度要安全符合要求。',
+    inspectMethod: '现场记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '周边环境',
-    checkStandard: '7.过高的物品要降低码放高度。',
-    checkMethod: '现场记录',
+    inspectItem: '周边环境',
+    inspectStandard: '7.过高的物品要降低码放高度。',
+    inspectMethod: '现场记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '周边环境',
-    checkStandard: '8.物资防雨措施落实到位。',
-    checkMethod: '现场记录',
+    inspectItem: '周边环境',
+    inspectStandard: '8.物资防雨措施落实到位。',
+    inspectMethod: '现场记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '通讯报讯',
-    checkStandard: '1.通讯是否发生过故障,讯情、灾情是否按规定上报或汇报。',
-    checkMethod: '查报告、记录',
+    inspectItem: '通讯报讯',
+    inspectStandard: '1.通讯是否发生过故障,讯情、灾情是否按规定上报或汇报。',
+    inspectMethod: '查报告、记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '通讯报讯',
-    checkStandard: '2.通信设备、电气设备是否遭受雷击等自然灾害的影响。',
-    checkMethod: '查报告、记录',
+    inspectItem: '通讯报讯',
+    inspectStandard: '2.通信设备、电气设备是否遭受雷击等自然灾害的影响。',
+    inspectMethod: '查报告、记录',
     compliance: null,
     actualSituation: '',
   },
@@ -366,79 +366,79 @@ export const MIDTERM_TEMPLATE_DATA = [
 
 export const LATER_TEMPLATE_DATA = [
   {
-    checkItem: '防台汛管理',
-    checkStandard: '1.各级防台汛机构值班、防台汛检查是否有效。',
-    checkMethod: '台账记录',
+    inspectItem: '防台汛管理',
+    inspectStandard: '1.各级防台汛机构值班、防台汛检查是否有效。',
+    inspectMethod: '台账记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '防台汛管理',
-    checkStandard: '2.历次防台汛检查发现的主要问题是否整改完成,或采取措施。',
-    checkMethod: '台账记录',
+    inspectItem: '防台汛管理',
+    inspectStandard: '2.历次防台汛检查发现的主要问题是否整改完成,或采取措施。',
+    inspectMethod: '台账记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '防台汛管理',
-    checkStandard: '3.开展几次防台汛演练,分别是什么预案或专业的演练。',
-    checkMethod: '台账记录',
+    inspectItem: '防台汛管理',
+    inspectStandard: '3.开展几次防台汛演练,分别是什么预案或专业的演练。',
+    inspectMethod: '台账记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施',
-    checkStandard: '1.建筑、仓库、厂房是否存在漏雨、损坏等情况;排水设施在台汛期是否出现过异常;异常事件简述。',
-    checkMethod: '现场资料',
+    inspectItem: '各类设施',
+    inspectStandard: '1.建筑、仓库、厂房是否存在漏雨、损坏等情况;排水设施在台汛期是否出现过异常;异常事件简述。',
+    inspectMethod: '现场资料',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施',
-    checkStandard: '2.建筑、仓库、厂房排水系统在台汛期是否出现过堵塞,堵塞事件简述。',
-    checkMethod: '记录',
+    inspectItem: '各类设施',
+    inspectStandard: '2.建筑、仓库、厂房排水系统在台汛期是否出现过堵塞,堵塞事件简述。',
+    inspectMethod: '记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施',
-    checkStandard: '3.是否发生过局部或全单位范围停电现象,其发生过几次及主要原因。',
-    checkMethod: '记录',
+    inspectItem: '各类设施',
+    inspectStandard: '3.是否发生过局部或全单位范围停电现象,其发生过几次及主要原因。',
+    inspectMethod: '记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '各类设施',
-    checkStandard: '4.重点建筑、厂房、仓库防雷接地装置是否出现故障,共几次及主要原因。',
-    checkMethod: '记录',
+    inspectItem: '各类设施',
+    inspectStandard: '4.重点建筑、厂房、仓库防雷接地装置是否出现故障,共几次及主要原因。',
+    inspectMethod: '记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '通讯报讯',
-    checkStandard: '1.通讯是否发生过故障;讯情、灾情是否按规定上报或汇报。',
-    checkMethod: '台账记录',
+    inspectItem: '通讯报讯',
+    inspectStandard: '1.通讯是否发生过故障;讯情、灾情是否按规定上报或汇报。',
+    inspectMethod: '台账记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '通讯报讯',
-    checkStandard: '2.通信设备、电气设备是否遭受雷击等自然灾害的影响。',
-    checkMethod: '台账记录',
+    inspectItem: '通讯报讯',
+    inspectStandard: '2.通信设备、电气设备是否遭受雷击等自然灾害的影响。',
+    inspectMethod: '台账记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '其他',
-    checkStandard: '1.台汛期发生过什么紧急的防台防汛事件,对防台防汛造成什么影响。',
-    checkMethod: '记录',
+    inspectItem: '其他',
+    inspectStandard: '1.台汛期发生过什么紧急的防台防汛事件,对防台防汛造成什么影响。',
+    inspectMethod: '记录',
     compliance: null,
     actualSituation: '',
   },
   {
-    checkItem: '其他',
-    checkStandard: '2.是否及时启动过防台应急预案。',
-    checkMethod: '记录',
+    inspectItem: '其他',
+    inspectStandard: '2.是否及时启动过防台应急预案。',
+    inspectMethod: '记录',
     compliance: null,
     actualSituation: '',
   },
@@ -482,8 +482,8 @@ export const TASK_TEMPLATE_FOOTER = [
 ];
 export const TASK_TEMPLATE_MAIN_COLUMNS = [
   { prop: 'index', label: '序号', width: 60 },
-  { prop: 'checkItem', label: '检查项目', width: 225 },
-  { prop: 'checkStandard', label: '检查标准', width: 500 },
-  { prop: 'checkMethod', label: '检查方法', width: 225 },
+  { prop: 'inspectItem', label: '检查项目', width: 225 },
+  { prop: 'inspectStandard', label: '检查标准', width: 500 },
+  { prop: 'inspectMethod', label: '检查方法', width: 225 },
 ];
-export const MERGE_FIELDS = ['index', 'checkItem', 'checkMethod'];
+export const MERGE_FIELDS = ['index', 'inspectItem', 'inspectMethod'];

+ 5 - 3
src/views/disaster/disaster-precaution/src/type.ts

@@ -2,11 +2,13 @@
  * 合并表格数据类型
  */
 export interface SpanTableData {
-  checkItem: string;
-  checkStandard: string;
-  checkMethod: string;
+  id: number;
+  inspectItem: string;
+  inspectStandard: string;
+  inspectMethod: string;
   compliance: number | null;
   actualSituation: string;
+  inspectImages: string[];
   index?: number;
   [key: string]: any;
 }

+ 3 - 3
src/views/disaster/disaster-precaution/src/utils/format-data.ts

@@ -5,14 +5,14 @@ const formatSpanTableData = (data: SpanTableData[]) => {
 
   // 找出不同的检查项目
   data.forEach((item) => {
-    if (!uniqueItems.includes(item.checkItem)) {
-      uniqueItems.push(item.checkItem);
+    if (!uniqueItems.includes(item.inspectItem)) {
+      uniqueItems.push(item.inspectItem);
     }
   });
 
   // 重新组织数据,添加序号
   uniqueItems.forEach((item, index) => {
-    const itemData = data.filter((dataItem) => dataItem.checkItem === item);
+    const itemData = data.filter((dataItem) => dataItem.inspectItem === item);
     itemData.forEach((data) => {
       data.index = index + 1;
     });

+ 2 - 0
src/views/disaster/hooks/userInfo.ts

@@ -8,10 +8,12 @@ const { getUserInfo } = storeToRefs(userStore);
  */
 export const useUserInfoHook = () => {
   const userInfo = getUserInfo.value;
+  const staffNo = userInfo.staffNo;
   const realname = userInfo.realname;
   const permissions = userInfo.permissions;
   return {
     realname,
     permissions,
+    staffNo,
   };
 };