xiaweibo 3 meses atrás
pai
commit
30c948590a
16 arquivos alterados com 2662 adições e 239 exclusões
  1. 320 2
      src/api/evaluationSystem/index.ts
  2. 14 1
      src/views/production-safety/safetyAssessment/evaluationDepartment/EvaluationDepartmentItem.vue
  3. 424 0
      src/views/production-safety/safetyAssessment/evaluationDepartment/components/EvaluationDepartmentAdvancedPerson.vue
  4. 227 47
      src/views/production-safety/safetyAssessment/evaluationDepartment/components/EvaluationDepartmentFeedback.vue
  5. 1 0
      src/views/production-safety/safetyAssessment/evaluationDepartment/configs/tables.ts
  6. 160 50
      src/views/production-safety/safetyAssessment/evaluationDepartment/evaluationDepartment.vue
  7. 20 4
      src/views/production-safety/safetyAssessment/evaluationSystem/EvaluationSystemItem.vue
  8. 124 16
      src/views/production-safety/safetyAssessment/evaluationSystem/components/EvaluationSystemDetail.vue
  9. 498 0
      src/views/production-safety/safetyAssessment/evaluationSystem/components/EvaluationSystemFeedback.vue
  10. 546 86
      src/views/production-safety/safetyAssessment/evaluationSystem/components/EvaluationTarget.vue
  11. 1 1
      src/views/production-safety/safetyAssessment/evaluationSystem/configs/tables.ts
  12. 115 2
      src/views/production-safety/safetyAssessment/evaluationSystem/configs/targetTables.ts
  13. 184 24
      src/views/production-safety/safetyAssessment/evaluationSystem/evaluationSystem.vue
  14. 22 4
      src/views/production-safety/safetyAssessment/pointDeduction/components/PointDeductionDetail.vue
  15. 1 0
      src/views/production-safety/safetyAssessment/pointDeduction/configs/form.ts
  16. 5 2
      src/views/production-safety/safetyAssessment/pointDeduction/pointDeduction.vue

+ 320 - 2
src/api/evaluationSystem/index.ts

@@ -11,6 +11,9 @@ export interface EvaluationContent {
   exProgram: string; // 考核项目
   exContent: string; // 考核内容
   scoringWay: string; // 评分方式
+  isAdd?: number; // 是否加分项(0-否,1-是)
+  reviewUserId?: number | null; // 复评人ID
+  reviewUserName?: string; // 复评人姓名
 }
 
 /**
@@ -119,9 +122,9 @@ export function querySecurityExamineDetail(id: number) {
  */
 export function deleteSecurityExamine(id: number) {
   return http.request({
-    url: `/securityExamine/admin/deleteSecurityExamine`,
+    url: `/securityExamine/admin/deleteSecurityExamine?id=${id}`,
     method: 'delete',
-    params: { id },
+    // params: { id },
   });
 }
 
@@ -154,3 +157,318 @@ export function updateSecurityExamine(data: UpdateSecurityExamineRequest) {
     data,
   });
 }
+
+/**
+ * 下发安全考核表请求参数
+ */
+export interface SaveSecurityExamineIssueRequest {
+  id: number;
+  deptNames: string; // 部门名称(逗号分隔)
+  deptIds: number; // 部门ID(单个)
+  getUserGroupId?: number; // 分组ID
+  deptSelfApproveUserId?: number; // 部门自评审核人ID
+  planStartTime?: string; // 计划开始时间
+  planEndTime?: string; // 计划结束时间
+}
+
+/**
+ * 下发安全考核表
+ */
+export function saveSecurityExamineIssue(data: SaveSecurityExamineIssueRequest) {
+  return http.request({
+    url: '/securityExamine/admin/saveSecurityExamineIssue',
+    method: 'post',
+    data,
+  });
+}
+
+/**
+ * 考核对象评分项
+ */
+export interface EvaluationScoreItem {
+  id: number;
+  psemId: number;
+  psedId: number;
+  pseiId: number;
+  serialNum: number;
+  exProgram: string; // 考核项目
+  exContent: string; // 考核内容
+  scoringWay: string; // 评分方式
+  selfScore: number; // 自评分数
+  attachments: string; // 附件
+  reviewScore: number; // 审核分数
+  isAdd?: number; // 是否加分项(0-否,1-是)
+  createdAt: string;
+  updatedAt: string;
+  isDeleted: number;
+}
+
+/**
+ * 考核对象列表项
+ */
+export interface EvaluationTargetItem {
+  id: number;
+  psemId: number; // 考核表ID
+  deptName: string; // 部门名称
+  exName: string; // 考核表名称
+  attachments: string; // 考核文档
+  planEndTime: string; // 计划结束时间
+  deptId: number; // 部门ID
+  status: number; // 状态(0-未下发/2-待反馈/3-待评分/4-待审核/5-已作废/1-已完成)
+  statusName: string; // 状态名称
+  reviewRejectReson?: string; // 审核拒绝原因
+  approveRejectReson?: string; // 审批拒绝原因
+  deptUserId?: number; // 部门负责人ID
+  deptUserName?: string; // 部门负责人姓名
+  deptUserLink?: string; // 部门负责人联系方式
+  reviewUserId?: number; // 审核人ID
+  reviewUserName?: string; // 审核人姓名
+  createdAt: string;
+  updatedAt: string;
+  isDeleted: number;
+  scores?: EvaluationScoreItem[]; // 评分详情
+  scoreRank?: number; // 排名
+}
+
+/**
+ * 查询考核对象列表请求参数(queryParam)
+ */
+export interface QuerySecurityExamineIssueQueryParam {
+  psemId?: number; // 考核表ID(从路由参数获取)
+  planStartTime?: string; // 计划开始时间
+  planEndTime?: string; // 计划结束时间
+  status?: number; // 状态(0-未下发/2-待反馈/3-待评分/4-待审核/5-已作废/1-已完成)
+  deptName?: string; // 部门名称(考核对象)
+}
+
+/**
+ * 查询考核对象列表请求参数
+ */
+export interface QuerySecurityExamineIssueParams {
+  pageNumber: number; // 页号
+  pageSize: number; // 每页数量
+  queryParam: QuerySecurityExamineIssueQueryParam; // 查询参数
+}
+
+/**
+ * 查询考核对象列表
+ */
+export function querySecurityExamineIssue(params: QuerySecurityExamineIssueParams) {
+  return http.request<QueryPageResponse<EvaluationTargetItem>>({
+    url: '/securityExamine/admin/querySecurityExamineIssue',
+    method: 'post',
+    data: params,
+  });
+}
+
+/**
+ * 作废安全考核表
+ */
+export function updateSecurityExamineRepeal(id: number) {
+  return http.request({
+    url: `/securityExamine/admin/updateSecurityExamineRepeal?id=${id}`,
+    method: 'put',
+  });
+}
+
+/**
+ * 作废考核对象
+ */
+export function updateSecurityExamineIssueRepeal(id: number) {
+  return http.request({
+    url: `/securityExamine/admin/updateSecurityExamineIssueRepeal?id=${id}`,
+    method: 'put',
+  });
+}
+
+/**
+ * 删除考核对象
+ */
+export function deleteSecurityExamineIssue(id: number) {
+  return http.request({
+    url: `/securityExamine/admin/deleteSecurityExamineIssue?id=${id}`,
+    method: 'delete',
+  });
+}
+
+/**
+ * 查询部门考核列表请求参数(queryParam)
+ */
+export interface QuerySecurityExamineDeptQueryParam {
+  exName?: string; // 考核表名称
+  status?: number; // 状态
+  deptName?: string; // 部门名称
+  planStartTime?: string; // 计划开始时间
+  planEndTime?: string; // 计划结束时间
+}
+
+/**
+ * 查询部门考核列表请求参数
+ */
+export interface QuerySecurityExamineDeptParams {
+  pageNumber: number; // 页号
+  pageSize: number; // 每页数量
+  queryParam: QuerySecurityExamineDeptQueryParam; // 查询参数
+}
+
+/**
+ * 查询部门考核列表
+ */
+export function querySecurityExamineDept(params: QuerySecurityExamineDeptParams) {
+  return http.request<QueryPageResponse<EvaluationTargetItem>>({
+    url: '/securityExamine/dept/querySecurityExamineDept',
+    method: 'post',
+    data: params,
+  });
+}
+
+/**
+ * 查询部门考核详情
+ */
+export function querySecurityExamineIssueDeptDetail(id: number) {
+  return http.request<EvaluationTargetItem>({
+    url: `/securityExamine/dept/querySecurityExamineIssueDeptDetail?id=${id}`,
+    method: 'get',
+  });
+}
+
+/**
+ * 查询管理员端考核对象详情(用于评分)
+ */
+export function querySecurityExamineIssueDetail(id: number) {
+  return http.request<EvaluationTargetItem>({
+    url: `/securityExamine/admin/querySecurityExamineIssueDetail?id=${id}`,
+    method: 'get',
+  }); 
+}
+
+/**
+ * 提交部门考核反馈请求参数
+ */
+export interface UpdateSecurityExamineDeptSubmitParams {
+  id: number; // 考核表ID (psemId)
+  scores: Array<{
+    id?: number; // 评分项ID
+    selfScore: number; // 自评得分
+  }>;
+}
+
+/**
+ * 提交部门考核反馈
+ */
+export function updateSecurityExamineDeptSubmit(params: UpdateSecurityExamineDeptSubmitParams) {
+  return http.request({
+    url: '/securityExamine/dept/updateSecurityExamineDeptSubmit',
+    method: 'put',
+    data: params,
+  });
+}
+
+/**
+ * 导出部门考核详情
+ */
+export function exportSecurityExamineIssueDeptDetail(id: number) {
+  return http.request({
+    url: '/securityExamine/dept/exportSecurityExamineIssueDeptDetail',
+    method: 'post',
+    data: { id },
+    responseType: 'blob',
+  });
+}
+
+/**
+ * 导入部门考核详情
+ */
+export function importSecurityExamineIssueDeptDetail(params: { id: number; file: File }) {
+  const formData = new FormData();
+  formData.append('id', String(params.id));
+  formData.append('file', params.file);
+  return http.request({
+    url: '/securityExamine/dept/importSecurityExamineIssueDeptDetail',
+    method: 'post',
+    data: formData,
+    headers: {
+      'Content-Type': 'multipart/form-data',
+    },
+  });
+}
+
+/**
+ * 管理员端复核提交
+ */
+export function updateSecurityExamineIssueReviewSubmit(params: UpdateSecurityExamineDeptSubmitParams) {
+  return http.request({
+    url: '/securityExamine/admin/updateSecurityExamineIssueReviewSubmit',
+    method: 'put',
+    data: params,
+  });
+}
+
+/**
+ * 管理员端复核通过(提交)
+ */
+export function updateSecurityExamineIssueReviewAgree(params: UpdateSecurityExamineDeptSubmitParams) {
+  return http.request({
+    url: '/securityExamine/admin/updateSecurityExamineIssueReviewAgree',
+    method: 'put',
+    data: params,
+  });
+}
+
+/**
+ * 管理员端复核不通过
+ */
+export function updateSecurityExamineIssueReviewDisagree(id: number, reviewRejectReson?: string) {
+  return http.request({
+    url: '/securityExamine/admin/updateSecurityExamineIssueReviewDisagree',
+    method: 'put',
+    data: {
+      id,
+      reviewRejectReson,
+    },
+  });
+}
+
+/**
+ * 管理员端审核通过
+ */
+export function updateSecurityExamineIssueApproveAgree(id: number) {
+  return http.request({
+    url: `/securityExamine/admin/updateSecurityExamineIssueApproveAgree?id=${id}`,
+    method: 'put',
+  });
+}
+
+/**
+ * 管理员端审核不通过
+ */
+export function updateSecurityExamineIssueApproveDisagree(id: number, approveRejectReson?: string) {
+  return http.request({
+    url: '/securityExamine/admin/updateSecurityExamineIssueApproveDisagree',
+    method: 'put',
+    data: {
+      id,
+      approveRejectReson,
+    },
+  });
+}
+
+/**
+ * 查询先进集体
+ */
+export interface QuerySecurityExamineIssueAdvancedParams extends QueryPageRequest<any> {
+  queryParam: {
+    psemId?: number;
+    deptName?: string;
+    planStartTime?: string;
+    planEndTime?: string;
+  };
+}
+
+export function querySecurityExamineIssueAdvanced(params: QuerySecurityExamineIssueAdvancedParams) {
+  return http.request<QueryPageResponse<EvaluationTargetItem>>({
+    url: '/securityExamine/admin/querySecurityExamineIssueAdvanced',
+    method: 'post',
+    data: params,
+  });
+}

+ 14 - 1
src/views/production-safety/safetyAssessment/evaluationDepartment/EvaluationDepartmentItem.vue

@@ -2,7 +2,7 @@
   <div class="safety-platform-container">
     <header class="safety-platform-container__header">
       <BreadcrumbBack />
-      <div class="breadcrumb-title">{{ headerTitle }}</div>
+      <span class="breadcrumb-title">{{ headerTitle }}</span>
     </header>
     <component :is="dynamicComponent" :id="id" />
   </div>
@@ -21,6 +21,9 @@
     if (operate.value === 'evaluationDepartment-feedback') {
       return '反馈';
     }
+    if (operate.value === 'evaluationDepartment-advanced-person') {
+      return '先进个人信息';
+    }
     return '安全考核管理(部门)详情';
   });
 
@@ -28,10 +31,20 @@
     if (operate.value === 'evaluationDepartment-feedback') {
       return defineAsyncComponent(() => import('./components/EvaluationDepartmentFeedback.vue'));
     }
+    if (operate.value === 'evaluationDepartment-advanced-person') {
+      return defineAsyncComponent(() => import('./components/EvaluationDepartmentAdvancedPerson.vue'));
+    }
     return null;
   });
 </script>
 
 <style scoped lang="scss">
   @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+
+  .safety-platform-container__header {
+    flex-direction: row !important;
+    justify-content: flex-start !important;
+    gap: 8px !important;
+  }
 </style>

+ 424 - 0
src/views/production-safety/safetyAssessment/evaluationDepartment/components/EvaluationDepartmentAdvancedPerson.vue

@@ -0,0 +1,424 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="evaluation-header">
+        <h1 class="evaluation-title">{{ evaluationDetail.exName || '先进个人信息' }}</h1>
+        <div class="evaluation-meta">
+          <span>考核部门: {{ evaluationDetail.deptNames || '-' }}</span>
+          <span>创建人: {{ evaluationDetail.createdUserName || '-' }}</span>
+          <span>创建时间: {{ formatDateTime(evaluationDetail.createdAt) || '-' }}</span>
+        </div>
+      </div>
+    </header>
+    <main class="safety-platform-container__main">
+      <div class="search-table-container">
+        <!-- tabs 放在搜索条件上面 -->
+        <div class="header-tabs-wrapper">
+          <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+            <el-tab-pane label="先进个人信息" name="ALL" />
+          </el-tabs>
+        </div>
+
+        <header>
+          <div class="act-search">
+            <section class="select-box">
+              <!-- 状态筛选仅在"全部"tab 下显示(包含文字和下拉框) -->
+              <div class="select-box--item" v-if="activeTab === 'ALL'">
+                <span>状态:</span>
+                <el-select
+                  v-model="tableQuery.queryParam.status"
+                  placeholder="请选择状态"
+                  clearable
+                >
+                  <el-option
+                    v-for="item in EVALUATION_SYSTEM_STATUS_OPTIONS"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  />
+                </el-select>
+              </div>
+              <div class="select-box--item">
+                <span>考核对象:</span>
+                <el-cascader
+                  ref="targetDeptCascaderRef"
+                  v-model="targetDeptId"
+                  :options="deptTree"
+                  :props="cascaderDeptProp"
+                  :show-all-levels="false"
+                  placeholder="请选择考核对象部门"
+                  filterable
+                  @change="handleTargetDeptChange"
+                />
+              </div>
+              <div class="select-box--item">
+                <span>时间范围:</span>
+                <el-date-picker
+                  v-model="tableQuery.queryParam.dateRange"
+                  type="daterange"
+                  range-separator="至"
+                  start-placeholder="开始日期"
+                  end-placeholder="结束日期"
+                  value-format="YYYY-MM-DD"
+                  format="YYYY-MM-DD"
+                />
+              </div>
+            </section>
+            <section class="search-btn">
+              <el-button type="primary" @click="handleSearch">查询</el-button>
+              <el-button @click="handleReset">重置</el-button>
+              <el-button plain class="search-table-container--button" @click="handleExport">
+                导出
+              </el-button>
+            </section>
+          </div>
+        </header>
+
+        <div class="batch-table">
+          <BasicTable
+            ref="basicTableRef"
+            :tableData="tableData"
+            :tableConfig="tableConfig"
+            @update:pageSize="handleSizeChange"
+            @update:pageNumber="handleCurrentChange"
+          >
+            <template #status="scope">
+              <span>
+                {{ EVALUATION_SYSTEM_STATUS_LABEL[String(scope.row.status)] || '-' }}
+              </span>
+            </template>
+            <template #evaluationDocument="scope">
+              <div
+                class="file-container--div"
+                v-for="item in parseAttachments(scope.row.evaluationDocument)"
+                :key="item.fileUrl"
+              >
+                <img
+                  class="file-container--div__icon"
+                  @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
+                  :src="FILE_TYPE_ICON[item.fileType]"
+                />
+                <span
+                  class="file-container--div__name"
+                  @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
+                  >{{ item.fileName }}</span
+                >
+                <img
+                  class="file-container--div__download"
+                  :src="DownloadIcon"
+                  @click="downloadFile(item.fileUrl, item.fileName)"
+                />
+              </div>
+            </template>
+          </BasicTable>
+        </div>
+      </div>
+    </main>
+    <PreviewOnline ref="previewOnlineRef" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { computed, onMounted, reactive, ref, watch } from 'vue';
+  import { useRoute } from 'vue-router';
+  import BasicTable from '@/components/BasicTable.vue';
+  import useTableConfig from '@/hooks/useTableConfigHook';
+  import { TABLE_OPTIONS } from '../../evaluationSystem/configs/tables';
+  import { EVALUATION_ADVANCED_PERSON_TABLE_COLUMNS } from '../../evaluationSystem/configs/targetTables';
+  import { EVALUATION_SYSTEM_STATUS_OPTIONS, EVALUATION_SYSTEM_STATUS_LABEL } from '../../evaluationSystem/configs/status';
+  import type { QueryPageRequest } from '@/types/basic-query';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import type { DeptTree } from '@/types/dept/type';
+  import {
+    querySecurityExamineIssue,
+    querySecurityExamineIssueDeptDetail,
+  } from '@/api/evaluationSystem';
+  import type { QuerySecurityExamineIssueParams, EvaluationTargetItem } from '@/api/evaluationSystem';
+  import DownloadIcon from '@/views/disaster/disaster-control/src/svg/download.svg';
+  import { downloadFile } from '@/views/disaster/utils';
+  import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
+  import { FILE_TYPE_ICON } from '@/components/UploadFiles/constants';
+
+  const route = useRoute();
+
+  // tabs(ALL:全部,其它为对应状态码字符串)
+  const activeTab = ref<'ALL' | '0' | '2' | '3' | '4' | '5' | '1'>('ALL');
+
+  // 表格
+  const basicTableRef = ref<InstanceType<typeof BasicTable>>();
+
+  const { tableConfig, pagination } = useTableConfig(EVALUATION_ADVANCED_PERSON_TABLE_COLUMNS, TABLE_OPTIONS);
+
+  const tableData = ref<any[]>([]);
+
+  // 部门树(复用下发考核表的部门下拉逻辑)
+  const targetDeptCascaderRef = ref();
+  const deptTree = ref<DeptTree[]>([]);
+  const targetDeptId = ref<number | null>(null);
+  const cascaderDeptProp = {
+    checkStrictly: true,
+    expandTrigger: 'hover' as const,
+    value: 'id',
+    label: 'deptName',
+    emitPath: false,
+  };
+
+  const tableQuery = reactive<QueryPageRequest<any>>({
+    pageNumber: pagination.pageNumber,
+    pageSize: pagination.pageSize,
+    queryParam: {
+      status: '',
+      target: '',
+      dateRange: null as any,
+      startTime: '',
+      endTime: '',
+    },
+  });
+
+  // 从路由获取考核对象ID(psemId)
+  const evaluationId = computed(() => {
+    const id = route.query.id;
+    return id ? Number(id) : undefined;
+  });
+
+  // 考核表详情
+  const evaluationDetail = ref<Partial<EvaluationTargetItem>>({});
+
+  const handleSizeChange = (value: number) => {
+    pagination.pageSize = value;
+    tableQuery.pageSize = value;
+    getTableData();
+  };
+
+  const handleCurrentChange = (value: number) => {
+    pagination.pageNumber = value;
+    tableQuery.pageNumber = value;
+    getTableData();
+  };
+
+  async function getTableData() {
+    tableConfig.loading = true;
+    try {
+      const params: QuerySecurityExamineIssueParams = {
+        pageNumber: tableQuery.pageNumber,
+        pageSize: tableQuery.pageSize,
+        queryParam: {
+          psemId: evaluationId.value, // 考核表ID(从路由参数获取)
+          planStartTime: tableQuery.queryParam.startTime || undefined,
+          planEndTime: tableQuery.queryParam.endTime || undefined,
+          deptName: tableQuery.queryParam.target || undefined,
+          status:
+            tableQuery.queryParam.status !== '' && tableQuery.queryParam.status !== null
+              ? Number(tableQuery.queryParam.status)
+              : undefined,
+        },
+      };
+
+      const res = await querySecurityExamineIssue(params);
+      if (res) {
+        // 映射返回数据字段到表格字段(先进个人信息)
+        tableData.value = res.records.map((item) => ({
+          id: item.id,
+          employeeCode: item.deptUserId || '-', // 员工工号(使用部门用户ID)
+          employeeName: item.deptUserName || '-', // 员工姓名(使用部门用户名)
+          deptName: item.deptName || '-', // 所属部门
+          contactPhone: item.deptUserLink || '-', // 联系方式
+          departmentLeader: item.deptUserName || '-', // 部门负责人(暂时使用部门用户名)
+          remark: '-', // 备注(接口暂无此字段)
+          evaluationDocument: item.attachments, // 考核文档
+          plannedCompletionTime: item.planEndTime || '-', // 计划完成时间
+          status: item.status, // 状态
+          statusName: item.statusName, // 状态名称
+          // 保留原始数据,供其他操作使用
+          psemId: item.psemId,
+          deptId: item.deptId,
+          scores: item.scores,
+          scoreRank: item.scoreRank,
+        }));
+        pagination.total = res.totalRow;
+      }
+    } catch (e) {
+      console.error('获取先进个人信息列表失败:', e);
+      tableData.value = [];
+      pagination.total = 0;
+    } finally {
+      tableConfig.loading = false;
+    }
+  }
+
+  const getDeptTreeData = async () => {
+    try {
+      const res = await getAllDepartments();
+      deptTree.value = res?.[0]?.children ?? [];
+    } catch (e) {
+      console.error('获取部门树失败:', e);
+    }
+  };
+
+  const handleTargetDeptChange = () => {
+    const nodes = targetDeptCascaderRef.value?.getCheckedNodes?.();
+    tableQuery.queryParam.target = nodes?.[0]?.label ?? '';
+  };
+
+  function handleSearch() {
+    if (tableQuery.queryParam.dateRange && tableQuery.queryParam.dateRange.length === 2) {
+      tableQuery.queryParam.startTime = tableQuery.queryParam.dateRange[0];
+      tableQuery.queryParam.endTime = tableQuery.queryParam.dateRange[1];
+    } else {
+      tableQuery.queryParam.startTime = '';
+      tableQuery.queryParam.endTime = '';
+    }
+    pagination.pageNumber = 1;
+    tableQuery.pageNumber = 1;
+    getTableData();
+  }
+
+  const handleReset = () => {
+    tableQuery.queryParam.status = '';
+    tableQuery.queryParam.target = '';
+    tableQuery.queryParam.dateRange = null;
+    targetDeptId.value = null;
+    handleSearch();
+  };
+
+  const handleTabChange = (name: string | number) => {
+    if (name === 'ALL') {
+      tableQuery.queryParam.status = '';
+    } else {
+      tableQuery.queryParam.status = Number(name);
+    }
+    handleSearch();
+  };
+
+  const handleExport = () => {
+    // TODO: 导出当前筛选结果
+    console.log('export advanced person list', tableQuery);
+  };
+
+  // 预览
+  const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
+  const previewOnline = (url: string | undefined, type: keyof typeof FILE_TYPE_ICON) => {
+    if (url) {
+      previewOnlineRef.value?.open(url, type);
+    }
+  };
+
+  // 解析逗号分隔的URL字符串为文件列表
+  const parseAttachments = (attachmentsStr: string | undefined): Array<{
+    fileUrl: string;
+    fileName: string;
+    fileType: string;
+  }> => {
+    if (!attachmentsStr || !attachmentsStr.trim()) {
+      return [];
+    }
+
+    // 按逗号分割URL
+    const urls = attachmentsStr.split(',').map(url => url.trim()).filter(url => url);
+
+    return urls.map((url) => {
+      // 从URL中提取文件名
+      const urlParts = url.split('/');
+      const fileName = urlParts[urlParts.length - 1] || '未知文件';
+
+      // 根据文件扩展名判断文件类型
+      const extension = fileName.split('.').pop()?.toLowerCase() || '';
+      let fileType = 'pdf';
+      if (extension === 'doc' || extension === 'docx') {
+        fileType = 'word';
+      } else if (extension === 'xls' || extension === 'xlsx') {
+        fileType = 'excel';
+      } else if (extension === 'ppt' || extension === 'pptx') {
+        fileType = 'ppt';
+      }
+
+      return {
+        fileUrl: url,
+        fileName,
+        fileType,
+      };
+    });
+  };
+
+  // 格式化日期时间
+  const formatDateTime = (dateTimeStr: string | undefined): string => {
+    if (!dateTimeStr) return '';
+    try {
+      const date = new Date(dateTimeStr);
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      const hours = String(date.getHours()).padStart(2, '0');
+      const minutes = String(date.getMinutes()).padStart(2, '0');
+      const seconds = String(date.getSeconds()).padStart(2, '0');
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    } catch (e) {
+      return dateTimeStr;
+    }
+  };
+
+  // 获取考核表详情(使用考核对象ID,即psemId)
+  const getEvaluationDetail = async () => {
+    // 路由参数中的id是考核对象ID(psemId),直接使用
+    if (!evaluationId.value) return;
+    
+    try {
+      const res = await querySecurityExamineIssueDeptDetail(evaluationId.value);
+      if (res) {
+        evaluationDetail.value = {
+          exName: res.exName,
+          deptNames: res.deptName, // 部门端接口返回的是 deptName,不是 deptNames
+          createdUserName: res.deptUserName || '-', // 部门端可能没有创建人字段,使用部门用户名
+          createdAt: res.createdAt,
+        };
+      }
+    } catch (e) {
+      console.error('获取考核表详情失败:', e);
+    }
+  };
+
+  onMounted(() => {
+    getDeptTreeData();
+    getTableData();
+    getEvaluationDetail();
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+  @use '@/styles/basic-table-file.scss' as *;
+  @use '@/views/traffic/violation/style/act-search-table.scss' as *;
+
+  .safety-platform-container__header {
+    padding-bottom: 0 !important;
+  }
+
+  .evaluation-header {
+    width: 100%;
+  }
+
+  .evaluation-title {
+    font-size: 20px;
+    font-weight: bold;
+    margin: 0 0 12px 0;
+    color: #333;
+  }
+
+  .evaluation-meta {
+    margin-bottom: 12px;
+    display: flex;
+    gap: 24px;
+    font-size: 14px;
+    color: #666;
+  }
+
+  .evaluation-meta span {
+    white-space: nowrap;
+  }
+
+  .header-tabs-wrapper {
+    margin-bottom: 16px;
+  }
+</style>

+ 227 - 47
src/views/production-safety/safetyAssessment/evaluationDepartment/components/EvaluationDepartmentFeedback.vue

@@ -2,50 +2,51 @@
   <main class="safety-platform-container__main">
     <el-form ref="formRef" :model="ruleFormData" :rules="formRules" label-width="auto" class="evaluation-form">
       <el-form-item label="考核信息标题:" prop="evaluationTitle">
-        <el-input v-model="ruleFormData.evaluationTitle" placeholder="请输入考核信息标题" />
+        <el-input v-model="ruleFormData.evaluationTitle" placeholder="请输入考核信息标题" disabled />
       </el-form-item>
       <el-form-item label="上传附件文档:" prop="attachmentDocument">
-        <UploadFiles label="上传附件" :file-list="ruleFormData.attachmentDocument" @uploadSuccess="handleUploadSuccess" />
+        <div class="upload-files-disabled">
+          <UploadFiles label="上传附件" :file-list="ruleFormData.attachmentDocument" @uploadSuccess="handleUploadSuccess" />
+        </div>
       </el-form-item>
       <el-form-item label="评分说明:" prop="scoringDescription">
-        <el-input v-model="ruleFormData.scoringDescription" type="textarea" :rows="5" placeholder="请输入评分说明" />
+        <el-input v-model="ruleFormData.scoringDescription" type="textarea" :rows="5" placeholder="请输入评分说明" disabled />
       </el-form-item>
     </el-form>
 
     <div class="evaluation-items-section">
       <div class="section-header">
-        <el-button plain @click="handleDownloadTemplate">模板下载</el-button>
+        <el-button plain @click="handleDownloadTemplate" disabled>模板下载</el-button>
         <el-button plain @click="handleImport">导入</el-button>
         <el-button plain @click="handleExport">导出</el-button>
+        <input
+          ref="importFileInputRef"
+          type="file"
+          accept=".xlsx,.xls"
+          style="display: none"
+          @change="handleFileChange"
+        />
       </div>
       <div class="evaluation-items-table">
-        <el-table :data="evaluationItems" border>
+        <el-table :data="evaluationItems" border show-summary :summary-method="getSummaries">
           <el-table-column label="编号" type="index" width="80" align="center" />
-          <el-table-column label="考核项目" min-width="150">
+          <el-table-column label="考核项目" prop="evaluationItem" min-width="150" />
+          <el-table-column label="考核内容" prop="evaluationContent" min-width="200" />
+          <el-table-column label="评分方式" prop="scoringMethod" min-width="150" />
+          <el-table-column label="加减分项" prop="scoreType" min-width="120" />
+          <el-table-column label="自评得分" prop="selfScore" min-width="180">
             <template #default="scope">
-              <el-input v-model="scope.row.evaluationItem" placeholder="请输入考核项目" />
-            </template>
-          </el-table-column>
-          <el-table-column label="考核内容" min-width="200">
-            <template #default="scope">
-              <el-input v-model="scope.row.evaluationContent" placeholder="请输入考核内容" />
-            </template>
-          </el-table-column>
-          <el-table-column label="评分方式" min-width="150">
-            <template #default="scope">
-              <el-input v-model="scope.row.scoringMethod" placeholder="请输入评分方式" />
-            </template>
-          </el-table-column>
-          <el-table-column label="自评得分" min-width="120">
-            <template #default="scope">
-              <el-input-number v-model="scope.row.selfScore" :min="0" placeholder="请输入自评得分" />
-            </template>
-          </el-table-column>
-          <el-table-column label="资料说明" min-width="200">
-            <template #default="scope">
-              <el-input v-model="scope.row.materialDescription" type="textarea" :rows="2" placeholder="请输入资料说明" />
+              <el-input-number
+                v-model="scope.row.selfScore"
+                :min="0"
+                :precision="0"
+                :step="1"
+                placeholder="请输入自评得分"
+                @blur="handleScoreBlur"
+              />
             </template>
           </el-table-column>
+          <el-table-column label="资料说明" prop="materialDescription" min-width="200" />
         </el-table>
       </div>
     </div>
@@ -62,6 +63,13 @@
   import { ElMessage } from 'element-plus';
   import type { FormInstance } from 'element-plus';
   import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
+  import {
+    querySecurityExamineIssueDeptDetail,
+    updateSecurityExamineDeptSubmit,
+    exportSecurityExamineIssueDeptDetail,
+    importSecurityExamineIssueDeptDetail,
+  } from '@/api/evaluationSystem';
+  import type { FileItem } from '@/components/UploadFiles/types';
 
   const props = defineProps<{
     id: number;
@@ -72,17 +80,56 @@
   const formRef = ref<FormInstance>();
   const ruleFormData = ref({
     evaluationTitle: '',
-    attachmentDocument: [],
+    attachmentDocument: [] as FileItem[],
     scoringDescription: '',
   });
 
   const formRules = {
     evaluationTitle: [{ required: true, message: '请输入考核信息标题', trigger: 'blur' }],
     attachmentDocument: [{ required: true, message: '请上传附件文档', trigger: 'change' }],
-    scoringDescription: [{ required: true, message: '请输入评分说明', trigger: 'blur' }],
+    // scoringDescription: [{ required: true, message: '请输入评分说明', trigger: 'blur' }],
   };
 
   const evaluationItems = ref<any[]>([]);
+  // 保存详情原始数据,用于提交
+  const detailData = ref<any>(null);
+
+  // 计算总计
+  const getTotalScore = () => {
+    return evaluationItems.value.reduce((sum, item) => {
+      return sum + (Number(item.selfScore) || 0);
+    }, 0);
+  };
+
+  // 自评得分失去焦点时触发(用于强制更新合计行)
+  const handleScoreBlur = () => {
+    // 触发响应式更新,让合计行重新计算
+    // 通过修改数组引用来触发更新
+    evaluationItems.value = [...evaluationItems.value];
+  };
+
+  // 表格合计行方法
+  const getSummaries = (param: any) => {
+    const { columns } = param;
+    const sums: string[] = [];
+    columns.forEach((column: any, index: number) => {
+      if (index === 0) {
+        // 编号列
+        sums[index] = '';
+      } else if (column.property === 'scoreType') {
+        // 加减分项列显示"总计:"
+        sums[index] = '总计:';
+      } else if (column.property === 'selfScore') {
+        // 自评得分列显示总分,即使为0也显示
+        const total = getTotalScore();
+        sums[index] = `${total}分`;
+      } else {
+        // 其他列显示空
+        sums[index] = '';
+      }
+    });
+    return sums;
+  };
 
   const handleValidate = async () => {
     if (!formRef.value) return;
@@ -102,38 +149,156 @@
     console.log('download template');
   };
 
+  // 导入文件引用
+  const importFileInputRef = ref<HTMLInputElement>();
+
   const handleImport = () => {
-    // TODO: 打开导入弹窗
-    console.log('import');
+    // 触发文件选择
+    importFileInputRef.value?.click();
   };
 
-  const handleExport = () => {
-    // TODO: 导出
-    console.log('export');
+  const handleFileChange = async (event: Event) => {
+    const target = event.target as HTMLInputElement;
+    const file = target.files?.[0];
+    if (!file) return;
+
+    try {
+      await importSecurityExamineIssueDeptDetail({
+        id: props.id,
+        file,
+      });
+      ElMessage.success('导入成功');
+      // 重新加载详情数据
+      await getDetail();
+    } catch (e: any) {
+      console.error('导入失败:', e);
+      ElMessage.error(e?.message || '导入失败,请重试');
+    } finally {
+      // 清空文件选择
+      if (target) {
+        target.value = '';
+      }
+    }
+  };
+
+  const handleExport = async () => {
+    try {
+      const blob = await exportSecurityExamineIssueDeptDetail(props.id);
+      // 创建下载链接
+      const url = window.URL.createObjectURL(blob);
+      const link = document.createElement('a');
+      link.href = url;
+      link.download = `部门考核详情_${new Date().getTime()}.xlsx`;
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+      window.URL.revokeObjectURL(url);
+      ElMessage.success('导出成功');
+    } catch (e: any) {
+      console.error('导出失败:', e);
+      ElMessage.error(e?.message || '导出失败,请重试');
+    }
+  };
+
+  // 将逗号分隔的 URL 字符串转换为 FileItem[] 格式
+  const parseAttachmentsToFileList = (attachmentsStr: string | undefined): FileItem[] => {
+    if (!attachmentsStr || !attachmentsStr.trim()) {
+      return [];
+    }
+
+    // 按逗号分割URL
+    const urls = attachmentsStr.split(',').map(url => url.trim()).filter(url => url);
+
+    return urls.map((url, index) => {
+      // 从URL中提取文件名
+      const urlParts = url.split('/');
+      const fileName = urlParts[urlParts.length - 1] || `文件${index + 1}`;
+
+      // 根据文件扩展名判断文件类型
+      const extension = fileName.split('.').pop()?.toLowerCase() || '';
+      let fileType = 'pdf';
+      if (extension === 'doc' || extension === 'docx') {
+        fileType = 'word';
+      } else if (extension === 'xls' || extension === 'xlsx') {
+        fileType = 'excel';
+      } else if (extension === 'ppt' || extension === 'pptx') {
+        fileType = 'ppt';
+      }
+
+      return {
+        fileId: index + 1,
+        fileName,
+        fileType,
+        fileSize: '0', // 接口未返回文件大小,使用默认值
+        fileUrl: url,
+      };
+    });
   };
 
   const getDetail = async () => {
-    // TODO: 根据 id 拉取详情填充表单
-    // const detail = await getEvaluationDepartmentDetail(props.id);
-    // if (!detail) return;
-    // ruleFormData.value.evaluationTitle = detail.evaluationTitle;
-    // ruleFormData.value.attachmentDocument = detail.attachmentDocument || [];
-    // ruleFormData.value.scoringDescription = detail.scoringDescription;
-    // evaluationItems.value = detail.evaluationItems || [];
-    console.log('fetch evaluation department detail', props.id);
+    try {
+      const detail = await querySecurityExamineIssueDeptDetail(props.id);
+      if (!detail) return;
+
+      // 保存原始详情数据,用于提交
+      detailData.value = detail;
+
+      // 映射表单字段
+      ruleFormData.value.evaluationTitle = detail.exName || ''; // 考核表名称
+      ruleFormData.value.attachmentDocument = parseAttachmentsToFileList(detail.attachments); // 附件文档
+      ruleFormData.value.scoringDescription = ''; // 评分说明(接口暂无此字段,留空)
+
+      // 映射考核项目列表(scores 数组)
+      if (detail.scores && detail.scores.length > 0) {
+        evaluationItems.value = detail.scores.map((score) => ({
+          id: score.id, // 保留评分项ID,用于提交
+          isAdd: score.isAdd !== undefined ? score.isAdd : (score.selfScore >= 0 ? 1 : 0), // 是否加分项(0-否,1-是)
+          isAddName: score.isAdd === 1 ? '加分项' : '减分项', // 加减分项名称
+          scoreType: score.isAdd === 1 ? '加分项' : '减分项', // 加减分项(用于显示,兼容旧字段)
+          evaluationItem: score.exProgram || '', // 考核项目
+          evaluationContent: score.exContent || '', // 考核内容
+          scoringMethod: score.scoringWay || '', // 评分方式
+          selfScore: score.selfScore || 0, // 自评得分
+          materialDescription: score.attachments || '', // 资料说明(使用附件字段)
+        }));
+      } else {
+        evaluationItems.value = [];
+      }
+    } catch (e) {
+      console.error('获取部门考核详情失败:', e);
+      ElMessage.error('获取详情失败,请重试');
+    }
   };
 
   const handleSubmit = async () => {
     const res = await handleValidate();
     if (!res) return;
     try {
-      // TODO: 调用反馈接口
-      // await submitEvaluationDepartmentFeedback({ id: props.id, ...ruleFormData.value, evaluationItems: evaluationItems.value });
-      console.log('submit evaluation department feedback', props.id, ruleFormData.value, evaluationItems.value);
+      if (!detailData.value) {
+        ElMessage.error('数据加载失败,请刷新后重试');
+        return;
+      }
+
+      // 使用详情原始数据,更新自评得分和isAdd字段
+      const submitData = {
+        ...detailData.value,
+        scores: detailData.value.scores?.map((score: any) => {
+          // 找到对应的自评得分和isAdd
+          const item = evaluationItems.value.find((item) => item.id === score.id);
+          return {
+            ...score,
+            selfScore: item ? Number(item.selfScore) || 0 : score.selfScore || 0,
+            isAdd: item ? (item.isAdd !== undefined ? item.isAdd : (item.selfScore >= 0 ? 1 : 0)) : (score.isAdd !== undefined ? score.isAdd : (score.selfScore >= 0 ? 1 : 0)),
+          };
+        }) || [],
+      };
+
+      await updateSecurityExamineDeptSubmit(submitData);
       ElMessage.success('提交成功');
       router.back();
-    } catch (e) {
-      console.log(e);
+    } catch (e: any) {
+      console.error('提交失败:', e);
+      ElMessage.error(e?.message || '提交失败,请重试');
     }
   };
 
@@ -173,4 +338,19 @@
   .evaluation-items-table {
     width: 100%;
   }
+
+  .upload-files-disabled {
+    pointer-events: none;
+    opacity: 0.6;
+
+    :deep(.upload-button) {
+      cursor: not-allowed;
+      background-color: #f5f5f5;
+      color: #aaa;
+    }
+
+    :deep(.delete-button) {
+      display: none;
+    }
+  }
 </style>

+ 1 - 0
src/views/production-safety/safetyAssessment/evaluationDepartment/configs/tables.ts

@@ -37,6 +37,7 @@ export const EVALUATION_DEPARTMENT_TABLE_COLUMNS: TableColumnProps[] = [
   {
     label: '考核文档',
     prop: 'evaluationDocument',
+    slot: 'evaluationDocument',
     align: 'left',
     minWidth: '150px',
   },

+ 160 - 50
src/views/production-safety/safetyAssessment/evaluationDepartment/evaluationDepartment.vue

@@ -2,9 +2,6 @@
   <div class="safety-platform-container">
     <header class="safety-platform-container__header">
       <div class="breadcrumb-title"> 安全考核管理(部门) </div>
-      <el-tabs v-model="activeTab">
-        <el-tab-pane v-for="item in EVALUATION_DEPARTMENT_TABS" :key="item.value" :label="item.label" :name="item.value" />
-      </el-tabs>
     </header>
     <main class="safety-platform-container__main">
       <div class="search-table-container">
@@ -65,52 +62,75 @@
           >
             <template #status="scope">
               <span>
-                {{
-                  scope.row.status === 'NOT_ISSUED'
-                    ? '未下发'
-                    : scope.row.status === 'SIGNING'
-                      ? '签署中'
-                      : scope.row.status === 'PENDING_SIGN'
-                        ? '待签署'
-                        : scope.row.status === 'PENDING_FEEDBACK'
-                          ? '待反馈材料'
-                          : scope.row.status === 'COMPLETED'
-                            ? '已完成'
-                            : scope.row.status === 'CANCELLED'
-                              ? '已作废'
-                              : '-'
-                }}
+                {{ EVALUATION_DEPARTMENT_STATUS_LABEL[scope.row.status] || scope.row.statusName || '-' }}
               </span>
             </template>
+            <template #evaluationDocument="scope">
+              <div
+                class="file-container--div"
+                v-for="item in parseAttachments(scope.row.evaluationDocument)"
+                :key="item.fileUrl"
+              >
+                <img
+                  class="file-container--div__icon"
+                  @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
+                  :src="FILE_TYPE_ICON[item.fileType]"
+                />
+                <span
+                  class="file-container--div__name"
+                  @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
+                  >{{ item.fileName }}</span
+                >
+                <img
+                  class="file-container--div__download"
+                  :src="DownloadIcon"
+                  @click="downloadFile(item.fileUrl, item.fileName)"
+                />
+              </div>
+            </template>
             <template #action="scope">
               <div class="action-container--div" style="justify-content: left">
-                <ActionButton text="反馈" @click="handleFeedback(scope.row.id)" />
-                <ActionButton text="先进个人申报" @click="handleAdvancedPerson(scope.row.id)" />
+                <!-- 待反馈材料:显示反馈 -->
+                <template v-if="scope.row.status === 'PENDING_FEEDBACK'">
+                  <ActionButton text="反馈" @click="handleFeedback(scope.row.id)" />
+                </template>
+                <!-- 已完成:显示反馈和先进个人申报 -->
+                <template v-else-if="scope.row.status === 'COMPLETED'">
+                  <ActionButton text="反馈" @click="handleFeedback(scope.row.id)" />
+                  <ActionButton text="先进个人申报" @click="handleAdvancedPerson(scope.row.id)" />
+                </template>
+                <!-- 已作废:显示反馈 -->
+                <template v-else-if="scope.row.status === 'CANCELLED'">
+                  <ActionButton text="反馈" @click="handleFeedback(scope.row.id)" />
+                </template>
               </div>
             </template>
           </BasicTable>
         </div>
       </div>
     </main>
+    <PreviewOnline ref="previewOnlineRef" />
   </div>
 </template>
 
 <script lang="ts" setup>
-  import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
+  import { onMounted, reactive, ref } from 'vue';
   import BasicTable from '@/components/BasicTable.vue';
   import useTableConfig from '@/hooks/useTableConfigHook';
   import ActionButton from '@/components/ActionButton.vue';
   import { TABLE_OPTIONS, EVALUATION_DEPARTMENT_TABLE_COLUMNS } from './configs/tables';
-  import { EVALUATION_DEPARTMENT_STATUS_OPTIONS } from './configs/status';
-  import { EVALUATION_DEPARTMENT_TABS } from './constants';
+  import { EVALUATION_DEPARTMENT_STATUS_OPTIONS, EVALUATION_DEPARTMENT_STATUS_LABEL } from './configs/status';
   import { useRouter } from 'vue-router';
   import type { QueryPageRequest } from '@/types/basic-query';
+  import DownloadIcon from '@/views/disaster/disaster-control/src/svg/download.svg';
+  import { downloadFile } from '@/views/disaster/utils';
+  import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
+  import { FILE_TYPE_ICON } from '@/components/UploadFiles/constants';
+  import { querySecurityExamineDept } from '@/api/evaluationSystem';
+  import type { QuerySecurityExamineDeptParams } from '@/api/evaluationSystem';
 
   const router = useRouter();
 
-  // Tabs 切换
-  const activeTab = ref(sessionStorage.getItem('evaluation-department-active') || EVALUATION_DEPARTMENT_TABS[0].value);
-
   // 表格
   const basicTableRef = ref<InstanceType<typeof BasicTable>>();
 
@@ -130,6 +150,26 @@
     },
   });
 
+  // 状态字符串到数字的映射(用于查询接口)
+  const statusStringToNumber: Record<string, number> = {
+    NOT_ISSUED: 0, // 未下发
+    COMPLETED: 1, // 已完成
+    PENDING_FEEDBACK: 2, // 待反馈材料
+    SIGNING: 3, // 签署中
+    PENDING_SIGN: 4, // 待签署
+    CANCELLED: 5, // 已作废
+  };
+
+  // 状态数字到字符串的映射(根据接口返回的数字状态转换为组件使用的字符串状态)
+  const statusNumberToString: Record<number, string> = {
+    0: 'NOT_ISSUED', // 未下发
+    1: 'COMPLETED', // 已完成
+    2: 'PENDING_FEEDBACK', // 待反馈材料
+    3: 'SIGNING', // 签署中
+    4: 'PENDING_SIGN', // 待签署
+    5: 'CANCELLED', // 已作废
+  };
+
   const handleSizeChange = (value: number) => {
     pagination.pageSize = value;
     tableQuery.pageSize = value;
@@ -150,13 +190,48 @@
 
   async function getTableData() {
     tableConfig.loading = true;
-    // TODO: 调用列表查询接口
-    // const res = await getEvaluationDepartmentList(tableQuery);
-    // tableData.value = res.records;
-    // pagination.total = res.totalRow;
-    tableData.value = [];
-    pagination.total = 0;
-    tableConfig.loading = false;
+    try {
+      const params: QuerySecurityExamineDeptParams = {
+        pageNumber: tableQuery.pageNumber,
+        pageSize: tableQuery.pageSize,
+        queryParam: {
+          exName: tableQuery.queryParam.evaluationTableName || undefined,
+          status:
+            tableQuery.queryParam.status !== '' && tableQuery.queryParam.status !== null
+              ? statusStringToNumber[tableQuery.queryParam.status] ?? undefined
+              : undefined,
+          deptName: undefined, // 部门名称(如果需要可以添加搜索条件)
+          planStartTime: tableQuery.queryParam.startTime || undefined,
+          planEndTime: tableQuery.queryParam.endTime || undefined,
+        },
+      };
+
+      const res = await querySecurityExamineDept(params);
+      if (res) {
+        // 映射返回数据字段到表格字段
+        tableData.value = res.records.map((item) => ({
+          id: item.id,
+          evaluationTableName: item.exName, // 考核表名称
+          status: statusNumberToString[item.status] || item.statusName || '', // 状态(转换为字符串)
+          statusName: item.statusName, // 状态名称
+          evaluationDocument: item.attachments, // 考核文档
+          plannedCompletionTime: item.planEndTime || '-', // 计划完成时间(使用计划结束时间)
+          // 保留原始数据,供其他操作使用
+          psemId: item.psemId,
+          deptId: item.deptId,
+          deptName: item.deptName,
+          scores: item.scores,
+          scoreRank: item.scoreRank,
+        }));
+        pagination.total = res.totalRow;
+      }
+    } catch (e) {
+      console.error('获取部门考核列表失败:', e);
+      tableData.value = [];
+      pagination.total = 0;
+    } finally {
+      tableConfig.loading = false;
+    }
   }
 
   const handleSearch = () => {
@@ -191,38 +266,73 @@
   };
 
   const handleAdvancedPerson = (id: number) => {
-    // TODO: 先进个人申报
-    console.log('advanced person', id);
+    router.push({
+      name: 'EvaluationDepartmentItem',
+      query: {
+        id,
+        operate: 'evaluationDepartment-advanced-person',
+      },
+    });
   };
 
-  // 监听 tabs 切换
-  watch(activeTab, () => {
-    sessionStorage.setItem('evaluation-department-active', activeTab.value);
-    handleSearch();
-  });
+  // 预览
+  const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
+  const previewOnline = (url: string | undefined, type: keyof typeof FILE_TYPE_ICON) => {
+    if (url) {
+      previewOnlineRef.value?.open(url, type);
+    }
+  };
+
+  // 解析逗号分隔的URL字符串为文件列表
+  const parseAttachments = (attachmentsStr: string | undefined): Array<{
+    fileUrl: string;
+    fileName: string;
+    fileType: string;
+  }> => {
+    if (!attachmentsStr || !attachmentsStr.trim()) {
+      return [];
+    }
+
+    // 按逗号分割URL
+    const urls = attachmentsStr.split(',').map(url => url.trim()).filter(url => url);
+
+    return urls.map((url) => {
+      // 从URL中提取文件名
+      const urlParts = url.split('/');
+      const fileName = urlParts[urlParts.length - 1] || '未知文件';
+
+      // 根据文件扩展名判断文件类型
+      const extension = fileName.split('.').pop()?.toLowerCase() || '';
+      let fileType = 'pdf';
+      if (extension === 'doc' || extension === 'docx') {
+        fileType = 'word';
+      } else if (extension === 'xls' || extension === 'xlsx') {
+        fileType = 'excel';
+      } else if (extension === 'ppt' || extension === 'pptx') {
+        fileType = 'ppt';
+      }
+
+      return {
+        fileUrl: url,
+        fileName,
+        fileType,
+      };
+    });
+  };
 
   onMounted(() => {
     getTableData();
   });
-
-  onUnmounted(() => {
-    sessionStorage.setItem('evaluation-department-active', activeTab.value);
-  });
 </script>
 
 <style scoped lang="scss">
   @use '@/styles/page-details-layout.scss' as *;
   @use '@/styles/page-main-layout.scss' as *;
   @use '@/styles/basic-table-action.scss' as *;
+  @use '@/styles/basic-table-file.scss' as *;
   @use '@/views/traffic/violation/style/act-search-table.scss' as *;
 
   .safety-platform-container__header {
     padding-bottom: 0 !important;
   }
-  :deep(.el-tabs__header) {
-    margin: 0;
-  }
-  :deep(.el-tabs__item) {
-    font-size: 14px !important;
-  }
 </style>

+ 20 - 4
src/views/production-safety/safetyAssessment/evaluationSystem/EvaluationSystemItem.vue

@@ -14,11 +14,11 @@
   import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
 
   const route = useRoute();
-  const operate = route.query.operate as string;
-  const id = Number(route.query.id);
+  const operate = computed(() => route.query.operate as string);
+  const id = computed(() => Number(route.query.id));
 
   const headerTitle = computed(() => {
-    switch (operate) {
+    switch (operate.value) {
       case 'evaluationSystem-create':
         return '新增安全考核';
       case 'evaluationSystem-edit':
@@ -27,19 +27,35 @@
         return '查看安全考核';
       case 'evaluationSystem-target':
         return '考核对象';
+      case 'evaluationSystem-advanced-group':
+        return '部门考核';
+      case 'evaluationSystem-advanced-person':
+        return '先进个人';
+      case 'evaluationSystem-feedback':
+        return '评分';
+      case 'evaluationSystem-feedback-view':
+        return '部门考核详情';
+      case 'evaluationSystem-audit':
+        return '审核';
       default:
         return '未知操作';
     }
   });
 
   const dynamicComponent = computed(() => {
-    switch (operate) {
+    switch (operate.value) {
       case 'evaluationSystem-create':
       case 'evaluationSystem-edit':
       case 'evaluationSystem-view':
         return defineAsyncComponent(() => import('./components/EvaluationSystemDetail.vue'));
       case 'evaluationSystem-target':
+      case 'evaluationSystem-advanced-group':
+      case 'evaluationSystem-advanced-person':
         return defineAsyncComponent(() => import('./components/EvaluationTarget.vue'));
+      case 'evaluationSystem-feedback':
+      case 'evaluationSystem-feedback-view':
+      case 'evaluationSystem-audit':
+        return defineAsyncComponent(() => import('./components/EvaluationSystemFeedback.vue'));
       default:
         return '';
     }

+ 124 - 16
src/views/production-safety/safetyAssessment/evaluationSystem/components/EvaluationSystemDetail.vue

@@ -75,6 +75,39 @@
               />
             </template>
           </el-table-column>
+          <el-table-column label="加减分项" min-width="150" align="center">
+            <template #default="scope">
+              <el-select
+                v-model="scope.row.isAdd"
+                placeholder="请选择"
+                :disabled="isViewMode"
+                style="width: 100%"
+              >
+                <el-option label="加分项" :value="1" />
+                <el-option label="减分项" :value="0" />
+              </el-select>
+            </template>
+          </el-table-column>
+          <el-table-column label="复评人" min-width="150">
+            <template #default="scope">
+              <el-select
+                v-model="scope.row.reviewUserId"
+                placeholder="请选择复评人"
+                filterable
+                clearable
+                :disabled="isViewMode"
+                style="width: 100%"
+                @change="(val) => handleReviewUserChange(scope.$index, val)"
+              >
+                <el-option
+                  v-for="user in reviewUserList"
+                  :key="user.id"
+                  :label="user.realname"
+                  :value="user.id"
+                />
+              </el-select>
+            </template>
+          </el-table-column>
           <el-table-column v-if="!isViewMode" label="操作" fixed="right" width="350" align="center">
             <template #default="scope">
               <el-button type="primary" link @click="handleAddItem(scope.$index)">新增</el-button>
@@ -138,6 +171,7 @@
   import type { FileItem } from '@/components/UploadFiles/types';
   import { useUserInfoHook } from '@/hooks/useUserInfoHook';
   import { formatAttachmentList } from '@/components/UploadFiles/utils';
+  import { getUserList } from '@/api/system/user-operate';
 
   const props = defineProps<{
     id?: number;
@@ -166,9 +200,40 @@
       evaluationItem: '',
       evaluationContent: '',
       scoringMethod: '',
+      isAdd: 1, // 是否加分项(0-否,1-是),默认为1(加分项)
+      reviewUserId: null as number | null, // 复评人ID
+      reviewUserName: '', // 复评人姓名
     },
   ]);
 
+  // 复评人用户列表
+  const reviewUserList = ref<UserLisItem[]>([]);
+  const getReviewUserList = async () => {
+    try {
+      const res = await getUserList({
+        pageNumber: 1,
+        pageSize: 9999,
+        queryParam: {}, // 不传递 deptId 参数
+      });
+      reviewUserList.value = res?.records || [];
+    } catch (e) {
+      console.error('获取复评人列表失败:', e);
+      reviewUserList.value = [];
+    }
+  };
+
+  // 处理复评人选择变化
+  const handleReviewUserChange = (index: number, userId: number | null) => {
+    if (userId) {
+      const selectedUser = reviewUserList.value.find((user) => user.id === userId);
+      if (selectedUser) {
+        evaluationItems.value[index].reviewUserName = selectedUser.realname || '';
+      }
+    } else {
+      evaluationItems.value[index].reviewUserName = '';
+    }
+  };
+
   // 用于存储输入过程中的临时值,避免实时合并
   const tempEvaluationItems = ref<Record<number, string>>({});
 
@@ -205,12 +270,16 @@
       // 接口返回的数据格式:{ code: 0, message: "string", data: EvaluationContent[] }
       if (response && response.data && Array.isArray(response.data)) {
         // 将导入的数据映射到 evaluationItems 格式
+        // 导入的数据视为新增数据,不带后端已保存的 id / psemId
         evaluationItems.value = response.data.map((item: any) => ({
-          id: item.id || 0,
-          psemId: item.psemId || 0,
+          id: 0,
+          psemId: 0,
           evaluationItem: item.exProgram || '', // 考核项目
           evaluationContent: item.exContent || '', // 考核内容
           scoringMethod: item.scoringWay || '', // 评分方式
+          isAdd: item.isAdd !== undefined ? item.isAdd : 1, // 是否加分项(0-否,1-是),默认为1
+          reviewUserId: item.reviewUserId || null, // 复评人ID
+          reviewUserName: item.reviewUserName || '', // 复评人姓名
         }));
 
         // 如果导入后列表为空,至少保留一条空白数据
@@ -416,11 +485,16 @@
   // 新增:在当前行下面插入一条新数据,如果当前行有考核项目值,新行也使用相同的值
   const handleAddItem = (index: number) => {
     const currentItem = evaluationItems.value[index];
-    evaluationItems.value.splice(index + 1, 0, {
-      evaluationItem: currentItem.evaluationItem || '', // 合并考核项目字段
-      evaluationContent: '',
-      scoringMethod: '',
-    });
+      evaluationItems.value.splice(index + 1, 0, {
+        id: 0,
+        psemId: 0,
+        evaluationItem: currentItem.evaluationItem || '', // 合并考核项目字段
+        evaluationContent: '',
+        scoringMethod: '',
+        isAdd: currentItem.isAdd !== undefined ? currentItem.isAdd : 1, // 是否加分项,继承当前行的值
+        reviewUserId: null, // 复评人ID
+        reviewUserName: '', // 复评人姓名
+      });
   };
 
   // 删除当前行
@@ -479,6 +553,9 @@
       evaluationItem: '',
       evaluationContent: '',
       scoringMethod: '',
+      isAdd: 1, // 是否加分项(0-否,1-是),默认为1
+      reviewUserId: null, // 复评人ID
+      reviewUserName: '', // 复评人姓名
     });
   };
 
@@ -491,6 +568,9 @@
       evaluationItem: '',
       evaluationContent: '',
       scoringMethod: '',
+      isAdd: 1, // 是否加分项(0-否,1-是),默认为1
+      reviewUserId: null, // 复评人ID
+      reviewUserName: '', // 复评人姓名
     });
   };
 
@@ -545,18 +625,29 @@
       
       // 填充考核项目列表
       if (detail.exContents && detail.exContents.length > 0) {
-        evaluationItems.value = detail.exContents.map((item) => ({
+        // 详情接口返回的数据视为已持久化的数据,保留 id / psemId
+        evaluationItems.value = detail.exContents.map((item: any) => ({
+          id: item.id || 0,
+          psemId: item.psemId || 0,
           evaluationItem: item.exProgram || '',
           evaluationContent: item.exContent || '',
           scoringMethod: item.scoringWay || '',
+          isAdd: item.isAdd !== undefined ? item.isAdd : 1, // 是否加分项(0-否,1-是),默认为1
+          reviewUserId: item.reviewUserId || null, // 复评人ID
+          reviewUserName: item.reviewUserName || '', // 复评人姓名
         }));
       } else {
         // 如果没有数据,至少保留一条空白数据
         evaluationItems.value = [
           {
+            id: 0,
+            psemId: 0,
             evaluationItem: '',
             evaluationContent: '',
             scoringMethod: '',
+            isAdd: 1, // 是否加分项(0-否,1-是),默认为1
+            reviewUserId: null, // 复评人ID
+            reviewUserName: '', // 复评人姓名
           },
         ];
       }
@@ -623,14 +714,25 @@
       }
 
       // 映射考核项目列表,添加序号
-      const exContents: EvaluationContent[] = evaluationItems.value.map((item, index) => ({
-        id: item.id || 0, // 编辑时保留原有ID,新增时为0
-        psemId: item.psemId || 0, // 编辑时保留原有psemId,新增时为0
-        serialNum: index + 1, // 序号从1开始
-        exProgram: item.evaluationItem || '', // 考核项目
-        exContent: item.evaluationContent || '', // 考核内容
-        scoringWay: item.scoringMethod || '', // 评分方式
-      }));
+      const exContents: EvaluationContent[] = evaluationItems.value.map((item, index) => {
+        const base: any = {
+          serialNum: index + 1, // 序号从1开始
+          exProgram: item.evaluationItem || '', // 考核项目
+          exContent: item.evaluationContent || '', // 考核内容
+          scoringWay: item.scoringMethod || '', // 评分方式
+          isAdd: item.isAdd !== undefined ? item.isAdd : 1, // 是否加分项(0-否,1-是)
+          reviewUserId: item.reviewUserId || null, // 复评人ID
+          reviewUserName: item.reviewUserName || '', // 复评人姓名
+        };
+
+        // 只有详情接口返回的已存在数据才携带 id / psemId
+        if (item.id && item.psemId) {
+          base.id = item.id;
+          base.psemId = item.psemId;
+        }
+
+        return base as EvaluationContent;
+      });
 
       if (isEditMode.value && props.id) {
         // 编辑模式:调用更新接口
@@ -682,6 +784,9 @@
   };
 
   onMounted(() => {
+    // 获取复评人列表
+    getReviewUserList();
+    
     if (isEditMode.value || isViewMode.value) {
       getDetail();
     } else {
@@ -694,6 +799,9 @@
             evaluationItem: '',
             evaluationContent: '',
             scoringMethod: '',
+            isAdd: 1, // 是否加分项(0-否,1-是),默认为1
+            reviewUserId: null, // 复评人ID
+            reviewUserName: '', // 复评人姓名
           },
         ];
       }

+ 498 - 0
src/views/production-safety/safetyAssessment/evaluationSystem/components/EvaluationSystemFeedback.vue

@@ -0,0 +1,498 @@
+<template>
+  <main class="safety-platform-container__main">
+    <el-form ref="formRef" :model="ruleFormData" :rules="formRules" label-width="auto" class="evaluation-form">
+      <el-form-item label="考核信息标题:" prop="evaluationTitle">
+        <el-input v-model="ruleFormData.evaluationTitle" placeholder="请输入考核信息标题" disabled />
+      </el-form-item>
+      <el-form-item label="上传附件文档:" prop="attachmentDocument">
+        <div class="upload-files-disabled">
+          <UploadFiles label="上传附件" :file-list="ruleFormData.attachmentDocument" @uploadSuccess="handleUploadSuccess" />
+        </div>
+      </el-form-item>
+      <el-form-item label="评分说明:" prop="scoringDescription">
+        <el-input v-model="ruleFormData.scoringDescription" type="textarea" :rows="5" placeholder="请输入评分说明" disabled />
+      </el-form-item>
+    </el-form>
+
+    <div class="evaluation-items-section">
+      <div class="section-header">
+        <el-button plain @click="handleDownloadTemplate" disabled>模板下载</el-button>
+        <el-button plain @click="handleImport">导入</el-button>
+        <el-button plain @click="handleExport">导出</el-button>
+        <input
+          ref="importFileInputRef"
+          type="file"
+          accept=".xlsx,.xls"
+          style="display: none"
+          @change="handleFileChange"
+        />
+      </div>
+      <div class="evaluation-items-table">
+        <el-table :data="evaluationItems" border show-summary :summary-method="getSummaries">
+          <el-table-column label="编号" type="index" width="80" align="center" />
+          <el-table-column label="考核项目" prop="evaluationItem" min-width="150" />
+          <el-table-column label="考核内容" prop="evaluationContent" min-width="200" />
+          <el-table-column label="评分方式" prop="scoringMethod" min-width="150" />
+          <el-table-column label="加减分项" prop="scoreType" min-width="120" />
+          <el-table-column label="自评得分" prop="selfScore" min-width="120" />
+          <el-table-column label="资料说明" prop="materialDescription" min-width="200" />
+          <el-table-column label="复核人姓名" prop="reviewUserName" min-width="120" />
+          <el-table-column label="复核得分" prop="reviewScore" min-width="180">
+            <template #default="scope">
+              <el-input-number
+                v-if="!isAudit && scope.row.isReviewInput"
+                v-model="scope.row.reviewScore"
+                :min="0"
+                :precision="0"
+                :step="1"
+                placeholder="请输入复核得分"
+                @blur="handleScoreBlur"
+              />
+              <span v-else>{{ scope.row.reviewScore || 0 }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+  </main>
+  <footer class="safety-platform-container__footer">
+    <el-button @click="router.back()">取消</el-button>
+    <el-button v-if="!isAudit && isSelfApproveButton" @click="openRejectDialog('review')">复核不通过</el-button>
+    <el-button v-if="isAudit" @click="openRejectDialog('approve')">审核不通过</el-button>
+    <el-button type="primary" @click="handleSubmit">{{ getSubmitButtonText }}</el-button>
+  </footer>
+
+  <!-- 拒绝原因弹窗 -->
+  <el-dialog
+    v-model="rejectDialogVisible"
+    :title="rejectDialogTitle"
+    width="600px"
+    :close-on-click-modal="false"
+  >
+    <el-input
+      v-model="rejectReason"
+      type="textarea"
+      :rows="6"
+      :maxlength="300"
+      :show-word-limit="true"
+      :placeholder="rejectDialogPlaceholder"
+    />
+    <template #footer>
+      <el-button @click="closeRejectDialog">取消</el-button>
+      <el-button type="primary" @click="confirmReject">确定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+  import { computed, onMounted, ref, watch } from 'vue';
+  import { useRouter, useRoute } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import type { FormInstance } from 'element-plus';
+  import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
+  import {
+    querySecurityExamineIssueDetail,
+    updateSecurityExamineIssueReviewSubmit,
+    updateSecurityExamineIssueReviewAgree,
+    updateSecurityExamineIssueReviewDisagree,
+    updateSecurityExamineIssueApproveAgree,
+    updateSecurityExamineIssueApproveDisagree,
+    exportSecurityExamineIssueDeptDetail,
+    importSecurityExamineIssueDeptDetail,
+  } from '@/api/evaluationSystem';
+  import type { FileItem } from '@/components/UploadFiles/types';
+
+  const props = defineProps<{
+    id: number;
+  }>();
+
+  const router = useRouter();
+  const route = useRoute();
+
+  // 判断是评分还是审核
+  const isAudit = computed(() => route.query.operate === 'evaluationSystem-audit');
+
+  // 提交按钮文字
+  const getSubmitButtonText = computed(() => {
+    if (isAudit.value) {
+      return '审核通过';
+    }
+    // 如果是评分模式,根据 isSelfApproveButton 决定按钮文字
+    if (isSelfApproveButton.value) {
+      return '复核同意';
+    }
+    return '复核提交';
+  });
+
+  // 拒绝原因弹窗相关
+  const rejectDialogVisible = ref(false);
+  const rejectReason = ref('');
+  const rejectType = ref<'review' | 'approve'>('review'); // 当前拒绝类型
+
+  const rejectDialogTitle = computed(() => {
+    return rejectType.value === 'review'
+      ? '复核评分不通过的记录,需要填写驳回原因'
+      : '审核不通过的记录,需要填写驳回原因';
+  });
+
+  const rejectDialogPlaceholder = computed(() => {
+    return rejectType.value === 'review'
+      ? '请填写驳回审批原因'
+      : '请填写驳回审批原因';
+  });
+
+  const openRejectDialog = (type: 'review' | 'approve') => {
+    rejectType.value = type;
+    rejectReason.value = '';
+    rejectDialogVisible.value = true;
+  };
+
+  const closeRejectDialog = () => {
+    rejectDialogVisible.value = false;
+    rejectReason.value = '';
+  };
+
+  const formRef = ref<FormInstance>();
+  const ruleFormData = ref({
+    evaluationTitle: '',
+    attachmentDocument: [] as FileItem[],
+    scoringDescription: '',
+  });
+
+  const formRules = {
+    evaluationTitle: [{ required: true, message: '请输入考核信息标题', trigger: 'blur' }],
+    attachmentDocument: [{ required: true, message: '请上传附件文档', trigger: 'change' }],
+    // scoringDescription: [{ required: true, message: '请输入评分说明', trigger: 'blur' }],
+  };
+
+  const evaluationItems = ref<any[]>([]);
+  // 保存详情原始数据,用于提交
+  const detailData = ref<any>(null);
+  // 是否显示复核不通过按钮(从详情接口获取)
+  const isSelfApproveButton = ref(false);
+
+  // 计算自评得分总计
+  const getTotalScore = () => {
+    return evaluationItems.value.reduce((sum, item) => {
+      return sum + (Number(item.selfScore) || 0);
+    }, 0);
+  };
+
+  // 计算复核得分总计
+  const getTotalReviewScore = () => {
+    return evaluationItems.value.reduce((sum, item) => {
+      return sum + (Number(item.reviewScore) || 0);
+    }, 0);
+  };
+
+  // 自评得分失去焦点时触发(用于强制更新合计行)
+  const handleScoreBlur = () => {
+    // 触发响应式更新,让合计行重新计算
+    // 通过修改数组引用来触发更新
+    evaluationItems.value = [...evaluationItems.value];
+  };
+
+  // 表格合计行方法
+  const getSummaries = (param: any) => {
+    const { columns } = param;
+    const sums: string[] = [];
+    columns.forEach((column: any, index: number) => {
+      if (index === 0) {
+        // 编号列
+        sums[index] = '';
+      } else if (column.property === 'scoreType') {
+        // 加减分项列显示"总计:"
+        sums[index] = '总计:';
+      } else if (column.property === 'reviewScore') {
+        // 复核得分列显示总分,即使为0也显示
+        const total = getTotalReviewScore();
+        sums[index] = `${total}分`;
+      } else {
+        // 其他列显示空
+        sums[index] = '';
+      }
+    });
+    return sums;
+  };
+
+  const handleValidate = async () => {
+    if (!formRef.value) return;
+    return new Promise((resolve) => {
+      formRef.value?.validate((valid: boolean) => {
+        resolve(valid);
+      });
+    });
+  };
+
+  const handleUploadSuccess = (files: any[]) => {
+    ruleFormData.value.attachmentDocument = files;
+  };
+
+  const handleDownloadTemplate = () => {
+    // TODO: 下载模板
+    console.log('download template');
+  };
+
+  // 导入文件引用
+  const importFileInputRef = ref<HTMLInputElement>();
+
+  const handleImport = () => {
+    // 触发文件选择
+    importFileInputRef.value?.click();
+  };
+
+  const handleFileChange = async (event: Event) => {
+    const target = event.target as HTMLInputElement;
+    const file = target.files?.[0];
+    if (!file) return;
+
+    try {
+      await importSecurityExamineIssueDeptDetail({
+        id: props.id,
+        file,
+      });
+      ElMessage.success('导入成功');
+      // 重新加载详情数据
+      await getDetail();
+    } catch (e: any) {
+      console.error('导入失败:', e);
+      ElMessage.error(e?.message || '导入失败,请重试');
+    } finally {
+      // 清空文件选择
+      if (target) {
+        target.value = '';
+      }
+    }
+  };
+
+  const handleExport = async () => {
+    try {
+      const blob = await exportSecurityExamineIssueDeptDetail(props.id);
+      // 创建下载链接
+      const url = window.URL.createObjectURL(blob);
+      const link = document.createElement('a');
+      link.href = url;
+      link.download = `部门考核详情_${new Date().getTime()}.xlsx`;
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+      window.URL.revokeObjectURL(url);
+      ElMessage.success('导出成功');
+    } catch (e: any) {
+      console.error('导出失败:', e);
+      ElMessage.error(e?.message || '导出失败,请重试');
+    }
+  };
+
+  // 将逗号分隔的 URL 字符串转换为 FileItem[] 格式
+  const parseAttachmentsToFileList = (attachmentsStr: string | undefined): FileItem[] => {
+    if (!attachmentsStr || !attachmentsStr.trim()) {
+      return [];
+    }
+
+    // 按逗号分割URL
+    const urls = attachmentsStr.split(',').map(url => url.trim()).filter(url => url);
+
+    return urls.map((url, index) => {
+      // 从URL中提取文件名
+      const urlParts = url.split('/');
+      const fileName = urlParts[urlParts.length - 1] || `文件${index + 1}`;
+
+      // 根据文件扩展名判断文件类型
+      const extension = fileName.split('.').pop()?.toLowerCase() || '';
+      let fileType = 'pdf';
+      if (extension === 'doc' || extension === 'docx') {
+        fileType = 'word';
+      } else if (extension === 'xls' || extension === 'xlsx') {
+        fileType = 'excel';
+      } else if (extension === 'ppt' || extension === 'pptx') {
+        fileType = 'ppt';
+      }
+
+      return {
+        fileId: index + 1,
+        fileName,
+        fileType,
+        fileSize: '0', // 接口未返回文件大小,使用默认值
+        fileUrl: url,
+      };
+    });
+  };
+
+  const getDetail = async () => {
+    if (!props.id || isNaN(props.id) || props.id <= 0) {
+      console.error('无效的ID:', props.id);
+      ElMessage.error('缺少有效的考核对象ID');
+      return;
+    }
+    try {
+      console.log('调用详情接口,ID:', props.id);
+      const detail = await querySecurityExamineIssueDetail(props.id);
+      if (!detail) {
+        console.warn('详情接口返回空数据');
+        return;
+      }
+
+      // 保存原始详情数据,用于提交
+      detailData.value = detail;
+
+      // 获取是否显示复核不通过按钮
+      isSelfApproveButton.value = detail.isSelfApproveButton === true;
+
+      // 映射表单字段
+      ruleFormData.value.evaluationTitle = detail.exName || ''; // 考核表名称
+      ruleFormData.value.attachmentDocument = parseAttachmentsToFileList(detail.attachments); // 附件文档
+      ruleFormData.value.scoringDescription = ''; // 评分说明(接口暂无此字段,留空)
+
+      // 映射考核项目列表(scores 数组)
+      if (detail.scores && detail.scores.length > 0) {
+        evaluationItems.value = detail.scores.map((score) => ({
+          id: score.id, // 保留评分项ID,用于提交
+          isAdd: score.isAdd !== undefined ? score.isAdd : (score.selfScore >= 0 ? 1 : 0), // 是否加分项(0-否,1-是)
+          isAddName: score.isAdd === 1 ? '加分项' : '减分项', // 加减分项名称
+          scoreType: score.isAdd === 1 ? '加分项' : '减分项', // 加减分项(用于显示,兼容旧字段)
+          evaluationItem: score.exProgram || '', // 考核项目
+          evaluationContent: score.exContent || '', // 考核内容
+          scoringMethod: score.scoringWay || '', // 评分方式
+          selfScore: score.selfScore || 0, // 自评得分
+          reviewUserName: score.reviewUserName || '-', // 复核人姓名(从详情顶层获取)
+          reviewScore: score.reviewScore || 0, // 复核得分
+          materialDescription: score.attachments || '', // 资料说明(使用附件字段)
+          isReviewInput: score.isReviewInput == true, // 是否显示复核得分输入框
+        }));
+      } else {
+        evaluationItems.value = [];
+      }
+    } catch (e) {
+      console.error('获取考核对象详情失败:', e);
+      ElMessage.error('获取详情失败,请重试');
+    }
+  };
+
+  const handleSubmit = async () => {
+    const res = await handleValidate();
+    if (!res) return;
+    try {
+      if (!detailData.value) {
+        ElMessage.error('数据加载失败,请刷新后重试');
+        return;
+      }
+
+      // 使用详情原始数据,更新复核得分和isAdd字段
+      const submitData = {
+        ...detailData.value,
+        scores: detailData.value.scores?.map((score: any) => {
+          // 找到对应的复核得分和isAdd
+          const item = evaluationItems.value.find((item) => item.id === score.id);
+          return {
+            ...score,
+            reviewScore: item ? Number(item.reviewScore) || 0 : score.reviewScore || 0,
+            isAdd: item ? (item.isAdd !== undefined ? item.isAdd : (item.selfScore >= 0 ? 1 : 0)) : (score.isAdd !== undefined ? score.isAdd : (score.selfScore >= 0 ? 1 : 0)),
+          };
+        }) || [],
+      };
+
+      if (isAudit.value) {
+        // 审核通过
+        await updateSecurityExamineIssueApproveAgree(props.id);
+        ElMessage.success('审核通过操作成功');
+      } else {
+        // 评分模式
+        if (isSelfApproveButton.value) {
+          // isSelfApproveButton 为 true,调用复核同意接口
+          await updateSecurityExamineIssueReviewAgree(submitData);
+          ElMessage.success('复核同意操作成功');
+        } else {
+          // isSelfApproveButton 为 false,调用提交接口
+          await updateSecurityExamineIssueReviewSubmit(submitData);
+          ElMessage.success('复核提交成功');
+        }
+      }
+      router.back();
+    } catch (e: any) {
+      console.error('提交失败:', e);
+      ElMessage.error(e?.message || '提交失败,请重试');
+    }
+  };
+
+  const confirmReject = async () => {
+    if (!rejectReason.value || !rejectReason.value.trim()) {
+      ElMessage.warning('请填写驳回原因');
+      return;
+    }
+
+    try {
+      if (rejectType.value === 'review') {
+        await updateSecurityExamineIssueReviewDisagree(props.id, rejectReason.value.trim());
+        ElMessage.success('复核不通过操作成功');
+      } else {
+        await updateSecurityExamineIssueApproveDisagree(props.id, rejectReason.value.trim());
+        ElMessage.success('审核不通过操作成功');
+      }
+      closeRejectDialog();
+      router.back();
+    } catch (e: any) {
+      console.error('操作失败:', e);
+      ElMessage.error(e?.message || '操作失败,请重试');
+    }
+  };
+
+  // 监听 props.id 变化,重新加载数据
+  watch(
+    () => props.id,
+    (newId) => {
+      if (newId && !isNaN(newId) && newId > 0) {
+        getDetail();
+      }
+    },
+    { immediate: true },
+  );
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+
+  .evaluation-form {
+    display: flex;
+    flex-direction: column;
+    width: 600px;
+    gap: 32px;
+
+    :deep(.el-form-item) {
+      margin-bottom: 0;
+    }
+
+    :deep(.el-form-item__label) {
+      padding: 0;
+    }
+  }
+
+  .evaluation-items-section {
+    margin-top: 32px;
+  }
+
+  .section-header {
+    display: flex;
+    gap: 10px;
+    margin-bottom: 20px;
+  }
+
+  .evaluation-items-table {
+    width: 100%;
+  }
+
+  .upload-files-disabled {
+    pointer-events: none;
+    opacity: 0.6;
+
+    :deep(.upload-button) {
+      cursor: not-allowed;
+      background-color: #f5f5f5;
+      color: #aaa;
+    }
+
+    :deep(.delete-button) {
+      display: none;
+    }
+  }
+</style>

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

@@ -1,79 +1,160 @@
 <template>
   <div class="safety-platform-container">
     <header class="safety-platform-container__header">
-      <span class="breadcrumb-title">考核对象</span>
-      <el-tabs v-model="activeTab" @tab-change="handleTabChange">
-        <el-tab-pane label="全部" name="ALL" />
-        <el-tab-pane label="未下发" name="0" />
-        <el-tab-pane label="待反馈" name="2" />
-        <el-tab-pane label="待评分" name="3" />
-        <el-tab-pane label="待审核" name="4" />
-        <el-tab-pane label="已作废" name="5" />
-        <el-tab-pane label="已完成" name="1" />
-      </el-tabs>
+      <div class="evaluation-header">
+        <h1 class="evaluation-title">{{ evaluationDetail.exName || '考核对象' }}</h1>
+        <div class="evaluation-meta">
+          <span>考核部门: {{ evaluationDetail.deptNames || '-' }}</span>
+          <span>创建人: {{ evaluationDetail.createdUserName || '-' }}</span>
+          <span>创建时间: {{ formatDateTime(evaluationDetail.createdAt) || '-' }}</span>
+        </div>
+      </div>
     </header>
     <main class="safety-platform-container__main">
       <div class="search-table-container">
-        <header>
-          <div style="position: relative">
-            <el-button plain class="search-table-container--button" @click="handleExport">
-              导出
-            </el-button>
-            <el-button plain class="search-table-container--button" @click="handleBatchDelete">
-              删除
-            </el-button>
-          </div>
+        <!-- tabs 放在搜索条件上面 -->
+        <div class="header-tabs-wrapper" v-if="showViewTab">
+          <!-- 顶部切换:按状态 / 按部门考核结果(先进集体排名) / 先进个人 -->
+          <el-tabs v-model="viewTab">
+            <el-tab-pane v-if="operate !== 'evaluationSystem-advanced-group'" label="状态" name="status" />
+            <el-tab-pane label="部门考核结果" name="dept" />
+            <el-tab-pane v-if="operate !== 'evaluationSystem-advanced-group'" label="先进个人" name="person" />
+          </el-tabs>
+          <!-- 状态模式下,展示原有状态 tabs -->
+          <el-tabs
+            v-if="viewTab === 'status'"
+            v-model="activeTab"
+            @tab-change="handleTabChange"
+          >
+            <template v-if="showAllTabs">
+              <el-tab-pane label="全部" name="ALL" />
+              <el-tab-pane
+                v-for="item in EVALUATION_SYSTEM_STATUS_OPTIONS"
+                :key="item.value"
+                :label="item.label"
+                :name="String(item.value)"
+              />
+            </template>
+            <template v-else>
+              <el-tab-pane :label="singleTabLabel" :name="singleStatus" />
+            </template>
+          </el-tabs>
+        </div>
+        <!-- 从考核对象入口进来(非先进集体/先进个人),仅展示状态 tabs -->
+        <div class="header-tabs-wrapper" v-else>
+          <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+            <template v-if="showAllTabs">
+              <el-tab-pane label="全部" name="ALL" />
+              <el-tab-pane
+                v-for="item in EVALUATION_SYSTEM_STATUS_OPTIONS"
+                :key="item.value"
+                :label="item.label"
+                :name="String(item.value)"
+              />
+            </template>
+            <template v-else>
+              <el-tab-pane :label="singleTabLabel" :name="singleStatus" />
+            </template>
+          </el-tabs>
+        </div>
 
+        <header>
           <div class="act-search">
             <section class="select-box">
-              <div class="select-box--item">
-                <span>状态:</span>
-                <el-select
-                  v-if="activeTab === 'ALL'"
-                  v-model="tableQuery.queryParam.status"
-                  placeholder="请选择状态"
-                  clearable
+              <!-- 部门考核结果视图:显示部门和部门负责人输入框 -->
+              <template v-if="viewTab === 'dept' && operate === 'evaluationSystem-advanced-group'">
+                <div class="select-box--item">
+                  <span>部门:</span>
+                  <el-input
+                    v-model="tableQuery.queryParam.deptName"
+                    placeholder="请输入部门名称"
+                    clearable
+                  />
+                </div>
+                <div class="select-box--item">
+                  <span>部门负责人:</span>
+                  <el-input
+                    v-model="tableQuery.queryParam.userName"
+                    placeholder="请输入部门负责人"
+                    clearable
+                  />
+                </div>
+                <!-- <div class="select-box--item">
+                  <span>时间范围:</span>
+                  <el-date-picker
+                    v-model="tableQuery.queryParam.dateRange"
+                    type="daterange"
+                    range-separator="至"
+                    start-placeholder="开始日期"
+                    end-placeholder="结束日期"
+                    value-format="YYYY-MM-DD"
+                    format="YYYY-MM-DD"
+                  />
+                </div> -->
+              </template>
+              <!-- 其他视图:显示原有搜索条件 -->
+              <template v-else>
+                <!-- 状态筛选仅在"全部"tab 下显示(包含文字和下拉框) -->
+                <div
+                  class="select-box--item"
+                  v-if="(showViewTab && viewTab === 'status' && activeTab === 'ALL') || (!showViewTab && activeTab === 'ALL')"
                 >
-                  <el-option
-                    v-for="item in EVALUATION_SYSTEM_STATUS_OPTIONS"
-                    :key="item.value"
-                    :label="item.label"
-                    :value="item.value"
+                  <span>状态:</span>
+                  <el-select
+                    v-model="tableQuery.queryParam.status"
+                    placeholder="请选择状态"
+                    clearable
+                  >
+                    <el-option
+                      v-for="item in EVALUATION_SYSTEM_STATUS_OPTIONS"
+                      :key="item.value"
+                      :label="item.label"
+                      :value="item.value"
+                    />
+                  </el-select>
+                </div>
+                <div class="select-box--item">
+                  <span>考核对象:</span>
+                  <el-cascader
+                    ref="targetDeptCascaderRef"
+                    v-model="targetDeptId"
+                    :options="deptTree"
+                    :props="cascaderDeptProp"
+                    :show-all-levels="false"
+                    placeholder="请选择考核对象部门"
+                    filterable
+                    @change="handleTargetDeptChange"
                   />
-                </el-select>
-              </div>
-              <div class="select-box--item">
-                <span>考核对象:</span>
-                <el-input
-                  v-model="tableQuery.queryParam.target"
-                  placeholder="请输入考核对象"
-                  class="act-search-input"
-                />
-              </div>
-              <div class="select-box--item">
-                <span>时间范围:</span>
-                <el-date-picker
-                  v-model="tableQuery.queryParam.dateRange"
-                  type="daterange"
-                  range-separator="至"
-                  start-placeholder="开始日期"
-                  end-placeholder="结束日期"
-                  value-format="YYYY-MM-DD"
-                  format="YYYY-MM-DD"
-                />
-              </div>
-              <div class="select-box--item">
-                <span>搜索关键词:</span>
-                <el-input
-                  v-model="tableQuery.queryParam.keyword"
-                  placeholder="请输入关键字"
-                  class="act-search-input"
-                />
-              </div>
+                </div>
+                <div class="select-box--item">
+                  <span>时间范围:</span>
+                  <el-date-picker
+                    v-model="tableQuery.queryParam.dateRange"
+                    type="daterange"
+                    range-separator="至"
+                    start-placeholder="开始日期"
+                    end-placeholder="结束日期"
+                    value-format="YYYY-MM-DD"
+                    format="YYYY-MM-DD"
+                  />
+                </div>
+              </template>
             </section>
             <section class="search-btn">
               <el-button type="primary" @click="handleSearch">查询</el-button>
               <el-button @click="handleReset">重置</el-button>
+              <el-button plain class="search-table-container--button" @click="handleExport">
+                导出
+              </el-button>
+              <!-- 先进集体排名 / 先进个人模式下,顶部按钮只保留“导出” -->
+              <!-- <el-button
+                v-if="viewTab === 'status'"
+                plain
+                class="search-table-container--button"
+                @click="handleBatchDelete"
+              >
+                删除
+              </el-button> -->
             </section>
           </div>
         </header>
@@ -91,10 +172,40 @@
                 {{ EVALUATION_SYSTEM_STATUS_LABEL[String(scope.row.status)] || '-' }}
               </span>
             </template>
+            <template #isAdvancedGroup="scope">
+              <span>{{ scope.row.isAdvancedGroup ? '是' : '否' }}</span>
+            </template>
+            <template #evaluationDocument="scope">
+              <div
+                class="file-container--div"
+                v-for="item in parseAttachments(scope.row.evaluationDocument)"
+                :key="item.fileUrl"
+              >
+                <img
+                  class="file-container--div__icon"
+                  @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
+                  :src="FILE_TYPE_ICON[item.fileType]"
+                />
+                <span
+                  class="file-container--div__name"
+                  @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
+                  >{{ item.fileName }}</span
+                >
+                <img
+                  class="file-container--div__download"
+                  :src="DownloadIcon"
+                  @click="downloadFile(item.fileUrl, item.fileName)"
+                />
+              </div>
+            </template>
             <template #action="scope">
               <div class="action-container--div" style="justify-content: left">
+                <!-- 先进集体视图:只显示详情 -->
+                <template v-if="viewTab === 'dept' && operate === 'evaluationSystem-advanced-group'">
+                  <ActionButton text="详情" @click="handleViewDetail(scope.row.id)" />
+                </template>
                 <!-- 待反馈:删除 / 作废 -->
-                <template v-if="Number(scope.row.status) === 2">
+                <template v-else-if="Number(scope.row.status) === 2">
                   <ActionButton
                     text="删除"
                     :popconfirm="{
@@ -176,21 +287,78 @@
         </div>
       </div>
     </main>
+    <PreviewOnline ref="previewOnlineRef" />
   </div>
 </template>
 
 <script lang="ts" setup>
-  import { onMounted, reactive, ref } from 'vue';
+  import { computed, onMounted, reactive, ref, watch } from 'vue';
+  import { useRoute, useRouter } from 'vue-router';
   import BasicTable from '@/components/BasicTable.vue';
   import useTableConfig from '@/hooks/useTableConfigHook';
   import ActionButton from '@/components/ActionButton.vue';
   import { TABLE_OPTIONS } from '../configs/tables';
-  import { EVALUATION_TARGET_TABLE_COLUMNS } from '../configs/targetTables';
+  import {
+    EVALUATION_TARGET_TABLE_COLUMNS,
+    EVALUATION_ADVANCED_GROUP_TABLE_COLUMNS,
+    EVALUATION_ADVANCED_PERSON_TABLE_COLUMNS,
+  } from '../configs/targetTables';
   import { EVALUATION_SYSTEM_STATUS_OPTIONS, EVALUATION_SYSTEM_STATUS_LABEL } from '../configs/status';
   import type { QueryPageRequest } from '@/types/basic-query';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import type { DeptTree } from '@/types/dept/type';
+  import {
+    querySecurityExamineIssue,
+    querySecurityExamineDetail,
+    updateSecurityExamineIssueRepeal,
+    deleteSecurityExamineIssue,
+    querySecurityExamineIssueAdvanced,
+  } from '@/api/evaluationSystem';
+  import type { QuerySecurityExamineIssueParams, EvaluationSystemItem } from '@/api/evaluationSystem';
+  import { ElMessage } from 'element-plus';
+  import DownloadIcon from '@/views/disaster/disaster-control/src/svg/download.svg';
+  import { downloadFile } from '@/views/disaster/utils';
+  import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
+  import { FILE_TYPE_ICON } from '@/components/UploadFiles/constants';
+
+  const route = useRoute();
+  const router = useRouter();
+  const operate = route.query.operate as string | undefined;
+
+  // 考核表详情
+  const evaluationDetail = ref<Partial<EvaluationSystemItem>>({});
+
+  // 是否展示“状态 / 部门考核结果 / 先进个人”顶层 tabs
+  const showViewTab = computed(
+    () =>
+      operate === 'evaluationSystem-advanced-group' ||
+      operate === 'evaluationSystem-advanced-person',
+  );
+
+  // 顶部 tabs:按状态 / 按部门考核结果(先进集体排名)/ 先进个人
+  const viewTab = ref<'status' | 'dept' | 'person'>(
+    operate === 'evaluationSystem-advanced-group'
+      ? 'dept'
+      : operate === 'evaluationSystem-advanced-person'
+        ? 'person'
+        : 'status',
+  );
+  // 如果从上级页传入 singleStatus(例如 3:待评分),则只展示对应状态的 tab
+  const singleStatus = (route.query.singleStatus as string | undefined) || '';
+  const showAllTabs = computed(() => !singleStatus);
+
+  const singleTabLabel = computed(() => {
+    if (!singleStatus) return '全部';
+    const found = EVALUATION_SYSTEM_STATUS_OPTIONS.find(
+      (item) => String(item.value) === String(singleStatus),
+    );
+    return found?.label || '全部';
+  });
 
   // tabs(ALL:全部,其它为对应状态码字符串)
-  const activeTab = ref<'ALL' | '0' | '2' | '3' | '4' | '5' | '1'>('ALL');
+  const activeTab = ref<'ALL' | '0' | '2' | '3' | '4' | '5' | '1'>(
+    (singleStatus as any) || 'ALL',
+  );
 
   // 表格
   const basicTableRef = ref<InstanceType<typeof BasicTable>>();
@@ -199,19 +367,60 @@
 
   const tableData = ref<any[]>([]);
 
+  // 部门树(复用下发考核表的部门下拉逻辑)
+  const targetDeptCascaderRef = ref();
+  const deptTree = ref<DeptTree[]>([]);
+  const targetDeptId = ref<number | null>(null);
+  const cascaderDeptProp = {
+    checkStrictly: true,
+    expandTrigger: 'hover' as const,
+    value: 'id',
+    label: 'deptName',
+    emitPath: false,
+  };
+
   const tableQuery = reactive<QueryPageRequest<any>>({
     pageNumber: pagination.pageNumber,
     pageSize: pagination.pageSize,
     queryParam: {
       status: '',
       target: '',
-      keyword: '',
+      deptName: '', // 部门名称(用于部门考核结果视图)
+      userName: '', // 部门负责人(用于部门考核结果视图)
       dateRange: null as any,
       startTime: '',
       endTime: '',
     },
   });
 
+  // 从路由获取考核表ID(如果有)
+  const evaluationId = computed(() => {
+    const id = route.query.id;
+    return id ? Number(id) : undefined;
+  });
+
+  // 如果是单状态模式(例如待评分),初始化筛选条件
+  if (singleStatus) {
+    tableQuery.queryParam.status = Number(singleStatus);
+  }
+
+  // 根据顶部 tabs 切换表格列配置
+  watch(
+    viewTab,
+    (val) => {
+      if (val === 'status') {
+        tableConfig.columns = EVALUATION_TARGET_TABLE_COLUMNS;
+      } else if (val === 'dept') {
+        tableConfig.columns = EVALUATION_ADVANCED_GROUP_TABLE_COLUMNS;
+      } else {
+        tableConfig.columns = EVALUATION_ADVANCED_PERSON_TABLE_COLUMNS;
+      }
+      // 切换视图时重查
+      handleSearch();
+    },
+    { immediate: true },
+  );
+
   const handleSizeChange = (value: number) => {
     pagination.pageSize = value;
     tableQuery.pageSize = value;
@@ -226,16 +435,127 @@
 
   async function getTableData() {
     tableConfig.loading = true;
-    // TODO: 调用考核对象列表查询接口
-    // const res = await getEvaluationTargetList(tableQuery);
-    // tableData.value = res.records;
-    // pagination.total = res.totalRow;
-    tableData.value = [];
-    pagination.total = 0;
-    tableConfig.loading = false;
+    try {
+      // 如果是先进集体(部门考核结果视图),调用先进集体接口
+      if (viewTab.value === 'dept' && operate === 'evaluationSystem-advanced-group') {
+        const params = {
+          pageNumber: tableQuery.pageNumber,
+          pageSize: tableQuery.pageSize,
+          queryParam: {
+            psemId: evaluationId.value, // 考核表ID(从路由参数获取)
+            planStartTime: tableQuery.queryParam.startTime || undefined,
+            planEndTime: tableQuery.queryParam.endTime || undefined,
+            deptName: tableQuery.queryParam.deptName || undefined, // 部门名称
+            userName: tableQuery.queryParam.userName || undefined, // 部门负责人
+          },
+        };
+
+        const res = await querySecurityExamineIssueAdvanced(params);
+        if (res) {
+          // 映射返回数据字段到表格字段(先进集体排名表格)
+          tableData.value = res.records.map((item) => {
+            // 计算总分数、加分项分数、减分项分数
+            let totalScore = 0;
+            let addScore = 0;
+            let subtractScore = 0;
+
+            if (item.scores && item.scores.length > 0) {
+              item.scores.forEach((score: any) => {
+                const scoreValue = Number(score.reviewScore) || 0;
+                totalScore += scoreValue;
+                if (scoreValue >= 0) {
+                  addScore += scoreValue;
+                } else {
+                  subtractScore += Math.abs(scoreValue);
+                }
+              });
+            }
+
+            return {
+              id: item.id,
+              departmentName: item.deptName, // 部门考核结果
+              isAdvancedGroup: item.isAdvancedGroup || false, // 是否先进集体(需要接口返回,暂时使用 false)
+              departmentSort: item.scoreRank || 0, // 部门排序(使用排名)
+              departmentLeader: item.deptUserName || '-', // 部门负责人
+              totalScore, // 总分数
+              addScore, // 加分项分数
+              subtractScore, // 减分项分数
+              // 保留原始数据
+              psemId: item.psemId,
+              deptId: item.deptId,
+              scores: item.scores,
+              scoreRank: item.scoreRank,
+            };
+          });
+          pagination.total = res.totalRow;
+        }
+      } else if (viewTab.value === 'status') {
+        // 状态视图下调用原有接口
+        const params: QuerySecurityExamineIssueParams = {
+          pageNumber: tableQuery.pageNumber,
+          pageSize: tableQuery.pageSize,
+          queryParam: {
+            psemId: evaluationId.value, // 考核表ID(从路由参数获取)
+            planStartTime: tableQuery.queryParam.startTime || undefined,
+            planEndTime: tableQuery.queryParam.endTime || undefined,
+            deptName: tableQuery.queryParam.target || undefined,
+            status:
+              tableQuery.queryParam.status !== '' && tableQuery.queryParam.status !== null
+                ? Number(tableQuery.queryParam.status)
+                : undefined,
+          },
+        };
+
+        const res = await querySecurityExamineIssue(params);
+        if (res) {
+          // 映射返回数据字段到表格字段
+          tableData.value = res.records.map((item) => ({
+            id: item.id,
+            evaluationTableName: item.exName, // 考核表名称
+            status: item.status, // 状态
+            statusName: item.statusName, // 状态名称
+            issueDepartment: item.deptName, // 下发部门(部门考核结果)
+            departmentLeader: item.deptUserName || '-', // 部门负责人
+            contactPhone: item.deptUserLink || '-', // 联系方式
+            evaluationDocument: item.attachments, // 考核文档
+            plannedCompletionTime: item.planEndTime || '-', // 计划完成时间(使用计划结束时间)
+            // 保留原始数据,供其他操作使用
+            psemId: item.psemId,
+            deptId: item.deptId,
+            scores: item.scores,
+            scoreRank: item.scoreRank,
+          }));
+          pagination.total = res.totalRow;
+        }
+      } else {
+        // 其他视图(如先进个人)暂时为空
+        tableData.value = [];
+        pagination.total = 0;
+      }
+    } catch (e) {
+      console.error('获取列表失败:', e);
+      tableData.value = [];
+      pagination.total = 0;
+    } finally {
+      tableConfig.loading = false;
+    }
   }
 
-  const handleSearch = () => {
+  const getDeptTreeData = async () => {
+    try {
+      const res = await getAllDepartments();
+      deptTree.value = res?.[0]?.children ?? [];
+    } catch (e) {
+      console.error('获取部门树失败:', e);
+    }
+  };
+
+  const handleTargetDeptChange = () => {
+    const nodes = targetDeptCascaderRef.value?.getCheckedNodes?.();
+    tableQuery.queryParam.target = nodes?.[0]?.label ?? '';
+  };
+
+  function handleSearch() {
     if (tableQuery.queryParam.dateRange && tableQuery.queryParam.dateRange.length === 2) {
       tableQuery.queryParam.startTime = tableQuery.queryParam.dateRange[0];
       tableQuery.queryParam.endTime = tableQuery.queryParam.dateRange[1];
@@ -246,13 +566,15 @@
     pagination.pageNumber = 1;
     tableQuery.pageNumber = 1;
     getTableData();
-  };
+  }
 
   const handleReset = () => {
     tableQuery.queryParam.status = '';
     tableQuery.queryParam.target = '';
-    tableQuery.queryParam.keyword = '';
+    tableQuery.queryParam.deptName = '';
+    tableQuery.queryParam.userName = '';
     tableQuery.queryParam.dateRange = null;
+    targetDeptId.value = null;
     handleSearch();
   };
 
@@ -275,28 +597,142 @@
     console.log('batch delete evaluation targets');
   };
 
-  const handleDelete = (id: number) => {
-    // TODO: 单条删除
-    console.log('delete evaluation target', id);
+  const handleDelete = async (id: number) => {
+    try {
+      await deleteSecurityExamineIssue(id);
+      ElMessage.success('删除成功');
+      getTableData();
+    } catch (e: any) {
+      console.error('删除失败:', e);
+      ElMessage.error(e?.message || '删除失败,请重试');
+    }
   };
 
-  const handleCancel = (id: number) => {
-    // TODO: 作废
-    console.log('cancel evaluation target', id);
+  const handleCancel = async (id: number) => {
+    try {
+      await updateSecurityExamineIssueRepeal(id);
+      ElMessage.success('作废成功');
+      getTableData();
+    } catch (e: any) {
+      console.error('作废失败:', e);
+      ElMessage.error(e?.message || '作废失败,请重试');
+    }
   };
 
   const handleScore = (id: number) => {
-    // TODO: 评分
-    console.log('score evaluation target', id);
+    router.push({
+      name: 'EvaluationSystemItem',
+      query: {
+        id,
+        operate: 'evaluationSystem-feedback',
+      },
+    });
   };
 
   const handleAudit = (id: number) => {
-    // TODO: 审核
-    console.log('audit evaluation target', id);
+    router.push({
+      name: 'EvaluationSystemItem',
+      query: {
+        id,
+        operate: 'evaluationSystem-audit',
+      },
+    });
+  };
+
+  const handleViewDetail = (id: number) => {
+    router.push({
+      name: 'EvaluationSystemItem',
+      query: {
+        id,
+        operate: 'evaluationSystem-feedback-view',
+      },
+    });
+  };
+
+  // 预览
+  const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
+  const previewOnline = (url: string | undefined, type: keyof typeof FILE_TYPE_ICON) => {
+    if (url) {
+      previewOnlineRef.value?.open(url, type);
+    }
+  };
+
+  // 解析逗号分隔的URL字符串为文件列表
+  const parseAttachments = (attachmentsStr: string | undefined): Array<{
+    fileUrl: string;
+    fileName: string;
+    fileType: string;
+  }> => {
+    if (!attachmentsStr || !attachmentsStr.trim()) {
+      return [];
+    }
+
+    // 按逗号分割URL
+    const urls = attachmentsStr.split(',').map(url => url.trim()).filter(url => url);
+
+    return urls.map((url) => {
+      // 从URL中提取文件名
+      const urlParts = url.split('/');
+      const fileName = urlParts[urlParts.length - 1] || '未知文件';
+
+      // 根据文件扩展名判断文件类型
+      const extension = fileName.split('.').pop()?.toLowerCase() || '';
+      let fileType = 'pdf';
+      if (extension === 'doc' || extension === 'docx') {
+        fileType = 'word';
+      } else if (extension === 'xls' || extension === 'xlsx') {
+        fileType = 'excel';
+      } else if (extension === 'ppt' || extension === 'pptx') {
+        fileType = 'ppt';
+      }
+
+      return {
+        fileUrl: url,
+        fileName,
+        fileType,
+      };
+    });
+  };
+
+  // 格式化日期时间
+  const formatDateTime = (dateTimeStr: string | undefined): string => {
+    if (!dateTimeStr) return '';
+    try {
+      const date = new Date(dateTimeStr);
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      const hours = String(date.getHours()).padStart(2, '0');
+      const minutes = String(date.getMinutes()).padStart(2, '0');
+      const seconds = String(date.getSeconds()).padStart(2, '0');
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    } catch (e) {
+      return dateTimeStr;
+    }
+  };
+
+  // 获取考核表详情
+  const getEvaluationDetail = async () => {
+    if (!evaluationId.value) return;
+    try {
+      const res = await querySecurityExamineDetail(evaluationId.value);
+      if (res) {
+        evaluationDetail.value = {
+          exName: res.exName,
+          deptNames: res.deptNames,
+          createdUserName: res.createdUserName,
+          createdAt: res.createdAt,
+        };
+      }
+    } catch (e) {
+      console.error('获取考核表详情失败:', e);
+    }
   };
 
   onMounted(() => {
+    getDeptTreeData();
     getTableData();
+    getEvaluationDetail();
   });
 </script>
 
@@ -304,10 +740,34 @@
   @use '@/styles/page-details-layout.scss' as *;
   @use '@/styles/page-main-layout.scss' as *;
   @use '@/styles/basic-table-action.scss' as *;
+  @use '@/styles/basic-table-file.scss' as *;
   @use '@/views/traffic/violation/style/act-search-table.scss' as *;
 
   .safety-platform-container__header {
     padding-bottom: 0 !important;
   }
+
+  .evaluation-header {
+    width: 100%;
+  }
+
+  .evaluation-title {
+    font-size: 20px;
+    font-weight: bold;
+    margin: 0 0 12px 0;
+    color: #333;
+  }
+
+  .evaluation-meta {
+    margin-bottom: 12px;
+    display: flex;
+    gap: 24px;
+    font-size: 14px;
+    color: #666;
+  }
+
+  .evaluation-meta span {
+    white-space: nowrap;
+  }
 </style>
 

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

@@ -65,7 +65,7 @@ export const EVALUATION_SYSTEM_TABLE_COLUMNS: TableColumnProps[] = [
     prop: 'action',
     slot: 'action',
     fixed: 'right',
-    width: '180px',
+    minWidth: '300px',
     align: 'left',
   },
 ];

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

@@ -35,6 +35,7 @@ export const EVALUATION_TARGET_TABLE_COLUMNS: TableColumnProps[] = [
   {
     label: '考核文档',
     prop: 'evaluationDocument',
+    slot: 'evaluationDocument',
     align: 'left',
     minWidth: '150px',
   },
@@ -46,11 +47,123 @@ export const EVALUATION_TARGET_TABLE_COLUMNS: TableColumnProps[] = [
   },
   {
     label: '操作',
-    prop: 'action',
+    // prop: 'action',
     slot: 'action',
     fixed: 'right',
-    width: '220px',
+    minWidth: '300px',
     align: 'left',
   },
 ];
 
+// 先进集体排名表格列
+export const EVALUATION_ADVANCED_GROUP_TABLE_COLUMNS: TableColumnProps[] = [
+  {
+    label: '编号',
+    type: 'index',
+    align: 'center',
+    width: '80px',
+  },
+  {
+    label: '部门名称',
+    prop: 'departmentName',
+    align: 'left',
+    minWidth: '150px',
+  },
+  {
+    label: '是否先进集体',
+    prop: 'isAdvancedGroup',
+    slot: 'isAdvancedGroup',
+    align: 'center',
+    minWidth: '120px',
+  },
+  {
+    label: '部门排序',
+    prop: 'departmentSort',
+    align: 'center',
+    minWidth: '120px',
+  },
+  {
+    label: '部门负责人',
+    prop: 'departmentLeader',
+    align: 'left',
+    minWidth: '140px',
+  },
+  {
+    label: '总分数',
+    prop: 'totalScore',
+    align: 'center',
+    minWidth: '100px',
+  },
+  {
+    label: '加分项分数',
+    prop: 'addScore',
+    align: 'center',
+    minWidth: '120px',
+  },
+  {
+    label: '减分项分数',
+    prop: 'subtractScore',
+    align: 'center',
+    minWidth: '120px',
+  },
+  {
+    label: '操作',
+    slot: 'action',
+    fixed: 'right',
+    minWidth: '100px',
+    align: 'left',
+  },
+];
+
+// 先进个人信息表格列
+export const EVALUATION_ADVANCED_PERSON_TABLE_COLUMNS: TableColumnProps[] = [
+  {
+    label: '员工工号',
+    prop: 'employeeCode',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '员工姓名',
+    prop: 'employeeName',
+    align: 'left',
+    minWidth: '120px',
+  },
+  {
+    label: '所属部门',
+    prop: 'deptName',
+    align: 'left',
+    minWidth: '150px',
+  },
+  {
+    label: '联系方式',
+    prop: 'contactPhone',
+    align: 'left',
+    minWidth: '140px',
+  },
+  {
+    label: '部门负责人',
+    prop: 'departmentLeader',
+    align: 'left',
+    minWidth: '140px',
+  },
+  {
+    label: '备注',
+    prop: 'remark',
+    align: 'left',
+    minWidth: '160px',
+  },
+  {
+    label: '考核文档',
+    prop: 'evaluationDocument',
+    slot: 'evaluationDocument',
+    align: 'left',
+    minWidth: '150px',
+  },
+  {
+    label: '计划完成时间',
+    prop: 'plannedCompletionTime',
+    align: 'left',
+    minWidth: '150px',
+  },
+];

+ 184 - 24
src/views/production-safety/safetyAssessment/evaluationSystem/evaluationSystem.vue

@@ -169,7 +169,7 @@
                   <ActionButton text="考核对象" @click="handleTarget(scope.row.id)" />
                 </template>
 
-                <!-- 已完成:删除 / 考核对象 / 先进集体 / 先进个人 -->
+                <!-- 已完成:删除 / 考核对象 / 部门排序 / 先进个人 -->
                 <template v-else-if="Number(scope.row.status) === 1">
                   <ActionButton
                     text="删除"
@@ -179,7 +179,7 @@
                     @confirm="handleDelete(scope.row.id)"
                   />
                   <ActionButton text="考核对象" @click="handleTarget(scope.row.id)" />
-                  <ActionButton text="先进集体" @click="handleAdvancedGroup(scope.row.id)" />
+                  <ActionButton text="部门排序" @click="handleAdvancedGroup(scope.row.id)" />
                   <ActionButton text="先进个人" @click="handleAdvancedIndividual(scope.row.id)" />
                 </template>
               </div>
@@ -190,9 +190,48 @@
 
       <!-- 下发弹窗 -->
       <el-dialog v-model="issueDialogVisible" title="下发考核表" width="480px" destroy-on-close>
-        <el-form :model="issueForm" label-width="100px">
+        <el-form :model="issueForm" label-width="130px">
           <el-form-item label="部门名称:">
-            <el-input v-model="issueForm.departmentName" placeholder="请输入部门名称" />
+            <el-cascader
+              ref="issueDeptCascaderRef"
+              v-model="issueDeptId"
+              :options="deptTree"
+              :props="cascaderDeptProp"
+              :show-all-levels="false"
+              placeholder="请选择部门"
+              filterable
+              @change="handleIssueDeptChange"
+            />
+          </el-form-item>
+          <el-form-item label="用户名称:">
+            <el-select
+              v-model="issueForm.userGroupId"
+              placeholder="请选择用户组"
+              filterable
+              clearable
+            >
+              <el-option
+                v-for="item in userGroupOptions"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="部门自评审核人:">
+            <el-select
+              v-model="issueForm.deptSelfApproveUserId"
+              placeholder="请选择部门自评审核人"
+              filterable
+              clearable
+            >
+              <el-option
+                v-for="user in deptSelfApproveUserList"
+                :key="user.id"
+                :label="user.realname"
+                :value="user.id"
+              />
+            </el-select>
           </el-form-item>
           <el-form-item label="计划开始日期:">
             <el-date-picker
@@ -234,13 +273,24 @@
   import { EVALUATION_SYSTEM_STATUS_OPTIONS, EVALUATION_SYSTEM_STATUS_LABEL } from './configs/status';
   import { useRouter } from 'vue-router';
   import type { QueryPageRequest } from '@/types/basic-query';
-  import { querySecurityExamine, deleteSecurityExamine } from '@/api/evaluationSystem';
+  import {
+    querySecurityExamine,
+    deleteSecurityExamine,
+    saveSecurityExamineIssue,
+    updateSecurityExamineRepeal,
+  } from '@/api/evaluationSystem';
   import type { EvaluationSystemQueryParam } from '@/api/evaluationSystem';
   import { ElMessage } from 'element-plus';
   import DownloadIcon from '@/views/disaster/disaster-control/src/svg/download.svg';
   import { downloadFile } from '@/views/disaster/utils';
   import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
   import { FILE_TYPE_ICON } from '@/components/UploadFiles/constants';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import type { DeptTree } from '@/types/dept/type';
+  import { queryUserGroupPage } from '@/api/system/person-group';
+  import type { PersonGroupListItem } from '@/types/person-group/type';
+  import { getUserList } from '@/api/system/user-operate';
+  import type { UserLisItem } from '@/api/system/user-operate';
 
   const router = useRouter();
 
@@ -261,7 +311,66 @@
     departmentName: '',
     startDate: '',
     endDate: '',
+    userGroupId: undefined as number | undefined,
+    deptSelfApproveUserId: undefined as number | undefined, // 部门自评审核人ID
   });
+  const issueDeptId = ref<number | null>(null);
+
+  // 用户组(用于选择用户名称)
+  const userGroupOptions = ref<PersonGroupListItem[]>([]);
+
+  // 部门自评审核人用户列表
+  const deptSelfApproveUserList = ref<UserLisItem[]>([]);
+
+  // 部门树(复用物品领取记录的部门字段下拉框逻辑)
+  const issueDeptCascaderRef = ref();
+  const deptTree = ref<DeptTree[]>([]);
+  const cascaderDeptProp = {
+    checkStrictly: true,
+    expandTrigger: 'hover' as const,
+    value: 'id',
+    label: 'deptName',
+    emitPath: false,
+  };
+
+  const getDeptTreeData = async () => {
+    try {
+      const res = await getAllDepartments();
+      deptTree.value = res?.[0]?.children ?? [];
+    } catch (e) {
+      console.error('获取部门树失败:', e);
+    }
+  };
+
+  // 获取用户组列表(用于下发时选择用户名称)
+  const getUserGroupOptions = async () => {
+    try {
+      const res = await queryUserGroupPage({
+        pageNumber: 1,
+        pageSize: 9999,
+        queryParam: '',
+      });
+      userGroupOptions.value = res?.records ?? [];
+    } catch (e) {
+      console.error('获取用户组列表失败:', e);
+      userGroupOptions.value = [];
+    }
+  };
+
+  // 获取部门自评审核人用户列表
+  const getDeptSelfApproveUserList = async () => {
+    try {
+      const res = await getUserList({
+        pageNumber: 1,
+        pageSize: 9999,
+        queryParam: {}, // 不传递 deptId 参数
+      });
+      deptSelfApproveUserList.value = res?.records || [];
+    } catch (e) {
+      console.error('获取部门自评审核人列表失败:', e);
+      deptSelfApproveUserList.value = [];
+    }
+  };
 
   const tableQuery = reactive<QueryPageRequest<EvaluationSystemQueryParam>>({
     pageNumber: pagination.pageNumber,
@@ -378,34 +487,77 @@
     issueForm.departmentName = '';
     issueForm.startDate = '';
     issueForm.endDate = '';
+    issueForm.userGroupId = undefined;
+    issueForm.deptSelfApproveUserId = undefined;
+    issueDeptId.value = null;
     issueDialogVisible.value = true;
   };
 
-  const handleIssueConfirm = () => {
-    // TODO: 调用下发接口,使用 currentIssueId 和 issueForm
-    console.log('issue confirm', {
-      id: currentIssueId.value,
-      ...issueForm,
-    });
-    issueDialogVisible.value = false;
+  const handleIssueDeptChange = () => {
+    const nodes = issueDeptCascaderRef.value?.getCheckedNodes?.();
+    issueForm.departmentName = nodes?.[0]?.label ?? '';
+  };
+
+  const handleIssueConfirm = async () => {
+    if (!currentIssueId.value) {
+      ElMessage.error('缺少考核表ID');
+      return;
+    }
+
+    try {
+      const payload = {
+        id: currentIssueId.value,
+        deptNames: issueForm.departmentName,
+        deptIds: issueDeptId.value || 0,
+        getUserGroupId: issueForm.userGroupId,
+        deptSelfApproveUserId: issueForm.deptSelfApproveUserId,
+        planStartTime: issueForm.startDate || undefined,
+        planEndTime: issueForm.endDate || undefined,
+      };
+      await saveSecurityExamineIssue(payload);
+      ElMessage.success('下发成功');
+      issueDialogVisible.value = false;
+      getTableData();
+    } catch (e) {
+      console.error('下发失败:', e);
+      ElMessage.error('下发失败,请重试');
+    }
   };
 
   // 作废
-  const handleCancel = (id: number) => {
-    // TODO: 作废操作
-    console.log('cancel', id);
+  const handleCancel = async (id: number) => {
+    try {
+      await updateSecurityExamineRepeal(id);
+      ElMessage.success('作废成功');
+      getTableData();
+    } catch (e: any) {
+      console.error('作废失败:', e);
+      ElMessage.error(e?.message || '作废失败,请重试');
+    }
   };
 
-  // 评分
+  // 评分:跳转到考核对象列表,并仅展示“待评分”tab
   const handleScore = (id: number) => {
-    // TODO: 评分操作
-    console.log('score', id);
+    router.push({
+      name: 'EvaluationSystemItem',
+      query: {
+        id,
+        operate: 'evaluationSystem-target',
+        singleStatus: '3', // 3 表示“待评分”
+      },
+    });
   };
 
-  // 审核
+  // 审核:跳转到考核对象列表,并仅展示“待审核”tab
   const handleAudit = (id: number) => {
-    // TODO: 审核操作
-    console.log('audit', id);
+    router.push({
+      name: 'EvaluationSystemItem',
+      query: {
+        id,
+        operate: 'evaluationSystem-target',
+        singleStatus: '4', // 4 表示“待审核”
+      },
+    });
   };
 
   // 考核对象
@@ -419,10 +571,15 @@
     });
   };
 
-  // 先进集体
+  // 部门排序
   const handleAdvancedGroup = (id: number) => {
-    // TODO: 先进集体操作
-    console.log('advanced group', id);
+    router.push({
+      name: 'EvaluationSystemItem',
+      query: {
+        id,
+        operate: 'evaluationSystem-advanced-group',
+      },
+    });
   };
 
   // 先进个人
@@ -488,6 +645,9 @@
 
   onMounted(() => {
     getTableData();
+    getDeptTreeData();
+    getUserGroupOptions();
+    getDeptSelfApproveUserList();
   });
 </script>
 

+ 22 - 4
src/views/production-safety/safetyAssessment/pointDeduction/components/PointDeductionDetail.vue

@@ -36,10 +36,11 @@
                 <el-input-number
                   v-if="!isViewMode"
                   v-model="row.scoreVal"
-                  :min="0"
+                  :min="1"
                   :precision="0"
                   size="small"
                   style="width: 100%"
+                  @change="calculateTotalScore"
                 />
                 <span v-else>{{ row.scoreVal }}</span>
               </template>
@@ -120,6 +121,12 @@
     });
   };
 
+  // 自动计算总扣分值
+  const calculateTotalScore = () => {
+    const totalScore = ruleFormData.dedResonList.reduce((sum, item) => sum + (item.scoreVal || 0), 0);
+    ruleFormData.deductionValue = totalScore;
+  };
+
   // 扣分原因列表操作 - 在指定位置后新增
   const handleAddReasonAfter = (index: number) => {
     ruleFormData.dedResonList.splice(index + 1, 0, {
@@ -127,15 +134,17 @@
       pmdId: currentId.value || 0,
       serialNum: 0, // 会自动排序
       deductionReason: '',
-      scoreVal: 0,
+      scoreVal: 1, // 默认值为1
     });
     updateSerialNumbers();
+    calculateTotalScore();
   };
 
   // 删除扣分原因
   const handleDeleteReason = (index: number) => {
     ruleFormData.dedResonList.splice(index, 1);
     updateSerialNumbers();
+    calculateTotalScore();
   };
 
   const handleValidate = async () => {
@@ -161,6 +170,9 @@
       }
     }
     
+    // 计算总扣分值
+    calculateTotalScore();
+    
     return true;
   };
 
@@ -175,8 +187,14 @@
         ruleFormData.departmentName = res.departmentName;
         ruleFormData.status = res.status ? 'ENABLE' : 'DISABLE';
         ruleFormData.dedResonList = res.dedResonList || [];
-        // 确保编号排序
+        // 确保编号排序和分值默认值
+        ruleFormData.dedResonList.forEach((item) => {
+          if (!item.scoreVal || item.scoreVal === 0) {
+            item.scoreVal = 1;
+          }
+        });
         updateSerialNumbers();
+        calculateTotalScore();
       }
       cloneRuleFormData();
     } catch (e) {
@@ -234,7 +252,7 @@
           pmdId: 0,
           serialNum: 1,
           deductionReason: '',
-          scoreVal: 0,
+          scoreVal: 1, // 默认值为1
         },
       ];
     } else {

+ 1 - 0
src/views/production-safety/safetyAssessment/pointDeduction/configs/form.ts

@@ -27,6 +27,7 @@ export const POINT_DEDUCTION_FORM_CONFIG: FormConfig[] = [
       min: 1,
       precision: 0, // 不允许小数点,只能输入整数
       placeholder: '请输入扣分值',
+      disabled: true, // 禁用扣分值字段
     },
   },
   {

+ 5 - 2
src/views/production-safety/safetyAssessment/pointDeduction/pointDeduction.vue

@@ -290,11 +290,12 @@
   @use '@/views/traffic/violation/style/act-search-table.scss' as *;
 
   .deduction-reason-list {
-    padding: 8px 0;
+    width: 100%;
+    /* padding: 8px 0; */
   }
 
   .deduction-reason-item {
-    padding: 4px 0;
+    /* padding: 4px 0; */
   }
 
   .reason-content {
@@ -320,8 +321,10 @@
 
   .reason-divider {
     height: 1px;
+    width: 100%;
     background-color: #dcdfe6;
     margin: 8px 0;
+    display: block;
   }
 
   .no-reason {