فهرست منبع

merge:合并风险识别与管控-工伤认定等模块

sunqijun 2 ماه پیش
والد
کامیت
b426f41421
13فایلهای تغییر یافته به همراه1771 افزوده شده و 136 حذف شده
  1. 105 22
      src/api/inventory/index.ts
  2. 131 0
      src/api/production-safety/business-registration-application.ts
  3. 55 8
      src/api/production-safety/personal-protective-equipment-purchase-apply.ts
  4. 230 17
      src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/components/detail.vue
  5. 85 0
      src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/configs/constant.ts
  6. 28 12
      src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/list.vue
  7. 278 0
      src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/listAdmin.vue
  8. 2 0
      src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/Item.vue
  9. 555 17
      src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/components/detail.vue
  10. 85 0
      src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/configs/constant.ts
  11. 80 28
      src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/configs/form.ts
  12. 27 10
      src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/configs/tables.ts
  13. 110 22
      src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/list.vue

+ 105 - 22
src/api/inventory/index.ts

@@ -4,10 +4,10 @@ import type { QueryPageRequest, QueryPageResponse } from '@/types/basic-query';
 /**
  * 查询物品信息返回对象
  */
-export interface InventoryItem {
+export interface BusinessCertification {
   id: number;
   stuffName: string; // 物品名称
-  inStoreTime: string; // 入库日期
+  injuryTime: string; // 入库日期
   stuffQty: number; // 物品数量
   surplusQty: number; // 物品结余数量
   remark: string; // 备注
@@ -17,22 +17,86 @@ export interface InventoryItem {
   updatedAt: string; // 更新时间
   isDeleted: number; // 0-未删除,大于0(时间戳)-已删除
   statusName: string; // 状态名称
+  accidentCertUrl: string; // 事故报告URL
+  powerAttorneyUrl: string; // 授权委托书URL
+  addressConfirmUrl: string; // 地址确认URL
+  applicationFormUrl: string; // 申请表URL
+  idCardUrl: string; // 身份证正反面URL
+  laborContractUrl: string; // 劳动合同URL
+  initialMedicalCertUrl: string; // 初次医疗证明URL
+  trusteeIdCardUrl: string; // 被委托人员身份证正反面URL
+  applicantName: string; // 申请人姓名
+  applicantCode: string; // 申请人工号
+  injuryReason: string; // 受伤原因
+  departmentCode: string; // 部门编码
+  templateId?: any; // 审批模板ID
 }
 
 /**
  * 查询物品信息请求参数
  */
-export interface InventoryQueryParam {
+export interface BusinessQueryParam {
   stuffName?: string; // 物品名称
   status?: boolean; // 状态
   ids?: string[]; // 选择数据的ID
 }
 
+/** 审批流程参数 */
+export interface ApprovalProcessParam {
+  id: string;
+  approvalDescription: string;
+  templateId?: number;
+  approvalInfoList: {
+    approvalOrder: number;
+    approverIdList: number[];
+  }[];
+}
+
+/** 审核参数(兼容旧调用:映射到 SaveApprovalReq) */
+export interface AuditPurchaseApplyParam {
+  id: number;
+  /** 1-审核通过,-1-审核不通过 → 映射 approvalStatus 2/3 */
+  status: number;
+  rejectReason?: string; // 同 returnReason
+  approvalOrder?: number;
+  templateId?: string;
+  code?: string;
+}
+
+/** 提交审批 - 通过/退回 SaveApprovalReq */
+export interface SaveApprovalReq {
+  id: number;
+  approvalOrder?: number;
+  /** 审批状态: 1-待审批,2-已审批,3-退回 */
+  approvalStatus: number;
+  /** 退回原因 */
+  returnReason?: string;
+  templateId?: string;
+}
+
+/**
+ * 保存物品信息请求参数
+ */
+export interface SaveInventoryRequest {
+  applicantCode: string; // 申请人工号
+  applicantName: string; // 申请人姓名
+  injuryTime: string; // 入库日期 (ISO 格式)
+  injuryReason: string; // 受伤原因
+  accidentCertUrl: string; // 事故报告URL
+  powerAttorneyUrl: string; // 授权委托书URL
+  addressConfirmUrl: string; // 地址确认URL
+  applicationFormUrl: string; // 申请表URL
+  idCardUrl: string; // 身份证正反面URL
+  laborContractUrl: string; // 劳动合同URL
+  initialMedicalCertUrl: string; // 初次医疗证明URL
+  trusteeIdCardUrl: string; // 被委托人员身份证正反面URL
+}
+
 /**
  * 查询物品库存管理列表
  */
-export function queryInventoryManage(query: QueryPageRequest<InventoryQueryParam>) {
-  return http.request<QueryPageResponse<InventoryItem>>({
+export function queryInventoryManage(query: QueryPageRequest<BusinessQueryParam>) {
+  return http.request<QueryPageResponse<BusinessCertification>>({
     url: '/inventory/queryInventoryManage',
     method: 'post',
     data: query,
@@ -40,21 +104,11 @@ export function queryInventoryManage(query: QueryPageRequest<InventoryQueryParam
 }
 
 /**
- * 保存物品信息请求参数
- */
-export interface SaveInventoryRequest {
-  stuffName: string; // 物品名称
-  inStoreTime: string; // 入库日期 (ISO 格式)
-  stuffQty: number; // 物品数量
-  remark: string; // 备注
-}
-
-/**
- * 保存物品库存(新增)
+ * 保存业务信息
  */
-export function saveInventory(data: SaveInventoryRequest) {
+export function saveBusinessInformation(data: SaveInventoryRequest) {
   return http.request({
-    url: '/inventory/saveInventory',
+    url: '/WorkInjuryApply/saveWorkInjuryApply',
     method: 'post',
     data,
   });
@@ -90,12 +144,12 @@ export function deleteInventory(id: number) {
 }
 
 /**
- * 查询物品库存详情
+ * 查询工伤认定详情
  */
 export function queryInventoryDetail(id: number) {
-  return http.request<InventoryItem>({
-    url: `/inventory/queryInventoryDetail?id=${id}`,
-    method: 'get',
+  return http.request<BusinessCertification>({
+    url: `/WorkInjuryApply/queryWorkInjuryApplyDetail?id=${id}`,
+    method: 'post',
   });
 }
 
@@ -127,3 +181,32 @@ export function exportInventory(query: ExportInventoryRequest) {
   });
 }
 
+/** 提交审批流程 */
+export const submitApprovalProcess = (data: ApprovalProcessParam) => {
+  return http.request({
+    url: '/WorkInjuryApply/submit',
+    method: 'post',
+    data,
+  });
+};
+
+/** 审核劳防用品采购申请(通过/退回)- 兼容旧接口,内部调 saveApproval */
+export function auditPurchaseApply(data: AuditPurchaseApplyParam) {
+  return saveApproval({
+    id: data.id,
+    approvalStatus: data.status === 2 ? 2 : 3, // 3审核不通过 4已完成
+    returnReason: data.rejectReason,
+    approvalOrder: data.approvalOrder,
+    templateId: data.templateId,
+    code: data.code,
+  });
+}
+
+/** 提交审批(通过/退回) */
+export function saveApproval(data: SaveApprovalReq) {
+  return http.request({
+    url: '/WorkInjuryApply/saveApproval',
+    method: 'post',
+    data,
+  });
+}

+ 131 - 0
src/api/production-safety/business-registration-application.ts

@@ -0,0 +1,131 @@
+  import { http } from '@/utils/http/axios';
+import type { QueryPageRequest, QueryPageResponse } from '@/types/basic-query';
+
+/**
+ * 查询工商认定信息返回对象
+ */
+export interface InventoryItem {
+  id: number;
+  applyNo: string; // 申请单号
+  applicantCode: string; // 工号
+  applicantName: string; // 姓名
+  departmentName: string; // 所属部门
+  injuryReason: string; // 受伤原因
+  injuryTime: string; // 受伤时间
+  injuryCategoryName: string; // 工伤类别
+  remark: string; // 备注
+  status: boolean; // 状态
+  approvalTemplateId: string; // 审批模板ID
+  approvalOrder: number; // 审批订单
+  templateId: string; // 模板ID
+  injuryCategoryCode: string; // 工伤类别编码
+}
+
+/**
+ * 查询工商认定信息请求参数
+ */
+export interface InventoryQueryParam {
+  stuffName?: string; // 物品名称
+  status?: boolean; // 状态
+  ids?: string[]; // 选择数据的ID
+}
+
+/**
+ * 查询物品库存管理列表
+ */
+export function queryWorkInjuryApplyList(query: QueryPageRequest<InventoryQueryParam>) {
+  return http.request<QueryPageResponse<InventoryItem>>({
+    url: '/WorkInjuryApply/queryWorkInjuryApplyList',
+    method: 'post',
+    data: query,
+  });
+}
+
+/**
+ * 保存工商认定信息请求参数
+ */
+export interface SaveInventoryRequest {
+  stuffName: string; // 物品名称
+  inStoreTime: string; // 入库日期 (ISO 格式)
+  stuffQty: number; // 物品数量
+  remark: string; // 备注
+}
+
+/**
+ * 保存物品库存(新增)
+ */
+export function saveInventory(data: SaveInventoryRequest) {
+  return http.request({
+    url: '/inventory/saveInventory',
+    method: 'post',
+    data,
+  });
+}
+
+/**
+ * 更新工商认定信息请求参数
+ */
+export interface UpdateInventoryRequest extends SaveInventoryRequest {
+  id: number; // 物品ID
+}
+
+/**
+ * 更新物品库存(编辑)
+ */
+export function updateInventory(data: UpdateInventoryRequest) {
+  return http.request({
+    url: '/inventory/updateInventory',
+    method: 'put',
+    data,
+  });
+}
+
+/**
+ * 删除物品库存
+ */
+export function deleteInventory(id: number) {
+  return http.request({
+    url: `/inventory/deleteInventory?id=${id}`,
+    method: 'delete',
+    // data: { id },
+  });
+}
+
+/**
+ * 查询物品库存详情
+ */
+export function queryInventoryDetail(id: number) {
+  return http.request<InventoryItem>({
+    url: `/inventory/queryInventoryDetail?id=${id}`,
+    method: 'get',
+  });
+}
+
+/**
+ * 导入物品库存
+ * 注意:导入功能使用 BatchImport 组件,直接通过 URL 上传文件
+ */
+
+/**
+ * 导出物品库存请求参数
+ */
+export interface ExportInventoryRequest {
+  stuffName?: string; // 物品名称
+  status?: boolean; // 状态
+  ids?: string[]; // 选择数据的ID
+}
+
+/**
+ * 导出物品库存
+ */
+export function exportInventory(query: ExportInventoryRequest) {
+  return http.request({
+    url: '/inventory/exportInventory',
+    method: 'post',
+    data: query,
+    responseType: 'blob',
+  }, {
+    isTransformResponse: false,
+  });
+}
+

+ 55 - 8
src/api/production-safety/personal-protective-equipment-purchase-apply.ts

@@ -79,6 +79,19 @@ export interface QueryPurchaseApplyPageReq {
   applyDeptCode?: string;
   /** 申请人部门名称 */
   applyDeptName?: string;
+  /** 审批状态:1-待审批,2-已审批,3-退回 */
+  approvalStatus?: number | string;
+}
+
+/** 审批流程参数 */
+export interface ApprovalProcessParam {
+  id: string;
+  approvalDescription: string;
+  templateId?: number;
+  approvalInfoList: {
+    approvalOrder: number;
+    approverIdList: number[];
+  }[];
 }
 
 /** 分页查询劳防用品采购申请列表 */
@@ -92,21 +105,30 @@ export function queryPurchaseApplyList(
   });
 }
 
+/** 分页查询劳防用品采购申请列表 */
+export function queryPurchaseApplyListAdmin(
+  query: QueryPageRequest<QueryPurchaseApplyPageReq>,
+) {
+  return http.request<QueryPageResponse<PpePurchaseApply>>({
+    url: '/ppePurchaseApply/queryPpePurchaseApplyListAdmin',
+    method: 'post',
+    data: query,
+  });
+}
+
 /** 查询劳防用品详细内容(返回明细列表) */
 export function queryPurchaseApplyDetail(id: number) {
   return http.request<PpePurchaseApplyDetail[]>({
-    url: '/ppePurchaseApply/queryPpePurchaseApplyDetail',
+    url: `/ppePurchaseApply/queryPpePurchaseApplyDetail?id=${id}`,
     method: 'post',
-    params: { id },
   });
 }
 
 /** 删除劳防用品采购申请 */
 export function deletePurchaseApply(id: number) {
   return http.request({
-    url: '/ppePurchaseApply/deletePpePurchaseApply',
+    url: `/ppePurchaseApply/deletePpePurchaseApply?id=${id}`,
     method: 'delete',
-    params: { id },
   });
 }
 
@@ -126,12 +148,14 @@ export interface SavePpePurchaseApplyReq extends PpePurchaseApply {
   approvalInfoList?: ApprovalInfoItem[];
   /** 劳防采购申请列表详情 */
   ppePurchaseApplyDetails?: PpePurchaseApplyDetail[];
+  /** 审核模板ID */
+  templateId?: number;
 }
 
 /** 编辑劳防用品采购申请(保存/提交,带审批信息) */
 export function updatePurchaseApply(data: SavePpePurchaseApplyReq) {
   return http.request({
-    url: '/ppePurchaseApply/submit',
+    url: '/ppePurchaseApply/updatePpePurchaseApplyDetail',
     method: 'put',
     data,
   });
@@ -140,7 +164,7 @@ export function updatePurchaseApply(data: SavePpePurchaseApplyReq) {
 /** 新增时也可走同一提交接口(无 id 或 saveOrSubmit=1) */
 export function savePurchaseApply(data: SavePpePurchaseApplyReq) {
   return http.request({
-    url: '/ppePurchaseApply/submit',
+    url: '/ppePurchaseApply/updatePpePurchaseApplyDetail',
     method: 'put',
     data,
   });
@@ -154,13 +178,14 @@ export interface SaveApprovalReq {
   approvalStatus: number;
   /** 退回原因 */
   returnReason?: string;
+  templateId?: string;
 }
 
 /** 提交审批(通过/退回) */
 export function saveApproval(data: SaveApprovalReq) {
   return http.request({
     url: '/ppePurchaseApply/saveApproval',
-    method: 'delete',
+    method: 'post',
     data,
   });
 }
@@ -171,13 +196,35 @@ export interface AuditPurchaseApplyParam {
   /** 1-审核通过,-1-审核不通过 → 映射 approvalStatus 2/3 */
   status: number;
   rejectReason?: string; // 同 returnReason
+  approvalOrder?: number;
+  templateId?: string;
 }
 
 /** 审核劳防用品采购申请(通过/退回)- 兼容旧接口,内部调 saveApproval */
 export function auditPurchaseApply(data: AuditPurchaseApplyParam) {
   return saveApproval({
     id: data.id,
-    approvalStatus: data.status === 1 ? 2 : 3, // 1 通过→2 已审批,-1 不通过→3 退回
+    approvalStatus: data.status === 2 ? 2 : 3, // 1 通过→2 已审批,-1 不通过→3 退回
     returnReason: data.rejectReason,
+    approvalOrder: data.approvalOrder,
+    templateId: data.templateId,
   });
 }
+
+/** 提交审批流程 */
+export const submitApprovalProcess = (data: ApprovalProcessParam) => {
+  return http.request({
+    url: '/ppePurchaseApply/submit',
+    method: 'post',
+    data,
+  });
+};
+
+/** 新增劳防用品采购申请(保存/提交,带审批信息) */
+export function saveThePurchaseRequest(data: SavePpePurchaseApplyReq) {
+  return http.request({
+    url: '/ppePurchaseApply/savePpePurchaseApplyList',
+    method: 'post',
+    data,
+  });
+}

+ 230 - 17
src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/components/detail.vue

@@ -1,5 +1,43 @@
 <template>
   <main class="safety-platform-container__main">
+
+    <el-alert
+      v-if="rejectReason && approvalStatus === '3'"
+      type="error"
+      :title="'不通过原因:' + rejectReason"
+      show-icon
+      class="detail-reject-alert"
+    />
+
+    <div class="select-box-ao">
+      <div class="select-box--item_new">
+        <span>
+          <span style="color: red;">*</span>
+          审批流程
+          :
+        </span>
+        <el-select
+          class="select-box--item__select"
+          v-model="form.approvalTemplateId"
+          placeholder="审批流程"
+          filterable
+          :disabled="isAuditMode"
+          popper-class="el-scrollbar--custom"
+        >
+          <el-option v-for="item in approvalList" :key="item.id" :label="item.templateName" :value="item.id" />
+        </el-select>
+      </div>
+
+      <div class="select-box--item_new" v-if="!isCreateMode">
+        <span>
+          <span style="color: red;">*</span>
+          申请单号
+          :
+        </span>
+        {{ form.applyNo }}
+      </div>
+    </div>
+    
     <el-form
       ref="formRef"
       :model="form"
@@ -8,7 +46,7 @@
       class="purchase-apply-form"
     >
       <!-- 申请单号:仅非新增时显示;查看/审核为纯文本 -->
-      <el-form-item v-if="!isCreateMode" label="申请单号" prop="applyNo" required>
+      <!-- <el-form-item v-if="!isCreateMode" label="申请单号" prop="applyNo" required>
         <span v-if="isViewMode || isAuditMode" class="apply-no-text">{{ form.applyNo || '-' }}</span>
         <el-input
           v-else
@@ -17,7 +55,7 @@
           readonly
           class="apply-no-input"
         />
-      </el-form-item>
+      </el-form-item> -->
 
       <!-- 劳防用品明细表 -->
       <div class="table-section">
@@ -28,7 +66,7 @@
               <span v-if="isViewMode || isAuditMode">{{ row.ppeName || row.equipmentName || '-' }}</span>
               <el-select
                 v-else
-                v-model="row.ppeName"
+                v-model="row.equipmentName"
                 placeholder="请选择"
                 filterable
                 clearable
@@ -61,7 +99,7 @@
               <span v-if="isViewMode || isAuditMode">{{ row.sizeOrShoeSize || row.specSize || '-' }}</span>
               <el-input
                 v-else
-                v-model="row.sizeOrShoeSize"
+                v-model="row.specSize"
                 placeholder="请输入..."
                 clearable
               />
@@ -72,7 +110,7 @@
               <span v-if="isViewMode || isAuditMode">{{ row.requiredQty ?? row.applyNum ?? '-' }}</span>
               <el-input-number
                 v-else
-                v-model="row.requiredQty"
+                v-model="row.applyNum"
                 :min="1"
                 :precision="0"
                 placeholder="请输入..."
@@ -86,7 +124,7 @@
               <span v-if="isViewMode || isAuditMode">{{ row.specModelType || row.model || '-' }}</span>
               <el-input
                 v-else
-                v-model="row.specModelType"
+                v-model="row.model"
                 placeholder="请输入..."
                 clearable
               />
@@ -97,6 +135,7 @@
               <span v-if="isViewMode || isAuditMode">{{ row.unitPrice ?? '-' }}</span>
               <el-input
                 v-else
+                type="number"
                 v-model="row.unitPrice"
                 placeholder="请输入..."
               />
@@ -104,13 +143,14 @@
           </el-table-column>
           <el-table-column label="商品编号" min-width="150">
             <template #default="{ row }">
-              <span v-if="isViewMode || isAuditMode">{{ row.productNo || '-' }}</span>
-              <el-input
+              <!-- v-if="isViewMode || isAuditMode || isCreateMode" -->
+              <span>{{ row.productNo || row.equipmentId || '-' }}</span>
+              <!-- <el-input
                 v-else
                 v-model="row.productNo"
                 placeholder="请输入..."
                 clearable
-              />
+              /> -->
             </template>
           </el-table-column>
           <el-table-column label="备注" min-width="150">
@@ -215,6 +255,61 @@
         <el-button type="primary" :loading="submitting" @click="handleApproverSave">保存</el-button>
       </template>
     </el-dialog>
+
+    <BasicDialog ref="basicDialogRef" title="提交审批" @refresh="closeDialog">
+      <template #form>
+        <div class="form">
+          <el-form ref="approvalFormRef" :model="approvalForm">
+            <el-form-item label="审批描述:" label-position="top">
+              <el-input v-model="approvalForm.description" placeholder="请输入审批描述" type="textarea" />
+            </el-form-item>
+            <div class="form-item">
+              <span>审批流程:</span>
+              <template v-for="item in approvalNodeList" :key="item.id">
+                <el-form-item
+                  :label="`第${item.approvalOrder}步:${item.nodeDescription}(${APPROVAL_TYPE_MAP[item.approvalType]})`"
+                  label-position="top"
+                  :prop="item.approverType !== APPROVER_TYPE.FIX ? `approvers.${item.id}` : ''"
+                  :rules="{ required: true, message: '请选择审批人员', trigger: 'change' }"
+                >
+                  <el-input
+                    v-if="item.approverType === APPROVER_TYPE.FIX"
+                    :model-value="item.approverInfoList.map((info) => info.approverName).join(',')"
+                    disabled
+                  ></el-input>
+                  <el-select
+                    v-else
+                    v-model="approvalForm.approvers[item.id]"
+                    placeholder="请选择审批人员"
+                    value-key="id"
+                    filterable
+                    remote
+                    collapse-tags
+                    collapse-tags-tooltip
+                    :max-collapse-tags="2"
+                    :remote-method="remoteMethod"
+                    :loading="loading"
+                    multiple
+                  >
+                    <el-option
+                      v-for="option in userOptions"
+                      :key="option.id"
+                      :label="`${option.realname}(${option.username})${option.deptName}`"
+                      :value="option.id"
+                    />
+                  </el-select>
+                </el-form-item>
+              </template>
+            </div>
+          </el-form>
+        </div>
+      </template>
+      <template #footer>
+        <el-button type="primary" @click="handleSubmitApproval">提交</el-button>
+        <el-button @click="basicDialogRef.closeDialog">取消</el-button>
+      </template>
+    </BasicDialog>
+
   </main>
   <footer class="safety-platform-container__footer">
     <el-button @click="router.back()">返回</el-button>
@@ -244,11 +339,14 @@
   import type { FileItem } from '@/components/UploadFiles/types';
   import { queryPersonalProtectiveEquipmentList } from '@/api/production-safety/personal-protective-equipment';
   import type { PersonalProtectiveEquipment } from '@/api/production-safety/personal-protective-equipment';
+  import { useEmergencyHook } from '@/views/emergency/src/hoos';
   import {
     queryPurchaseApplyDetail,
     savePurchaseApply,
     updatePurchaseApply,
     auditPurchaseApply,
+    submitApprovalProcess,
+    saveThePurchaseRequest,
     type PersonalProtectiveEquipmentPurchaseApply,
     type PurchaseApplyItem,
     type PpePurchaseApplyDetail,
@@ -260,6 +358,25 @@
   import type { DeptTree } from '@/types/dept/type';
   import { getUserList } from '@/api/system/user-operate';
   import type { UserLisItem } from '@/api/system/user-operate';
+  import BasicDialog from '@/components/BasicDialog.vue';
+  import type { ApprovalNodeInstanceType } from '@/views/system/approval/types';
+  import { APPROVAL_TYPE_MAP, APPROVER_TYPE } from '../configs/constant';
+  import { useEmergencySuppliesHook } from '@/views/emergency/emergency-supplies/src/hook';
+  import { getApprovalNodeInstanceList } from '@/api/approval/approval';
+  import { i } from 'vite/dist/node/types.d-jgA8ss1A';
+  import { te } from 'element-plus/es/locale';
+
+  const { approvalList, getApprovalList } =
+  useEmergencyHook()
+  const basicDialogRef = ref();
+  const approvalFormRef = ref();
+  const approvalForm = reactive({
+    description: '',
+    approvers: {} as Record<number, any[]>,
+  });
+
+  const approvalNodeList = ref<ApprovalNodeInstanceType[]>([]);
+  const { userOptions, loading, remoteMethod } = useEmergencySuppliesHook();
 
   const router = useRouter();
   const route = useRoute();
@@ -267,7 +384,8 @@
   const submitting = ref(false);
   const auditSubmitting = ref(false);
   const showRejectDialog = ref(false);
-  const rejectReason = ref('');
+
+  const planId = ref<string>(route.query.id as string || '');
 
   const showApproverDialog = ref(false);
   const approverFormRef = ref<FormInstance>();
@@ -284,6 +402,10 @@
 
   const operate = computed(() => (route.query.operate as string) || 'inventory-create');
   const currentId = computed(() => Number(route.query.id));
+  const approvalOrder = ref<string>(route.query.approvalOrder as string || '');
+  const rejectReason = ref<string>(route.query.rejectReason as string || '');
+  const approvalStatus = ref<string>(route.query.approvalStatus as string || '');
+  
   /** 从列表带入的申请单号/申请人/部门(无主表查询接口时用于展示) */
   const mainFromQuery = computed(() => ({
     applyCode: route.query.applyCode as string | undefined,
@@ -309,9 +431,10 @@
     productNo: '',
     remark: '',
   });
-
+  
   const form = reactive<PersonalProtectiveEquipmentPurchaseApply & { itemList: PurchaseApplyItem[] }>({
     applyNo: '',
+    approvalTemplateId: route.query.templateId,
     itemList: [defaultItem()],
   });
 
@@ -373,6 +496,7 @@
       pictureUrl: d.pictureUrl,
       stylePhoto: d.pictureUrl ?? '',
       remark: d.remark,
+      productNo: d.productNo,
     };
   }
 
@@ -389,6 +513,7 @@
       unitPrice: it.unitPrice,
       pictureUrl: it.pictureUrl ?? (typeof it.stylePhoto === 'string' ? it.stylePhoto : undefined),
       remark: it.remark,
+      productNo: it.productNo,
     };
   }
 
@@ -397,6 +522,7 @@
     if (found?.id) {
       row.ppeId = found.id;
       row.equipmentId = found.id;
+      row.productNo = String(found.id);
     }
   }
 
@@ -417,7 +543,9 @@
         pageSize: 500,
         queryParam: {},
       } as QueryPageRequest<any>);
-      const list = res?.records ?? res?.list ?? [];
+      // const list = res?.records ?? res?.list ?? [];
+      const resAny = res as any;
+      const list = resAny?.records ?? resAny?.list ?? [];
       ppeOptions.value = (list as PersonalProtectiveEquipment[]).map((item) => ({
         ...item,
         ppeName: item.ppeName ?? (item as any).name ?? '',
@@ -443,7 +571,7 @@
     }
   }
 
-  function buildSubmitPayload(extra?: { approvalInfoList?: ApprovalInfoItem[]; approvalDescription?: string }): SavePpePurchaseApplyReq {
+  function buildSubmitPayload(extra?: { approvalInfoList?: ApprovalInfoItem[]; approvalDescription?: string; templateId?: number | undefined;}): SavePpePurchaseApplyReq {
     const ppePurchaseApplyDetails = form.itemList
       .filter((it) => it.ppeName || it.equipmentName)
       .map((it) => itemToDetail(it));
@@ -454,6 +582,7 @@
       deptName: mainFromQuery.value.deptName,
       saveOrSubmit: isCreateMode.value ? 1 : 0,
       ppePurchaseApplyDetails,
+      templateId: form.approvalTemplateId,
       ...extra,
     };
   }
@@ -471,7 +600,9 @@
   async function loadDeptOptions() {
     try {
       const res = await getAllDepartments();
-      const tree = (Array.isArray(res) ? res : res?.children) ?? [];
+      // const tree = (Array.isArray(res) ? res : res?.children) ?? [];
+      const resWithChildren = res as (DeptTree[] | { children?: DeptTree[] });
+      const tree = (Array.isArray(resWithChildren) ? resWithChildren : resWithChildren?.children) ?? [];
       const list = tree.length && tree[0]?.children ? tree[0].children : tree;
       deptOptions.value = flattenDeptTree(list as DeptTree[]);
     } catch (e) {
@@ -503,13 +634,29 @@
 
   /** 点击底部「提交」:先校验表单,再弹出选择审核人 */
   async function openApproverDialog() {
+    if (!form.approvalTemplateId) {
+      ElMessage.warning('请选择审批模板');
+      return;
+    }
     const valid = await formRef.value?.validate().catch(() => false);
     if (!valid) return;
     if (!form.itemList.length || form.itemList.every((it) => !it.ppeName && !it.equipmentName)) {
       ElMessage.warning('请至少填写一条劳防用品明细');
       return;
     }
-    showApproverDialog.value = true;
+    // showApproverDialog.value = true;
+    const payload = buildSubmitPayload({
+      templateId: form.approvalTemplateId,
+    });
+    await saveThePurchaseRequest(payload).then((res) => {
+      ElMessage.success('提交成功');
+      if(isCreateMode.value){
+        planId.value = res || undefined;
+      }
+    });
+
+    await getApprovalNode(form.approvalTemplateId);
+    basicDialogRef.value.openDialog();
     if (!deptOptions.value.length) await loadDeptOptions();
   }
 
@@ -523,6 +670,7 @@
     const payload = buildSubmitPayload({
       approvalInfoList,
       approvalDescription: '',
+      templateId: form.approvalTemplateId,
     });
     await doSubmit(payload);
     showApproverDialog.value = false;
@@ -539,6 +687,9 @@
       } else {
         await updatePurchaseApply(payload);
         ElMessage.success('保存成功');
+        await getApprovalNode(form.approvalTemplateId);
+        basicDialogRef.value.openDialog();
+        return;
       }
       router.back();
     } catch (e) {
@@ -568,7 +719,7 @@
     if (!currentId.value) return;
     auditSubmitting.value = true;
     try {
-      await auditPurchaseApply({ id: currentId.value, status: 1 });
+      await auditPurchaseApply({ id: currentId.value, status: 2, approvalOrder: Number(approvalOrder.value),templateId: form.approvalTemplateId });
       ElMessage.success('审核通过');
       router.back();
     } catch (e) {
@@ -587,7 +738,7 @@
     if (!currentId.value) return;
     auditSubmitting.value = true;
     try {
-      await auditPurchaseApply({ id: currentId.value, status: -1, rejectReason: rejectReason.value.trim() });
+      await auditPurchaseApply({ id: currentId.value, status: 3, rejectReason: rejectReason.value.trim(), approvalOrder: Number(approvalOrder.value),templateId: form.approvalTemplateId });
       ElMessage.success('已提交审核不通过');
       showRejectDialog.value = false;
       router.back();
@@ -599,8 +750,56 @@
     }
   }
 
+  const refreshForm = () => {
+    approvalFormRef.value?.resetFields();
+    approvalForm.description = '';
+  };
+
+  const closeDialog = () => {
+    refreshForm();
+    basicDialogRef.value.closeDialog();
+  };
+
+  const getApprovalNode = async (id: number) => {
+    const res = await getApprovalNodeInstanceList(id);
+    approvalNodeList.value = res.approvalNodeInfoList || [];
+  };
+
+  const handleSubmitApproval = () => {
+    approvalFormRef.value.validate(async (valid: boolean) => {
+      if (valid) {
+        // 构造后端需要的数据格式
+        const approvalData = {
+          id: planId.value!,
+          templateId: form.approvalTemplateId,
+          approvalDescription: approvalForm.description,
+          approvalInfoList: approvalNodeList.value.map((node) => {
+            let approverIdList: number[] = [];
+            if (node.approverType === APPROVER_TYPE.FIX) {
+              approverIdList = node.approverInfoList.map((info) => info.approverId);
+            } else if (approvalForm.approvers[node.id]) {
+              approverIdList = approvalForm.approvers[node.id];
+            }
+            return {
+              approvalOrder: node.approvalOrder,
+              approverIdList,
+            };
+          }),
+        };
+        await submitApprovalProcess(approvalData);
+        ElMessage.success('提交成功');
+        basicDialogRef.value.closeDialog();
+        router.back();
+      }
+    });
+  };
+
   onMounted(() => {
     loadPpeOptions();
+    getApprovalList();
+    if (form.approvalTemplateId) {
+      form.approvalTemplateId = Number(form.approvalTemplateId);
+    }
     if (isEditMode.value || isViewMode.value || isAuditMode.value) {
       getDetail();
     } else {
@@ -633,4 +832,18 @@
   .items-table {
     width: 100%;
   }
+  .select-box-ao{
+    display: flex;
+    gap: 20px;
+  }
+  .select-box--item_new{
+    display: flex;
+    align-items: center;
+  }
+  .select-box--item__select{
+    width: 320px;
+  }
+  .detail-reject-alert{
+    margin-bottom: 20px;
+  }
 </style>

+ 85 - 0
src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/configs/constant.ts

@@ -0,0 +1,85 @@
+export enum EMERGENCY_PLAN_STATUS {
+  UNAPPROVED = 0,
+  APPROVAL_IN_PROGRESS,
+  RETURNED,
+  PUBLISHED,
+}
+
+export const EMERGENCY_PLAN_STATUS_MAP = {
+  [EMERGENCY_PLAN_STATUS.UNAPPROVED]: '未审批',
+  [EMERGENCY_PLAN_STATUS.APPROVAL_IN_PROGRESS]: '预案审批中',
+  [EMERGENCY_PLAN_STATUS.RETURNED]: '预案已退回',
+  [EMERGENCY_PLAN_STATUS.PUBLISHED]: '已公示',
+};
+
+export const EMERGENCY_PLAN_STATUS_OPTIONS = [
+  {
+    label: EMERGENCY_PLAN_STATUS_MAP[EMERGENCY_PLAN_STATUS.UNAPPROVED],
+    value: EMERGENCY_PLAN_STATUS.UNAPPROVED,
+  },
+  {
+    label: EMERGENCY_PLAN_STATUS_MAP[EMERGENCY_PLAN_STATUS.APPROVAL_IN_PROGRESS],
+    value: EMERGENCY_PLAN_STATUS.APPROVAL_IN_PROGRESS,
+  },
+  {
+    label: EMERGENCY_PLAN_STATUS_MAP[EMERGENCY_PLAN_STATUS.RETURNED],
+    value: EMERGENCY_PLAN_STATUS.RETURNED,
+  },
+  {
+    label: EMERGENCY_PLAN_STATUS_MAP[EMERGENCY_PLAN_STATUS.PUBLISHED],
+    value: EMERGENCY_PLAN_STATUS.PUBLISHED,
+  },
+];
+
+export enum APPROVAL_TYPE {
+  COUNTER_SIGN = 0,
+  ORDINARY_SIGN,
+}
+
+export const APPROVAL_TYPE_MAP = {
+  [APPROVAL_TYPE.COUNTER_SIGN]: '会签',
+  [APPROVAL_TYPE.ORDINARY_SIGN]: '或签',
+};
+
+export enum APPROVER_TYPE{
+  FIX = 0,
+  CUSTOM,
+}
+
+export const APPROVER_TYPE_MAP = {
+  [APPROVER_TYPE.FIX]: '固定',
+  [APPROVER_TYPE.CUSTOM]: '自选',
+};
+
+export enum APPROVAL_STATUS {
+  PENDING = 1,
+  APPROVED,
+  REJECTED,
+  OHTER,
+}
+
+export const APPROVAL_STATUS_MAP = {
+  [APPROVAL_STATUS.PENDING]: '待审批',
+  [APPROVAL_STATUS.APPROVED]: '已审批',
+  [APPROVAL_STATUS.REJECTED]: '退回',
+  [APPROVAL_STATUS.OHTER]: '他人已审批',
+};
+
+export const APPROVAL_STATUS_OPTIONS = [
+  {
+    label: APPROVAL_STATUS_MAP[APPROVAL_STATUS.PENDING],
+    value: APPROVAL_STATUS.PENDING,
+  },
+  {
+    label: APPROVAL_STATUS_MAP[APPROVAL_STATUS.APPROVED],
+    value: APPROVAL_STATUS.APPROVED,
+  },
+  {
+    label: APPROVAL_STATUS_MAP[APPROVAL_STATUS.REJECTED],
+    value: APPROVAL_STATUS.REJECTED,
+  },
+  {
+    label: APPROVAL_STATUS_MAP[APPROVAL_STATUS.OHTER],
+    value: APPROVAL_STATUS.OHTER,
+  },
+];

+ 28 - 12
src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/list.vue

@@ -31,9 +31,9 @@
                   clearable
                   class="act-search-input"
                 >
-                  <el-option label="待审核" :value="0" />
-                  <el-option label="审核通过" :value="1" />
-                  <el-option label="审核不通过" :value="-1" />
+                  <el-option label="待审核" :value="1" />
+                  <el-option label="审核通过" :value="2" />
+                  <el-option label="审核不通过" :value="3" />
                 </el-select>
               </div>
               <div class="select-box--item">
@@ -69,16 +69,16 @@
             <template #action="scope">
               <div class="action-container--div" style="justify-content: left">
                 <!-- 待审核:查看、审核 -->
-                <template v-if="scope.row.status === 0 || scope.row.status === '0'">
+                <template v-if="scope.row.status === 1 || scope.row.status === '1'">
                   <ActionButton text="查看" @click="handleView(scope.row.id!, scope.row)" />
                   <ActionButton text="审核" @click="handleAudit(scope.row.id!, scope.row)" />
                 </template>
                 <!-- 审核通过:查看 -->
-                <template v-else-if="scope.row.status === 1 || scope.row.status === '1'">
+                <template v-else-if="scope.row.status === 2 || scope.row.status === '2'">
                   <ActionButton text="查看" @click="handleView(scope.row.id!, scope.row)" />
                 </template>
                 <!-- 审核不通过:编辑、删除、查看 -->
-                <template v-else-if="scope.row.status === -1 || scope.row.status === '-1'">
+                <template v-else-if="scope.row.status === 3 || scope.row.status === '3'">
                   <ActionButton text="编辑" @click="handleEdit(scope.row.id!, scope.row)" />
                   <ActionButton
                     text="删除"
@@ -114,6 +114,7 @@
     type PpePurchaseApply,
     type QueryPurchaseApplyPageReq,
   } from '@/api/production-safety/personal-protective-equipment-purchase-apply';
+  import { template } from 'lodash-es';
 
   const router = useRouter();
   const basicTableRef = ref<InstanceType<typeof BasicTable>>();
@@ -137,9 +138,9 @@
   });
 
   function getStatusText(status: number | string | undefined) {
-    if (status === 0 || status === '0') return '待审核';
-    if (status === 1 || status === '1') return '审核通过';
-    if (status === -1 || status === '-1') return '审核不通过';
+    if (status === 1 || status === '1') return '待审核';
+    if (status === 2 || status === '2') return '审核通过';
+    if (status === 3 || status === '3') return '审核不通过';
     return '-';
   }
 
@@ -158,10 +159,19 @@
   async function getTableData() {
     tableConfig.loading = true;
     try {
+      // const res = await queryPurchaseApplyList(tableQuery);
+      // if (res) {
+      //   tableData.value = (res?.records ?? res?.list ?? []) as PpePurchaseApply[];
+      //   pagination.total = res?.totalRow ?? res?.total ?? 0;
+      // }
       const res = await queryPurchaseApplyList(tableQuery);
-      if (res) {
-        tableData.value = (res?.records ?? res?.list ?? []) as PpePurchaseApply[];
-        pagination.total = res?.totalRow ?? res?.total ?? 0;
+      const resWithExtra = res as (typeof res & {
+        list?: PpePurchaseApply[],
+        total?: number
+      });
+      if (resWithExtra) {
+        tableData.value = (resWithExtra?.records ?? resWithExtra?.list ?? []) as PpePurchaseApply[];
+        pagination.total = resWithExtra?.totalRow ?? resWithExtra?.total ?? 0;
       }
     } catch (e) {
       console.error('获取劳防用品采购申请列表失败:', e);
@@ -203,6 +213,7 @@
           applyCode: String(row.applyCode ?? ''),
           applicantName: row.applicantName ?? '',
           deptName: row.deptName ?? '',
+          templateId: row.templateId ?? '',
         }),
       },
     });
@@ -229,6 +240,7 @@
           applyCode: String(row.applyCode ?? ''),
           applicantName: row.applicantName ?? '',
           deptName: row.deptName ?? '',
+          templateId: row.templateId ?? '',
         }),
       },
     });
@@ -244,6 +256,10 @@
           applyCode: String(row.applyCode ?? ''),
           applicantName: row.applicantName ?? '',
           deptName: row.deptName ?? '',
+          approvalOrder: row.approvalOrder ?? '',
+          templateId: row.templateId ?? '',
+          rejectReason: row.rejectReason ?? '',
+          approvalStatus: row.status ?? '',
         }),
       },
     });

+ 278 - 0
src/views/production-safety/risk-identification-and-control/labor-products-purchase-apply-manage/listAdmin.vue

@@ -0,0 +1,278 @@
+<template>
+  <div class="safety-platform-container">
+    <header class="safety-platform-container__header">
+      <div class="breadcrumb-title">劳防用品采购申请</div>
+    </header>
+    <main class="safety-platform-container__main">
+      <div class="search-table-container">
+        <header>
+          <div style="position: relative">
+            <el-button type="primary" class="search-table-container--button" @click="handleCreate">
+              添加
+            </el-button>
+          </div>
+
+          <div class="act-search">
+            <section class="select-box">
+              <div class="select-box--item">
+                <span>用品名称/申请人:</span>
+                <el-input
+                  v-model="tableQuery.queryParam.name"
+                  placeholder="请输入用品名称或申请人"
+                  class="act-search-input"
+                  clearable
+                />
+              </div>
+              <div class="select-box--item">
+                <span>状态:</span>
+                <el-select
+                  v-model="tableQuery.queryParam.status"
+                  placeholder="请选择状态"
+                  clearable
+                  class="act-search-input"
+                >
+                  <el-option label="待审核" :value="1" />
+                  <el-option label="审核通过" :value="2" />
+                  <el-option label="审核不通过" :value="3" />
+                </el-select>
+              </div>
+              <div class="select-box--item">
+                <span>申请部门:</span>
+                <el-input
+                  v-model="tableQuery.queryParam.applyDeptName"
+                  placeholder="请输入申请部门名称"
+                  class="act-search-input"
+                  clearable
+                />
+              </div>
+            </section>
+            <section class="search-btn">
+              <el-button type="primary" @click="handleSearch">查询</el-button>
+              <el-button @click="handleReset">重置</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>
+                {{ scope.row.statusName || getStatusText(scope.row.status) }}
+              </span>
+            </template>
+            <template #action="scope">
+              <div class="action-container--div" style="justify-content: left">
+                <!-- 待审核:查看、审核 -->
+                <template v-if="scope.row.status === 1 || scope.row.status === '1'">
+                  <ActionButton text="查看" @click="handleView(scope.row.id!, scope.row)" />
+                  <ActionButton text="审核" @click="handleAudit(scope.row.id!, scope.row)" />
+                </template>
+                <!-- 审核通过:查看 -->
+                <template v-else-if="scope.row.status === 2 || scope.row.status === '2'">
+                  <ActionButton text="查看" @click="handleView(scope.row.id!, scope.row)" />
+                </template>
+                <!-- 审核不通过:编辑、删除、查看 -->
+                <template v-else-if="scope.row.status === 3 || scope.row.status === '3'">
+                  <ActionButton text="编辑" @click="handleEdit(scope.row.id!, scope.row)" />
+                  <ActionButton
+                    text="删除"
+                    :popconfirm="{ title: '确定要删除该申请吗?' }"
+                    @confirm="handleDelete(scope.row.id!)"
+                  />
+                  <ActionButton text="查看" @click="handleView(scope.row.id!, scope.row)" />
+                </template>
+                <template v-else>
+                  <ActionButton text="查看" @click="handleView(scope.row.id!, scope.row)" />
+                </template>
+              </div>
+            </template>
+          </BasicTable>
+        </div>
+      </div>
+    </main>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { onMounted, reactive, ref } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import BasicTable from '@/components/BasicTable.vue';
+  import useTableConfig from '@/hooks/useTableConfigHook';
+  import ActionButton from '@/components/ActionButton.vue';
+  import { TABLE_OPTIONS, PURCHASE_APPLY_TABLE_COLUMNS } from './configs/tables';
+  import { useRouter } from 'vue-router';
+  import type { QueryPageRequest } from '@/types/basic-query';
+  import {
+    queryPurchaseApplyListAdmin,
+    deletePurchaseApply,
+    type PpePurchaseApply,
+    type QueryPurchaseApplyPageReq,
+  } from '@/api/production-safety/personal-protective-equipment-purchase-apply';
+  import { template } from 'lodash-es';
+
+  const router = useRouter();
+  const basicTableRef = ref<InstanceType<typeof BasicTable>>();
+
+  const { tableConfig, pagination } = useTableConfig(
+    PURCHASE_APPLY_TABLE_COLUMNS,
+    TABLE_OPTIONS,
+  );
+
+  const tableData = ref<PpePurchaseApply[]>([]);
+
+  const tableQuery = reactive<QueryPageRequest<QueryPurchaseApplyPageReq>>({
+    pageNumber: pagination.pageNumber,
+    pageSize: pagination.pageSize,
+    queryParam: {
+      name: '',
+      status: undefined,
+      applyDeptCode: '',
+      applyDeptName: '',
+    },
+  });
+
+  function getStatusText(status: number | string | undefined) {
+    if (status === 1 || status === '1') return '待审核';
+    if (status === 2 || status === '2') return '审核通过';
+    if (status === 3 || status === '3') return '审核不通过';
+    return '-';
+  }
+
+  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 res = await queryPurchaseApplyListAdmin(tableQuery);
+      // if (res) {
+      //   tableData.value = (res?.records ?? res?.list ?? []) as PpePurchaseApply[];
+      //   pagination.total = res?.totalRow ?? res?.total ?? 0;
+      // }
+      const res = await queryPurchaseApplyListAdmin(tableQuery);
+      const resWithExtra = res as (typeof res & {
+        list?: PpePurchaseApply[],
+        total?: number
+      });
+      if (resWithExtra) {
+        tableData.value = (resWithExtra?.records ?? resWithExtra?.list ?? []) as PpePurchaseApply[];
+        pagination.total = resWithExtra?.totalRow ?? resWithExtra?.total ?? 0;
+      }
+    } catch (e) {
+      console.error('获取劳防用品采购申请列表失败:', e);
+      tableData.value = [];
+      pagination.total = 0;
+    } finally {
+      tableConfig.loading = false;
+    }
+  }
+
+  const handleSearch = () => {
+    pagination.pageNumber = 1;
+    tableQuery.pageNumber = 1;
+    getTableData();
+  };
+
+  const handleReset = () => {
+    tableQuery.queryParam.name = '';
+    tableQuery.queryParam.status = undefined;
+    tableQuery.queryParam.applyDeptCode = '';
+    tableQuery.queryParam.applyDeptName = '';
+    handleSearch();
+  };
+
+  const handleCreate = () => {
+    router.push({
+      name: 'laborProductsPurchaseApplyManageItem',
+      query: { operate: 'inventory-create' },
+    });
+  };
+
+  const handleEdit = (id: number, row?: PpePurchaseApply) => {
+    router.push({
+      name: 'laborProductsPurchaseApplyManageItem',
+      query: {
+        id: String(id),
+        operate: 'inventory-edit',
+        ...(row && {
+          applyCode: String(row.applyCode ?? ''),
+          applicantName: row.applicantName ?? '',
+          deptName: row.deptName ?? '',
+          templateId: row.templateId ?? '',
+        }),
+      },
+    });
+  };
+
+  const handleDelete = async (id: number) => {
+    try {
+      await deletePurchaseApply(id);
+      ElMessage.success('删除成功');
+      getTableData();
+    } catch (e) {
+      console.error('删除失败:', e);
+      ElMessage.error('删除失败,请重试');
+    }
+  };
+
+  const handleView = (id: number, row?: PpePurchaseApply) => {
+    router.push({
+      name: 'laborProductsPurchaseApplyManageItem',
+      query: {
+        id: String(id),
+        operate: 'inventory-view',
+        ...(row && {
+          applyCode: String(row.applyCode ?? ''),
+          applicantName: row.applicantName ?? '',
+          deptName: row.deptName ?? '',
+          templateId: row.templateId ?? '',
+        }),
+      },
+    });
+  };
+
+  const handleAudit = (id: number, row?: PpePurchaseApply) => {
+    router.push({
+      name: 'laborProductsPurchaseApplyManageItem',
+      query: {
+        id: String(id),
+        operate: 'audit',
+        ...(row && {
+          applyCode: String(row.applyCode ?? ''),
+          applicantName: row.applicantName ?? '',
+          deptName: row.deptName ?? '',
+          approvalOrder: row.approvalOrder ?? '',
+          templateId: row.templateId ?? '',
+          rejectReason: row.rejectReason ?? '',
+          approvalStatus: row.status ?? '',
+        }),
+      },
+    });
+  };
+
+  onMounted(() => {
+    getTableData();
+  });
+</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 '@/views/traffic/violation/style/act-search-table.scss' as *;
+</style>

+ 2 - 0
src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/Item.vue

@@ -25,6 +25,8 @@
         return '编辑工伤认定申请';
       case 'inventory-view':
         return '查看工伤认定申请';
+      case 'inventory-audit':
+        return '审核工伤认定申请';
       default:
         return '工伤认定申请详情';
     }

+ 555 - 17
src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/components/detail.vue

@@ -6,6 +6,111 @@
       :formRules="isViewMode ? undefined : formRules"
       :formConfig="computedFormConfig"
     >
+      <template #approvalTemplateId>
+        <el-select
+          class="select-box--item__select"
+          v-model="ruleFormData.approvalTemplateId"
+          placeholder="审批流程"
+          filterable
+          :disabled="!isCreateMode"
+          popper-class="el-scrollbar--custom"
+        >
+          <el-option v-for="item in approvalList" :key="item.id" :label="item.templateName" :value="item.id" />
+        </el-select>
+      </template>
+      <template #reviewDepartmentId>
+        <el-cascader
+          v-model="ruleFormData.departmentCode"
+          ref="cascaderRef"
+          :options="firstLevelDepts"
+          :props="cascaderProp"
+          :show-all-levels="false"
+          placeholder="部门名称"
+          filterable
+          :disabled="!isCreateMode"
+          @change="handleChangeDept"
+        />
+      </template>
+      <template #accidentReport>
+        <UploadFiles
+          label="上传事故报告"
+          :file-list="accidentCertUrl"
+          :readonly="!isCreateMode"
+          :disabled="!isCreateMode"
+          @uploadSuccess="handleAccidentReportUploadSuccess"
+          @preview="handlePreview"
+        />
+      </template>
+      <template #powerOfAttorney>
+        <UploadFiles
+          label="上传委托书"
+          :file-list="powerAttorneyUrl"
+          :readonly="!isCreateMode"
+          :disabled="!isCreateMode"
+          @uploadSuccess="handlePowerOfAttorneyUploadSuccess"
+          @preview="handlePreview"
+        />
+      </template>
+      <template #addressConfirmation>
+        <UploadFiles
+          label="上传地址确认书"
+          :file-list="addressConfirmUrl"
+          :readonly="!isCreateMode"
+          :disabled="!isCreateMode"
+          @uploadSuccess="handleAddressConfirmationUploadSuccess"
+          @preview="handlePreview"
+        />
+      </template>
+      <template #applicationForm>
+        <UploadFiles
+          label="上传申请表"
+          :file-list="applicationFormUrl"
+          :readonly="!isCreateMode"
+          :disabled="!isCreateMode"
+          @uploadSuccess="handleApplicationFormUploadSuccess"
+          @preview="handlePreview"
+        />
+      </template>
+      <template #idCard>
+        <UploadFiles
+          label="上传身份证正反面"
+          :file-list="idCardUrl"
+          :readonly="!isCreateMode"
+          :disabled="!isCreateMode"
+          @uploadSuccess="handleIdCardUploadSuccess"
+          @preview="handlePreview"
+        />
+      </template>
+      <template #laborContract>
+        <UploadFiles
+          label="上传劳动合同"
+          :file-list="laborContractUrl"
+          :readonly="!isCreateMode"
+          :disabled="!isCreateMode"
+          @uploadSuccess="handleLaborContractUploadSuccess"
+          @preview="handlePreview"
+        />
+      </template>
+      <template #initialMedicalCertificate>
+        <UploadFiles
+          label="上传初次医疗证明"
+          :file-list="initialMedicalCertUrl"
+          :readonly="!isCreateMode"
+          :disabled="!isCreateMode"
+          @uploadSuccess="handleInitialMedicalCertificateUploadSuccess"
+          @preview="handlePreview"
+        />
+      </template>
+      <template #agentIdCard>
+        <UploadFiles
+          label="上传被委托人员身份证正反面"
+          :file-list="trusteeIdCardUrl"
+          :readonly="!isCreateMode"
+          :disabled="!isCreateMode"
+          @uploadSuccess="handleAgentIdCardUploadSuccess"
+          @preview="handlePreview"
+        />
+      </template>
       <template #status>
         <el-radio-group v-model="ruleFormData.status" :disabled="isViewMode">
           <el-radio label="ENABLE">启用</el-radio>
@@ -13,33 +118,168 @@
         </el-radio-group>
       </template>
     </BasicForm>
+    <PreviewOnline ref="previewOnlineRef" />
+    <BasicDialog ref="basicDialogRef" title="提交审批" @refresh="closeDialog">
+      <template #form>
+        <div class="form">
+          <el-form ref="approvalFormRef" :model="approvalForm">
+            <el-form-item label="审批描述:" label-position="top">
+              <el-input v-model="approvalForm.description" placeholder="请输入审批描述" type="textarea" />
+            </el-form-item>
+            <div class="form-item">
+              <span>审批流程:</span>
+              <template v-for="item in approvalNodeList" :key="item.id">
+                <el-form-item
+                  :label="`第${item.approvalOrder}步:${item.nodeDescription}(${APPROVAL_TYPE_MAP[item.approvalType]})`"
+                  label-position="top"
+                  :prop="item.approverType !== APPROVER_TYPE.FIX ? `approvers.${item.id}` : ''"
+                  :rules="{ required: true, message: '请选择审批人员', trigger: 'change' }"
+                >
+                  <el-input
+                    v-if="item.approverType === APPROVER_TYPE.FIX"
+                    :model-value="item.approverInfoList.map((info) => info.approverName).join(',')"
+                    disabled
+                  ></el-input>
+                  <el-select
+                    v-else
+                    v-model="approvalForm.approvers[item.id]"
+                    placeholder="请选择审批人员"
+                    value-key="id"
+                    filterable
+                    remote
+                    collapse-tags
+                    collapse-tags-tooltip
+                    :max-collapse-tags="2"
+                    :remote-method="remoteMethod"
+                    :loading="loading"
+                    multiple
+                  >
+                    <el-option
+                      v-for="option in userOptions"
+                      :key="option.id"
+                      :label="`${option.realname}(${option.username})${option.deptName}`"
+                      :value="option.id"
+                    />
+                  </el-select>
+                </el-form-item>
+              </template>
+            </div>
+          </el-form>
+        </div>
+      </template>
+      <template #footer>
+        <el-button type="primary" @click="handleSubmitApproval">提交</el-button>
+        <el-button @click="basicDialogRef.closeDialog">取消</el-button>
+      </template>
+    </BasicDialog>
+      <!-- 审核不通过原因 -->
+    <el-dialog
+      v-model="showRejectDialog"
+      title="审核不通过"
+      width="400px"
+      destroy-on-close
+      @close="rejectReason = ''"
+    >
+      <el-radio-group v-model="rejectReason" v-if="auditType">
+        <el-radio value="1">责任事故</el-radio>
+        <el-radio value="2">非责任事故</el-radio>
+        <el-radio value="3">交通事故</el-radio>
+      </el-radio-group>
+      <el-input
+      v-else
+        v-model="rejectReason"
+        type="textarea"
+        :rows="3"
+        placeholder="请输入审核不通过原因"
+      />
+      <template #footer>
+        <el-button @click="showRejectDialog = false">取消</el-button>
+        <el-button type="danger" :loading="auditSubmitting" @click="handleAuditReject">
+          确定
+        </el-button>
+      </template>
+    </el-dialog>
   </main>
   <footer class="safety-platform-container__footer">
     <el-button @click="router.back()">返回</el-button>
-    <el-button v-if="!isViewMode" type="primary" @click="handleSubmit">
-      {{ isCreateMode ? '提交' : '保存' }}
-    </el-button>
+    <template v-if="isCreateMode || isEditMode">
+      <el-button type="primary" :loading="submitting" @click="handleSubmit">
+        {{ isCreateMode ? '提交' : '保存' }}
+      </el-button>
+    </template>
+    <template v-if="isAuditMode">
+      <el-button type="success" :loading="auditSubmitting" @click="showRejectDialog = true;auditType = true">
+        审核通过
+      </el-button>
+      <el-button type="danger" :loading="auditSubmitting" @click="showRejectDialog = true;auditType = false">
+        审核不通过
+      </el-button>
+    </template>
   </footer>
 </template>
 
 <script setup lang="ts">
-  import { computed, onMounted, ref } from 'vue';
+  import { computed, onMounted, ref , reactive } from 'vue';
   import { useRoute, useRouter } from 'vue-router';
   import { ElMessage } from 'element-plus';
   import BasicForm from '@/components/BasicForm.vue';
   import { useFormConfigHook } from '@/hooks/useFormConfigHook';
   import { INVENTORY_FORM_CONFIG, INVENTORY_FORM_DATA, INVENTORY_FORM_RULES } from '../configs/form';
-  import { queryInventoryDetail, saveInventory, updateInventory } from '@/api/inventory';
+  import { queryInventoryDetail, saveBusinessInformation, updateInventory , submitApprovalProcess, auditPurchaseApply } from '@/api/inventory';
+  import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
+  import type { FileItem } from '@/components/UploadFiles/types';
+  import { formatAttachmentList } from '@/components/UploadFiles/utils';
+  import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import BasicDialog from '@/components/BasicDialog.vue';
+  import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
+  import type { ApprovalNodeInstanceType } from '@/views/system/approval/types';
+  import { APPROVAL_TYPE_MAP, APPROVER_TYPE } from '../configs/constant';
+  import { useEmergencySuppliesHook } from '@/views/emergency/emergency-supplies/src/hook';
+  import { useEmergencyHook } from '@/views/emergency/src/hoos';
+  import { getApprovalNodeInstanceList } from '@/api/approval/approval';
+import { template } from 'lodash-es';
 
   const router = useRouter();
   const route = useRoute();
 
+  const cascaderProp = {
+    expandTrigger: 'click',
+    checkStrictly: true,
+    // emitPath: false,
+    value: 'id',
+    label: 'deptName',
+  };
+  const { approvalList, getApprovalList } = useEmergencyHook()
+
   const operate = computed(() => (route.query.operate as string) || 'inventory-create');
   const currentId = computed(() => Number(route.query.id));
+  const newFormId = ref('');
+
+  const firstLevelDepts = ref<any[]>([]);
 
   const isCreateMode = computed(() => operate.value === 'inventory-create');
   const isEditMode = computed(() => operate.value === 'inventory-edit');
   const isViewMode = computed(() => operate.value === 'inventory-view');
+  const isAuditMode = computed(() => operate.value === 'inventory-audit');
+  const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
+  const cascaderRef = ref();
+  const rejectReason = ref('');
+  const approvalOrder = ref<string>(route.query.approvalOrder as string || '');
+  const approvalTemplateId = ref<string>(route.query.approvalTemplateId as string || '');
+  const auditType = ref(true);
+
+  const basicDialogRef = ref();
+  const approvalFormRef = ref();
+  const approvalForm = reactive({
+    description: '',
+    approvers: {} as Record<number, any[]>,
+  });
+  const approvalNodeList = ref<ApprovalNodeInstanceType[]>([]);
+  const { userOptions, loading, remoteMethod } = useEmergencySuppliesHook();
+  const submitting = ref(false);
+  const auditSubmitting = ref(false);
+  const showRejectDialog = ref(false);
 
   const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
     useFormConfigHook(INVENTORY_FORM_CONFIG, INVENTORY_FORM_DATA, INVENTORY_FORM_RULES);
@@ -59,6 +299,9 @@
     if (isViewMode.value) {
       return viewFormConfig.value;
     }
+    if (isAuditMode.value) {
+      return viewFormConfig.value;
+    }
     return ruleFormConfig.value;
   });
 
@@ -76,11 +319,21 @@
       const res = await queryInventoryDetail(currentId.value);
       if (res) {
         // 映射接口字段到表单字段
-        ruleFormData.itemName = res.stuffName; // 物品名称
-        ruleFormData.warehouseDate = res.inStoreTime ? res.inStoreTime.split('T')[0] : ''; // 入库日期(YYYY-MM-DD)
-        ruleFormData.itemQuantity = res.stuffQty; // 物品数量
+        ruleFormData.itemName = res.applicantName || ''; // 申请人姓名
+        ruleFormData.applicantCode = res.applicantCode || ''; // 工号
+        ruleFormData.warehouseDate = res.injuryTime ? res.injuryTime.split('T')[0] : ''; // 受伤时间
         ruleFormData.status = res.status ? 'ENABLE' : 'DISABLE'; // 状态
-        ruleFormData.remarks = res.remark || ''; // 备注
+        ruleFormData.remarks = res.injuryReason || ''; // 受伤原因
+        ruleFormData.accidentCertUrl = res.accidentCertUrl || ''; // 事故报告
+        ruleFormData.powerAttorneyUrl = res.powerAttorneyUrl || ''; // 委托书
+        ruleFormData.addressConfirmUrl = res.addressConfirmUrl || ''; // 地址确认书
+        ruleFormData.applicationFormUrl = res.applicationFormUrl || ''; // 申请表
+        ruleFormData.idCardUrl = res.idCardUrl || ''; // 身份证正反面
+        ruleFormData.laborContractUrl = res.laborContractUrl || ''; // 劳动合同
+        ruleFormData.initialMedicalCertUrl = res.initialMedicalCertUrl || ''; // 初次医疗证明
+        ruleFormData.trusteeIdCardUrl = res.trusteeIdCardUrl || ''; // 被委托人员身份证正反面
+        ruleFormData.departmentCode = JSON.parse(res.departmentCode || '[]') || ''; // 部门编码
+        ruleFormData.approvalTemplateId = res.templateId || ''; // 审批模板ID
       }
       cloneRuleFormData();
     } catch (e) {
@@ -94,18 +347,34 @@
     if (!res) return;
     try {
       const basePayload = {
-        stuffName: ruleFormData.itemName,
-        inStoreTime: ruleFormData.warehouseDate
+        applicantName: ruleFormData.itemName,
+        applicantCode: ruleFormData.applicantCode,
+        injuryTime: ruleFormData.warehouseDate
           ? new Date(ruleFormData.warehouseDate).toISOString()
           : '',
-        stuffQty: ruleFormData.itemQuantity,
         status: ruleFormData.status === 'ENABLE',
-        remark: ruleFormData.remarks || '',
+        injuryReason: ruleFormData.remarks || '',
+        accidentCertUrl: ruleFormData.accidentCertUrl,
+        powerAttorneyUrl: ruleFormData.powerAttorneyUrl,
+        addressConfirmUrl: ruleFormData.addressConfirmUrl,
+        applicationFormUrl: ruleFormData.applicationFormUrl,
+        idCardUrl: ruleFormData.idCardUrl,
+        laborContractUrl: ruleFormData.laborContractUrl,
+        initialMedicalCertUrl: ruleFormData.initialMedicalCertUrl,
+        trusteeIdCardUrl: ruleFormData.trusteeIdCardUrl,
+        departmentName: ruleFormData.departmentName || '',
+        departmentCode: ruleFormData.departmentCode || '',
+        templateId: Number(ruleFormData.approvalTemplateId),
       };
 
       if (isCreateMode.value) {
-        await saveInventory(basePayload);
+        await saveBusinessInformation(basePayload).then((res)=>{
+          newFormId.value = res || '';
+        });
         ElMessage.success('创建成功');
+        await getApprovalNode(Number(ruleFormData.approvalTemplateId));
+        basicDialogRef.value.openDialog();
+        return;
       } else if (isEditMode.value && currentId.value) {
         await updateInventory({
           id: currentId.value,
@@ -121,10 +390,280 @@
     }
   };
 
+  const handlePreview = (url: string) => {
+    if (url) {
+      // 根据文件扩展名判断文件类型
+      const extension = url.split('.').pop()?.toLowerCase() || '';
+      let fileType: 'pdf' | 'word' | 'excel' | 'ppt' = 'pdf';
+      if (extension === 'doc' || extension === 'docx') {
+        fileType = 'word';
+      } else if (extension === 'xls' || extension === 'xlsx') {
+        fileType = 'excel';
+      } else if (extension === 'ppt' || extension === 'pptx') {
+        fileType = 'ppt';
+      }
+      previewOnlineRef.value?.open(url, fileType);
+    }
+  };
+
+   /** 附件 JSON [{file_name, url}] 转 FileItem 列表,供 UploadFiles 使用(新增/编辑/查看/审核统一展示) */
+  function convertAttachmentJsonToFileItems(attachmentStr: string): FileItem[] {
+    if (!attachmentStr || !String(attachmentStr).trim()) return [];
+    try {
+      const arr = JSON.parse(attachmentStr);
+      if (!Array.isArray(arr)) return [];
+      return arr.map((item: any, index: number) => {
+        const fileName = item.file_name || item.fileName || `附件${index + 1}`;
+        const url = item.url || item.fileUrl || '';
+        const ext = (fileName || '').split('.').pop()?.toLowerCase() || '';
+        let fileType = 'pdf';
+        if (['doc', 'docx'].includes(ext)) fileType = 'word';
+        else if (['xls', 'xlsx'].includes(ext)) fileType = 'excel';
+        else if (['ppt', 'pptx'].includes(ext)) fileType = 'ppt';
+        return { fileId: index + 1, fileName, fileType, fileSize: '0', fileUrl: url };
+      });
+    } catch {
+      return [];
+    }
+  }
+
+  const accidentCertUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.accidentCertUrl || ''));
+  const powerAttorneyUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.powerAttorneyUrl || ''));
+  const addressConfirmUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.addressConfirmUrl || ''));
+  const applicationFormUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.applicationFormUrl || ''));
+  const idCardUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.idCardUrl || ''));
+  const laborContractUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.laborContractUrl || ''));
+  const initialMedicalCertUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.initialMedicalCertUrl || ''));
+  const trusteeIdCardUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.trusteeIdCardUrl || ''));
+
+  async function handleAccidentReportUploadSuccess(files: FileItem[]) {
+    if (!files?.length) {
+      ruleFormData.accidentCertUrl = '';
+      return;
+    }
+    try {
+      const list = await formatAttachmentList(files);
+      const jsonArr = (list || []).map((r) => ({
+        file_name: r.fileName,
+        url: r.fileUrl || '',
+      })).filter((x) => x.url);
+      ruleFormData.accidentCertUrl = JSON.stringify(jsonArr);
+    } catch (e) {
+      console.error('事故报告上传失败:', e);
+      ElMessage.error(e?.message || e?.data || '事故报告上传失败,请重试');
+    }
+  }
+
+  async function handlePowerOfAttorneyUploadSuccess(files: FileItem[]) {
+    if (!files?.length) {
+      ruleFormData.powerAttorneyUrl = '';
+      return;
+    }
+    try {
+      const list = await formatAttachmentList(files);
+      const jsonArr = (list || []).map((r) => ({
+        file_name: r.fileName,
+        url: r.fileUrl || '',
+      })).filter((x) => x.url);
+      ruleFormData.powerAttorneyUrl = JSON.stringify(jsonArr);
+    } catch (e) {
+      console.error('委托书上传失败:', e);
+      ElMessage.error(e?.message || e?.data || '委托书上传失败,请重试');
+    }
+  }
+
+  async function handleAddressConfirmationUploadSuccess(files: FileItem[]) {
+    if (!files?.length) {
+      ruleFormData.addressConfirmUrl = '';
+      return;
+    }
+    try {
+      const list = await formatAttachmentList(files);
+      const jsonArr = (list || []).map((r) => ({
+        file_name: r.fileName,
+        url: r.fileUrl || '',
+      })).filter((x) => x.url);
+      ruleFormData.addressConfirmUrl = JSON.stringify(jsonArr);
+    } catch (e) {
+      console.error('地址确认书上传失败:', e);
+      ElMessage.error(e?.message || e?.data || '地址确认书上传失败,请重试');
+    }
+  }
+
+  async function handleApplicationFormUploadSuccess(files: FileItem[]) {
+    if (!files?.length) {
+      ruleFormData.applicationFormUrl = '';
+      return;
+    }
+    try {
+      const list = await formatAttachmentList(files);
+      const jsonArr = (list || []).map((r) => ({
+        file_name: r.fileName,
+        url: r.fileUrl || '',
+      })).filter((x) => x.url);
+      ruleFormData.applicationFormUrl = JSON.stringify(jsonArr);
+    } catch (e) {
+      console.error('申请表上传失败:', e);
+      ElMessage.error(e?.message || e?.data || '申请表上传失败,请重试');
+    }
+  }
+
+  async function handleIdCardUploadSuccess(files: FileItem[]) {
+    if (!files?.length) {
+      ruleFormData.idCardUrl = '';
+      return;
+    }
+    try {
+      const list = await formatAttachmentList(files);
+      const jsonArr = (list || []).map((r) => ({
+        file_name: r.fileName,
+        url: r.fileUrl || '',
+      })).filter((x) => x.url);
+      ruleFormData.idCardUrl = JSON.stringify(jsonArr);
+    } catch (e) {
+      console.error('身份证正反面上传失败:', e);
+      ElMessage.error(e?.message || e?.data || '身份证正反面上传失败,请重试');
+    }
+  }
+
+  async function handleLaborContractUploadSuccess(files: FileItem[]) {
+    if (!files?.length) {
+      ruleFormData.laborContractUrl = '';
+      return;
+    }
+    try {
+      const list = await formatAttachmentList(files);
+      const jsonArr = (list || []).map((r) => ({
+        file_name: r.fileName,
+        url: r.fileUrl || '',
+      })).filter((x) => x.url);
+      ruleFormData.laborContractUrl = JSON.stringify(jsonArr);
+    } catch (e) {
+      console.error('劳动合同上传失败:', e);
+      ElMessage.error(e?.message || e?.data || '劳动合同上传失败,请重试');
+    }
+  }
+
+  async function handleInitialMedicalCertificateUploadSuccess(files: FileItem[]) {
+    if (!files?.length) {
+      ruleFormData.initialMedicalCertUrl = '';
+      return;
+    }
+    try {
+      const list = await formatAttachmentList(files);
+      const jsonArr = (list || []).map((r) => ({
+        file_name: r.fileName,
+        url: r.fileUrl || '',
+      })).filter((x) => x.url);
+      ruleFormData.initialMedicalCertUrl = JSON.stringify(jsonArr);
+    } catch (e) {
+      console.error('初次医疗证明上传失败:', e);
+      ElMessage.error(e?.message || e?.data || '初次医疗证明上传失败,请重试');
+    }
+  }
+
+  async function handleAgentIdCardUploadSuccess(files: FileItem[]) {
+    if (!files?.length) {
+      ruleFormData.trusteeIdCardUrl = '';
+      return;
+    }
+    try {
+      const list = await formatAttachmentList(files);
+      const jsonArr = (list || []).map((r) => ({
+        file_name: r.fileName,
+        url: r.fileUrl || '',
+      })).filter((x) => x.url);
+      ruleFormData.trusteeIdCardUrl = JSON.stringify(jsonArr);
+    } catch (e) {
+      console.error('被委托人员身份证正反面上传失败:', e);
+      ElMessage.error(e?.message || e?.data || '被委托人员身份证正反面上传失败,请重试');
+    }
+  }
+
+  const getDeptData = () => {
+    getAllDepartments().then((res) => {
+      firstLevelDepts.value = formatDeptTree(res);
+    });
+  };
+
+  const handleChangeDept = () => {
+    const deptInfo = cascaderRef.value?.getCheckedNodes();
+    if (deptInfo?.[0]) {
+      ruleFormData.departmentName = deptInfo[0].label;
+      ruleFormData.departmentCode = deptInfo[0].pathValues;
+    }
+  };
+
+  const refreshForm = () => {
+    approvalFormRef.value?.resetFields();
+    approvalForm.description = '';
+  };
+
+  const closeDialog = () => {
+    refreshForm();
+    basicDialogRef.value.closeDialog();
+  };
+
+  const handleSubmitApproval = () => {
+    approvalFormRef.value.validate(async (valid: boolean) => {
+      if (valid) {
+        // 构造后端需要的数据格式
+        const approvalData = {
+          id: currentId.value || newFormId.value || '',
+          templateId: ruleFormData.approvalTemplateId,
+          approvalDescription: approvalForm.description,
+          approvalInfoList: approvalNodeList.value.map((node) => {
+            let approverIdList: number[] = [];
+            if (node.approverType === APPROVER_TYPE.FIX) {
+              approverIdList = node.approverInfoList.map((info) => info.approverId);
+            } else if (approvalForm.approvers[node.id]) {
+              approverIdList = approvalForm.approvers[node.id];
+            }
+            return {
+              approvalOrder: node.approvalOrder,
+              approverIdList,
+            };
+          }),
+        };
+        await submitApprovalProcess(approvalData);
+        ElMessage.success('提交成功');
+        basicDialogRef.value.closeDialog();
+        router.back();
+      }
+    });
+  };
+
+  const getApprovalNode = async (id: number) => {
+    const res = await getApprovalNodeInstanceList(id);
+    approvalNodeList.value = res.approvalNodeInfoList || [];
+  };
+
+  async function handleAuditReject() {
+    if (!rejectReason.value?.trim()) {
+      ElMessage.warning('请选择审核不通过原因');
+      return;
+    }
+    if (!currentId.value) return;
+    auditSubmitting.value = true;
+    try {
+      await auditPurchaseApply({ id: currentId.value, status: auditType.value === true ? 2 : 3, rejectReason: rejectReason.value.trim(), approvalOrder: Number(approvalOrder.value),templateId: approvalTemplateId.value,code: rejectReason.value });
+      ElMessage.success('已提交审核不通过');
+      showRejectDialog.value = false;
+      router.back();
+    } catch (e) {
+      console.error('审核失败:', e);
+      ElMessage.error('审核失败,请重试');
+    } finally {
+      auditSubmitting.value = false;
+    }
+  }
+
   onMounted(() => {
     cloneRuleFormData();
     beforeRouteLeave();
-    if (isEditMode.value || isViewMode.value) {
+    getDeptData();
+    getApprovalList();
+    if (isEditMode.value || isViewMode.value || isAuditMode.value) {
       getDetail();
     }
   });
@@ -132,5 +671,4 @@
 
 <style scoped lang="scss">
   @use '@/styles/page-details-layout.scss' as *;
-</style>
-
+</style>

+ 85 - 0
src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/configs/constant.ts

@@ -0,0 +1,85 @@
+export enum EMERGENCY_PLAN_STATUS {
+  UNAPPROVED = 0,
+  APPROVAL_IN_PROGRESS,
+  RETURNED,
+  PUBLISHED,
+}
+
+export const EMERGENCY_PLAN_STATUS_MAP = {
+  [EMERGENCY_PLAN_STATUS.UNAPPROVED]: '未审批',
+  [EMERGENCY_PLAN_STATUS.APPROVAL_IN_PROGRESS]: '预案审批中',
+  [EMERGENCY_PLAN_STATUS.RETURNED]: '预案已退回',
+  [EMERGENCY_PLAN_STATUS.PUBLISHED]: '已公示',
+};
+
+export const EMERGENCY_PLAN_STATUS_OPTIONS = [
+  {
+    label: EMERGENCY_PLAN_STATUS_MAP[EMERGENCY_PLAN_STATUS.UNAPPROVED],
+    value: EMERGENCY_PLAN_STATUS.UNAPPROVED,
+  },
+  {
+    label: EMERGENCY_PLAN_STATUS_MAP[EMERGENCY_PLAN_STATUS.APPROVAL_IN_PROGRESS],
+    value: EMERGENCY_PLAN_STATUS.APPROVAL_IN_PROGRESS,
+  },
+  {
+    label: EMERGENCY_PLAN_STATUS_MAP[EMERGENCY_PLAN_STATUS.RETURNED],
+    value: EMERGENCY_PLAN_STATUS.RETURNED,
+  },
+  {
+    label: EMERGENCY_PLAN_STATUS_MAP[EMERGENCY_PLAN_STATUS.PUBLISHED],
+    value: EMERGENCY_PLAN_STATUS.PUBLISHED,
+  },
+];
+
+export enum APPROVAL_TYPE {
+  COUNTER_SIGN = 0,
+  ORDINARY_SIGN,
+}
+
+export const APPROVAL_TYPE_MAP = {
+  [APPROVAL_TYPE.COUNTER_SIGN]: '会签',
+  [APPROVAL_TYPE.ORDINARY_SIGN]: '或签',
+};
+
+export enum APPROVER_TYPE{
+  FIX = 0,
+  CUSTOM,
+}
+
+export const APPROVER_TYPE_MAP = {
+  [APPROVER_TYPE.FIX]: '固定',
+  [APPROVER_TYPE.CUSTOM]: '自选',
+};
+
+export enum APPROVAL_STATUS {
+  PENDING = 1,
+  APPROVED,
+  REJECTED,
+  OHTER,
+}
+
+export const APPROVAL_STATUS_MAP = {
+  [APPROVAL_STATUS.PENDING]: '待审批',
+  [APPROVAL_STATUS.APPROVED]: '已审批',
+  [APPROVAL_STATUS.REJECTED]: '退回',
+  [APPROVAL_STATUS.OHTER]: '他人已审批',
+};
+
+export const APPROVAL_STATUS_OPTIONS = [
+  {
+    label: APPROVAL_STATUS_MAP[APPROVAL_STATUS.PENDING],
+    value: APPROVAL_STATUS.PENDING,
+  },
+  {
+    label: APPROVAL_STATUS_MAP[APPROVAL_STATUS.APPROVED],
+    value: APPROVAL_STATUS.APPROVED,
+  },
+  {
+    label: APPROVAL_STATUS_MAP[APPROVAL_STATUS.REJECTED],
+    value: APPROVAL_STATUS.REJECTED,
+  },
+  {
+    label: APPROVAL_STATUS_MAP[APPROVAL_STATUS.OHTER],
+    value: APPROVAL_STATUS.OHTER,
+  },
+];

+ 80 - 28
src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/configs/form.ts

@@ -3,47 +3,89 @@ import { FormConfig } from '@/types/basic-form';
 export const INVENTORY_FORM_CONFIG: FormConfig[] = [
   {
     prop: 'itemName',
-    label: '物品名称:',
+    label: '姓名:',
     component: 'ElInput',
     componentProps: {
-      placeholder: '请输入物品名称',
+      placeholder: '请输入姓名',
     },
+  },
+   {
+    prop: 'applicantCode',
+    label: '工号:',
+    component: 'ElInput',
+    componentProps: {
+      placeholder: '请输入工号',
+    },
+  },
+   {
+    prop: 'departmentName',
+    label: '所属部门:',
+    slot: 'reviewDepartmentId',
   },
   {
     prop: 'warehouseDate',
-    label: '入库日期:',
+    label: '受伤时间:',
     component: 'ElDatePicker',
     componentProps: {
       type: 'date',
-      placeholder: '请选择入库日期',
+      placeholder: '请选择受伤时间',
       valueFormat: 'YYYY-MM-DD',
     },
   },
   {
-    prop: 'itemQuantity',
-    label: '物品数量:',
-    component: 'ElInputNumber',
-    componentProps: {
-      min: 1,
-      max: 99999,
-      precision: 0, // 不允许小数点,只能输入整数
-      placeholder: '请输入物品数量',
-    },
-  },
-  {
-    label: '备注:',
+    label: '受伤原因:',
     prop: 'remarks',
     component: 'ElInput',
     componentProps: {
       type: 'textarea',
       rows: 5,
-      placeholder: '请输入备注',
+      placeholder: '请输入受伤原因',
     },
   },
   {
-    prop: 'status',
-    label: '状态:',
-    slot: 'status',
+    prop: 'approvalTemplateId',
+    label: '审批流程:',
+    slot: 'approvalTemplateId',
+  },
+  {
+    prop: 'accidentReport',
+    label: '事故报告:',
+    slot: 'accidentReport',
+  },
+  {
+    prop: 'powerOfAttorney',
+    label: '委托书:',
+    slot: 'powerOfAttorney',
+  },
+  {
+    prop: 'addressConfirmation',
+    label: '地址确认书:',
+    slot: 'addressConfirmation',
+  },
+  {
+    prop: 'applicationForm',
+    label: '申请表:',
+    slot: 'applicationForm',
+  },
+  {
+    prop: 'idCard',
+    label: '身份证正反面:',
+    slot: 'idCard',
+  },
+  {
+    prop: 'laborContract',
+    label: '劳动合同:',
+    slot: 'laborContract',
+  },
+  {
+    prop: 'initialMedicalCertificate',
+    label: '初次医疗证明:',
+    slot: 'initialMedicalCertificate',
+  },
+  {
+    prop: 'agentIdCard',
+    label: '被委托人员身份证正反面:',
+    slot: 'agentIdCard',
   },
 ];
 
@@ -51,18 +93,28 @@ export const INVENTORY_FORM_DATA = {
   status: 'ENABLE',
   itemName: '',
   warehouseDate: '',
-  itemQuantity: 1, // 最小值为1
   remarks: '',
+  accidentCertUrl: '',
+  powerAttorneyUrl: '',
+  addressConfirmUrl: '',
+  applicationFormUrl: '',
+  idCardUrl: '',
+  laborContractUrl: '',
+  initialMedicalCertUrl: '',
+  trusteeIdCardUrl: '',
+  applicantName: '',
+  applicantCode: '',
+  departmentCode: '',
+  departmentName: '',
+  approvalTemplateId: '',
+  approvalOrder: 0,
 };
 
 export const INVENTORY_FORM_RULES = {
   status: [{ required: true, message: '请选择状态', trigger: 'change' }],
   itemName: [{ required: true, message: '请输入物品名称', trigger: 'blur' }],
   warehouseDate: [{ required: true, message: '请选择入库日期', trigger: 'change' }],
-  itemQuantity: [
-    { required: true, message: '请输入物品数量', trigger: 'blur' },
-    { type: 'number', min: 1, message: '物品数量不能小于1', trigger: 'blur' },
-    { type: 'number', max: 99999, message: '物品数量不能大于99999', trigger: 'blur' },
-  ],
-  // status: [{ required: true, message: '请选择状态', trigger: 'change' }],
-};
+  applicantCode: [{ required: true, message: '请输入工号', trigger: 'blur' }],
+  departmentCode: [{ required: true, message: '请选择所属部门', trigger: 'change' }],
+  remarks: [{ required: true, message: '请输入受伤原因', trigger: 'blur' }],
+};

+ 27 - 10
src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/configs/tables.ts

@@ -15,35 +15,52 @@ export const INVENTORY_TABLE_COLUMNS: TableColumnProps[] = [
     width: '80px',
   },
   {
-    label: '物品名称',
-    prop: 'itemName',
+    label: '申请单号',
+    prop: 'applyNo',
     align: 'left',
     minWidth: '120px',
   },
   {
-    label: '入库日期',
-    prop: 'warehouseDate',
+    label: '工号',
+    prop: 'applicantCode',
     align: 'left',
     minWidth: '120px',
   },
   {
-    label: '物品数量',
-    prop: 'itemQuantity',
+    label: '姓名',
+    prop: 'applicantName',
     align: 'center',
     minWidth: '120px',
   },
   {
-    label: '经办人',
-    prop: 'handler',
+    label: '所属部门',
+    prop: 'departmentName',
     align: 'left',
     minWidth: '120px',
   },
   {
-    label: '备注',
-    prop: 'remarks',
+    label: '受伤原因',
+    prop: 'injuryReason',
     align: 'left',
     minWidth: '150px',
   },
+  {
+    label: '受伤时间',
+    prop: 'injuryTime',
+    align: 'left',
+    minWidth: '150px',
+  },
+  {
+    label: '工伤类别',
+    prop: 'injuryCategoryName',
+    slot: 'injuryCategoryCode',
+  },
+  // {
+  //   label: '备注',
+  //   prop: 'remark',
+  //   align: 'left',
+  //   minWidth: '150px',
+  // },
   {
     label: '状态',
     prop: 'status',

+ 110 - 22
src/views/production-safety/risk-identification-and-control/work-injury-apply-manage/list.vue

@@ -24,7 +24,7 @@
                 <span>单号:</span>
                 <el-input
                   v-model="tableQuery.queryParam.stuffName"
-                  placeholder="搜索物品名称"
+                  placeholder="搜索申请单号/工号/姓名"
                   class="act-search-input"
                 />
               </div>
@@ -35,10 +35,26 @@
                   placeholder="请选择状态"
                   clearable
                 >
-                  <el-option label="启用" :value="true" />
-                  <el-option label="禁用" :value="false" />
+                  <el-option label="待审核" :value="1" />
+                  <el-option label="申请加盖公章及材料申请" :value="2" />
+                  <el-option label="审核不通过" :value="3" />
+                  <el-option label="已完成" :value="4" />
                 </el-select>
               </div>
+              <div class="select-box--item">
+                <span>所属部门:</span>
+                 <el-cascader
+                  v-model="tableQuery.queryParam.applicationDepartmentId"
+                  style="width: 170px"
+                  ref="cascaderRef"
+                  :options="firstLevelDepts"
+                  :props="cascaderProp"
+                  :show-all-levels="false"
+                  placeholder="部门名称"
+                  filterable
+                  @change="handleChangeDept"
+                />
+              </div>
             </section>
             <section class="search-btn">
               <el-button type="primary" @click="handleSearch">查询</el-button>
@@ -57,20 +73,41 @@
           >
             <template #status="scope">
               <span>
-                {{ scope.row.statusName || (scope.row.status === true || scope.row.status === 'true' ? '启用' : scope.row.status === false || scope.row.status === 'false' ? '禁用' : '-') }}
+                {{ scope.row.status === 1 ? '待审核' : scope.row.status === 2 ? '申请加盖公章及材料申请' : scope.row.status === 3 ? '审核不通过' : scope.row.status === 4 ? '已完成' : '-' }}
+              </span>
+            </template>
+            <template #injuryCategoryCode="scope">
+              <span>
+                {{ scope.row.injuryCategoryCode === '1' ? '责任事故' : scope.row.injuryCategoryCode === '2' ? '非责任事故' :  scope.row.injuryCategoryCode == '3' ? "交通事故" : '-' }}
               </span>
             </template>
             <template #action="scope">
               <div class="action-container--div" style="justify-content: left">
-                <ActionButton text="编辑" @click="handleEdit(scope.row.id)" />
-                <ActionButton
-                  text="删除"
-                  :popconfirm="{
-                    title: '确定要删除?',
-                  }"
-                  @confirm="handleDelete(scope.row.id)"
-                />
-                <ActionButton text="查看" @click="handleView(scope.row.id)" />
+                
+                <!-- 待审核:查看、审核 -->
+                <template v-if="scope.row.status === 1 || scope.row.status === '1'">
+                  <ActionButton text="查看" @click="handleView(scope.row.id)" />
+                  <ActionButton text="审核" @click="handleAudit(scope.row.id,scope.row)" />
+                </template>
+                <!-- 申请加盖公章及材料申请 -->
+                <template v-else-if="scope.row.status === 2 || scope.row.status === '2'">
+                  <ActionButton text="查看" @click="handleView(scope.row.id)" />
+                  <ActionButton text="确认" @click="handleAudit(scope.row.id,scope.row)" />
+                </template>
+                <!-- 审核不通过:编辑、删除、查看 -->
+                <template v-else-if="scope.row.status === 3 || scope.row.status === '3'">
+                  <ActionButton text="编辑" @click="handleEdit(scope.row.id)" />
+                  <ActionButton
+                    text="删除"
+                    :popconfirm="{ title: '确定要删除该申请吗?' }"
+                    @confirm="handleDelete(scope.row.id)"
+                  />
+                  <ActionButton text="查看" @click="handleView(scope.row.id!)" />
+                </template>
+                <!-- 已完成:查看 -->
+                <template v-else-if="scope.row.status === 4 || scope.row.status === '4'">
+                  <ActionButton text="查看" @click="handleView(scope.row.id!)" />
+                </template>
               </div>
             </template>
           </BasicTable>
@@ -104,9 +141,26 @@
   import BatchImport from '@/components/batch-import/BatchImport.vue';
   import { useGlobSetting } from '@/hooks/setting';
   import urlJoin from 'url-join';
+  import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
+  import { getAllDepartments } from '@/api/auth/dept';
+  import {
+    queryWorkInjuryApplyList,
+    type InventoryItem,
+    type InventoryQueryParam,
+  } from '@/api/production-safety/business-registration-application';
 
   const router = useRouter();
 
+  const firstLevelDepts = ref<any[]>([]);
+  const cascaderProp = {
+    expandTrigger: 'click',
+    checkStrictly: true,
+    // emitPath: false,
+    value: 'id',
+    label: 'deptName',
+  };
+  const cascaderRef = ref();
+
   // 表格
   const basicTableRef = ref<InstanceType<typeof BasicTable>>();
 
@@ -119,8 +173,9 @@
     pageSize: pagination.pageSize,
     queryParam: {
       stuffName: '', // 物品名称
-      status: true, // 状态,默认启用
+      status: '', // 状态,默认启用
       ids: [], // 选择数据的ID
+      applicationDepartmentId: '', // 所属部门ID
     },
   });
 
@@ -140,18 +195,24 @@
   async function getTableData() {
     tableConfig.loading = true;
     try {
-      const res = await queryInventoryManage(tableQuery);
+      const res = await queryWorkInjuryApplyList(tableQuery);
       if (res) {
         // 映射返回数据字段到表格字段
         tableData.value = res.records.map((item) => ({
           id: item.id,
-          itemName: item.stuffName, // 物品名称
-          warehouseDate: item.inStoreTime, // 入库日期
-          itemQuantity: item.stuffQty, // 物品数量
-          handler: item.createdUserName, // 经办人
-          remarks: item.remark, // 备注
-          status: item.status, // 状态:true-启用,false-禁用
-          statusName: item.statusName, // 状态名称
+          applyNo: item.applyNo, // 申请单号
+          applicantCode: item.applicantCode, // 工号
+          applicantName: item.applicantName, // 姓名
+          departmentName: item.departmentName, // 所属部门
+          injuryReason: item.injuryReason, // 受伤原因
+          injuryTime: item.injuryTime, // 受伤时间
+          injuryCategoryName: item.injuryCategoryName, // 工伤类别
+          remark: item.remark, // 备注
+          status: item.status, // 状态
+          approvalTemplateId: item.approvalTemplateId, // 审批模板ID
+          approvalOrder: item.approvalOrder, // 审批订单
+          templateId: item.templateId, // 模板ID
+          injuryCategoryCode: item.injuryCategoryCode, // 工伤类别编码
         }));
         pagination.total = res.totalRow;
       }
@@ -230,6 +291,18 @@
     });
   };
 
+  const handleAudit = (id: number,row: any) => {
+    router.push({
+      name: 'workInjuryApplyManageItem',
+      query: {
+        id,
+        approvalTemplateId:row.templateId ?? '',
+        approvalOrder:row.approvalOrder ?? '',
+        operate: 'inventory-audit',
+      },
+    });
+  }
+
   const handleDelete = async (id: number) => {
     try {
       await deleteInventory(id);
@@ -251,8 +324,23 @@
     });
   };
 
+  const getDeptData = () => {
+    getAllDepartments().then((res) => {
+      firstLevelDepts.value = formatDeptTree(res);
+    });
+  };
+  
+  const handleChangeDept = () => {
+    // const deptInfo = cascaderRef.value?.getCheckedNodes();
+    // if (deptInfo?.[0]) {
+    //   tableQuery.queryParam.department = deptInfo[0].label;
+    //   tableQuery.queryParam.departmentId = deptInfo[0].pathValues;
+    // }
+  };
+
   onMounted(() => {
     getTableData();
+    getDeptData();
   });
 </script>