ソースを参照

feat: 应急管理物资申领功能补充

ai0187 4 ヶ月 前
コミット
d5144cc028

+ 24 - 0
src/api/emergency-supplier/index.ts

@@ -160,6 +160,15 @@ export const getSupplyRequestList = (data: QueryPageRequest<SupplyRequestListQue
     data,
   });
 };
+/**
+ * 通过id查询应急物资申领计划信息
+ */
+export const getSupplyRequestInfoById = (planId: number) => {
+  return http.request<SupplyRequestListItem>({
+    url: `/emergencySupplies/queryEmergencySuppliesRequestPlan?planId=${planId}`,
+    method: 'get',
+  });
+};
 /**
  * 删除应急物资申领计划
  */
@@ -237,3 +246,18 @@ export const receiveSupplyRequestDetail = (data: ReceiveSupplyRequestDetailForm)
     data,
   });
 };
+/**
+ * 下载申领记录
+ */
+export const exportSupplyRequestRecord = (planId: number) => {
+  return http.request(
+    {
+      url: `/emergencySupplies/downloadRequestRecord?planId=${planId}`,
+      method: 'get',
+      responseType: 'blob',
+    },
+    {
+      isTransformResponse: false,
+    },
+  );
+};

+ 0 - 2
src/types/emergency-supplier/index.ts

@@ -133,8 +133,6 @@ export interface SupplyRequestDetailInfo {
   id: number;
   /*申领计划id */
   planId: number;
-  /*申领计划名称 */
-  planName: string;
   /*物资名称 */
   supplyName: string;
   /*规格 */

+ 27 - 6
src/views/emergency/emergency-supplies/PageSupplyRequest.vue

@@ -6,13 +6,19 @@
     <div class="safety-platform-container__main">
       <div class="search-table-container">
         <header class="disaster-precaution__header">
-          <el-button type="primary" class="search-table-container--button" :icon="Plus" @click="handleCreateRequest">
+          <el-button
+            type="primary"
+            class="search-table-container--button"
+            :icon="Plus"
+            @click="handleCreateRequest"
+            v-if="supplyRequestManagePermission"
+          >
             创建申领计划
           </el-button>
           <BasicSearch
             :searchConfig="SUPPLY_REQUEST_SEARCH_CONFIG"
             :searchData="searchData"
-            @update-search-data="handleSearch"
+            @update:search-data="handleSearch"
           >
             <template #planName>
               <el-input
@@ -27,8 +33,8 @@
         <BasicTable
           :tableData="tableData"
           :tableConfig="tableConfig"
-          @update-page-size="handleSizeChange"
-          @update-page-number="handleCurrentChange"
+          @update:page-size="handleSizeChange"
+          @update:page-number="handleCurrentChange"
         >
           <template #status="scope">
             <span>{{ getStatusText(scope.row.status) }}</span>
@@ -37,10 +43,15 @@
             <div class="action-container--div">
               <ActionButton text="详情" @click="handleViewDetail(scope.row.id)" />
               <template v-if="scope.row.status === SUPPLY_REQUEST_STATUS.APPLYING">
-                <ActionButton text="发起采购" @click="handleStartPurchase(scope.row.id, scope.row.purchaseDate)" />
+                <ActionButton
+                  text="发起采购"
+                  @click="handleStartPurchase(scope.row.id, scope.row.purchaseDate)"
+                  v-if="supplyRequestManagePermission"
+                />
                 <ActionButton
                   text="编辑"
                   @click="handleEdit(scope.row.id, scope.row.planName, scope.row.purchaseDate)"
+                  v-if="supplyRequestManagePermission"
                 />
                 <ActionButton
                   text="删除"
@@ -48,6 +59,7 @@
                     title: '确定删除?',
                   }"
                   @confirm="handleDelete(scope.row.id)"
+                  v-if="supplyRequestManagePermission"
                 />
               </template>
               <template
@@ -59,6 +71,7 @@
                 <ActionButton
                   text="编辑"
                   @click="handleEdit(scope.row.id, scope.row.planName, scope.row.purchaseDate)"
+                  v-if="supplyRequestManagePermission"
                 />
               </template>
             </div>
@@ -88,12 +101,17 @@
     SUPPLY_REQUEST_TABLE_MAX_HEIGHT_DEFAULT,
   } from './src/config';
   import useTableConfig from '@/hooks/useTableConfigHook';
+  import { useUserInfoHook } from '@/hooks/useUserInfoHook';
+  import { EMERGENCY_PERMISSIONS } from '@/views/emergency/src/constant';
   import { SUPPLY_REQUEST_STATUS, SUPPLY_REQUEST_STATUS_MAP } from './src/constant';
   import type { QueryPageRequest } from '@/types/basic-query';
   import type { SupplyRequestListQuery, SupplyRequestListItem } from '@/types/emergency-supplier';
   import { getSupplyRequestList, deleteSupplyRequest } from '@/api/emergency-supplier';
 
   const router = useRouter();
+  const { permissions } = useUserInfoHook();
+
+  const supplyRequestManagePermission = ref<Boolean>(false);
 
   const { tableConfig, pagination } = useTableConfig(SUPPLY_REQUEST_TABLE_COLUMNS, SUPPLY_REQUEST_TABLE_OPTIONS);
   let supplyRequestListQuery: QueryPageRequest<SupplyRequestListQuery> = {
@@ -152,7 +170,7 @@
 
   // 创建申领计划
   const handleCreateRequest = () => {
-    supplyRequestFormRef.value?.openDialog();
+    supplyRequestFormRef.value?.openAddDialog();
   };
 
   // 查看详情
@@ -194,6 +212,9 @@
   onMounted(() => {
     getTableData();
     tableConfig.maxHeight = SUPPLY_REQUEST_TABLE_MAX_HEIGHT_DEFAULT;
+    supplyRequestManagePermission.value = Boolean(
+      permissions.find((item: { code: string }) => item.code === EMERGENCY_PERMISSIONS.SUPPLY_REQUEST_MANAGE),
+    );
   });
 </script>
 

+ 60 - 33
src/views/emergency/emergency-supplies/PageSupplyRequestDetail.vue

@@ -3,13 +3,19 @@
     <div class="safety-platform-container__header">
       <div class="bread">
         <BreadcrumbBack />
-        <div class="breadcrumb-title">{{ planName || '物资采购申领详情' }}</div>
+        <div class="breadcrumb-title">{{ supplyRequestInfo?.planName || '物资采购申领详情' }}</div>
       </div>
     </div>
     <div class="safety-platform-container__main">
       <div class="detail-container">
         <header class="detail-header">
-          <el-button type="primary" :icon="Plus" @click="handleAddMaterial">添加需求物资</el-button>
+          <el-button
+            type="primary"
+            :icon="Plus"
+            @click="handleAddMaterial"
+            v-if="supplyRequestInfo?.status === SUPPLY_REQUEST_STATUS.APPLYING && supplyRequestManagePermission"
+            >添加需求物资</el-button
+          >
           <el-button type="primary" :icon="Download" @click="handleDownloadRecord">下载采购记录</el-button>
         </header>
         <div class="table-container">
@@ -27,7 +33,7 @@
                 <span>{{ getStatusText(scope.row.status) }}</span>
               </template>
             </el-table-column>
-            <el-table-column prop="claim" label="领用" width="100" align="center">
+            <el-table-column prop="claim" label="领用" width="100" align="center" v-if="supplyRequestManagePermission">
               <template #default="scope">
                 <el-link
                   v-if="scope.row.status === SUPPLY_REQUEST_STATUS.PURCHASING && !scope.row.isClaimed"
@@ -39,7 +45,14 @@
                 <span v-else>-</span>
               </template>
             </el-table-column>
-            <el-table-column prop="action" label="物资操作" width="150" align="center" fixed="right">
+            <el-table-column
+              prop="action"
+              label="物资操作"
+              width="150"
+              align="center"
+              fixed="right"
+              v-if="supplyRequestManagePermission"
+            >
               <template #default="scope">
                 <div class="action-container">
                   <el-link
@@ -65,6 +78,8 @@
         </div>
       </div>
     </div>
+    <!-- 领用弹窗 -->
+    <ReceiveSupplyDialog ref="receiveSupplyDialogRef" @success="handleClaimSuccess" />
   </div>
 </template>
 
@@ -75,7 +90,20 @@
   import { ElMessage, ElMessageBox } from 'element-plus';
   import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
   import { SUPPLY_REQUEST_STATUS, SUPPLY_REQUEST_STATUS_MAP } from './src/constant';
-  import { getSupplyRequestDetail, deleteSupplyRequestDetail } from '@/api/emergency-supplier';
+  import {
+    getSupplyRequestInfoById,
+    getSupplyRequestDetail,
+    deleteSupplyRequestDetail,
+    exportSupplyRequestRecord,
+  } from '@/api/emergency-supplier';
+  import { SupplyRequestListItem } from '@/types/emergency-supplier';
+  import { downloadFile } from '@/views/disaster/utils/download';
+  import ReceiveSupplyDialog from './src/components/ReceiveSupplyDialog.vue';
+  import { useUserInfoHook } from '@/hooks/useUserInfoHook';
+  import { EMERGENCY_PERMISSIONS } from '@/views/emergency/src/constant';
+
+  const { permissions } = useUserInfoHook();
+  const supplyRequestManagePermission = ref<Boolean>(false);
 
   // 表格行数据类型
   interface TableRowData {
@@ -96,10 +124,14 @@
   const route = useRoute();
   const id = Number(route.params.id);
 
+  const supplyRequestInfo = ref<SupplyRequestListItem>();
+
   const loading = ref(false);
-  const planName = ref('');
   const tableData = ref<TableRowData[]>([]);
 
+  // 领用弹窗相关
+  const receiveSupplyDialogRef = ref<InstanceType<typeof ReceiveSupplyDialog>>();
+
   // 获取状态文本
   const getStatusText = (status: number) => {
     return SUPPLY_REQUEST_STATUS_MAP[status] || '';
@@ -143,11 +175,6 @@
       const flatData: TableRowData[] = [];
 
       res.forEach((item) => {
-        // 设置计划名称(使用第一个 item 的 planName)
-        if (!planName.value && item.info.planName) {
-          planName.value = item.info.planName;
-        }
-
         // 将每个 detailList 项与 info 合并成一行
         item.detailList.forEach((detail) => {
           flatData.push({
@@ -183,32 +210,28 @@
   };
 
   // 下载采购记录
-  const handleDownloadRecord = () => {
-    // TODO: 实现下载采购记录功能
-    ElMessage.info('下载采购记录功能待实现');
-  };
-
-  // 领用
-  const handleClaim = async (row: TableRowData) => {
+  const handleDownloadRecord = async () => {
     try {
-      await ElMessageBox.confirm('确定要领用该物资吗?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      });
-      // TODO: 调用领用接口
-      // await claimMaterial(row.id);
-      console.log('领用物资:', row);
-      ElMessage.success('领用成功');
-      await getDetailData();
+      const res = await exportSupplyRequestRecord(id);
+      if (res.size === 0) return;
+      const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+      const url = window.URL.createObjectURL(blob);
+      downloadFile(url, '采购申领记录.xlsx');
     } catch (error) {
-      if (error !== 'cancel') {
-        console.error('领用失败:', error);
-        ElMessage.error('领用失败');
-      }
+      ElMessage.error('下载失败');
     }
   };
 
+  // 领用
+  const handleClaim = (row: TableRowData) => {
+    receiveSupplyDialogRef.value?.openDialog(row.materialName, row.quantity, row.id, id);
+  };
+
+  // 领用成功回调
+  const handleClaimSuccess = async () => {
+    await getDetailData();
+  };
+
   // 编辑
   const handleEdit = (row: TableRowData) => {
     // TODO: 打开编辑对话框,需要传递 requestId 和 detailId
@@ -232,8 +255,12 @@
     }
   };
 
-  onMounted(() => {
+  onMounted(async () => {
     getDetailData();
+    supplyRequestInfo.value = await getSupplyRequestInfoById(id);
+    supplyRequestManagePermission.value = Boolean(
+      permissions.find((item: { code: string }) => item.code === EMERGENCY_PERMISSIONS.SUPPLY_REQUEST_MANAGE),
+    );
   });
 </script>
 

+ 136 - 0
src/views/emergency/emergency-supplies/src/components/ReceiveSupplyDialog.vue

@@ -0,0 +1,136 @@
+<template>
+  <BasicDialog ref="basicDialogRef" title="物资领用" width="500px" @refresh="refreshFormData">
+    <template #form>
+      <div class="claim-dialog-content">
+        <div class="claim-dialog-label">请选择领用存放信息</div>
+        <el-select
+          v-model="selectedSupplyId"
+          placeholder="请选择需求部门"
+          filterable
+          style="width: 100%"
+          :loading="supplyListLoading"
+        >
+          <el-option
+            v-for="item in supplyList"
+            :key="item.id"
+            :label="`${item.supplyName} | ${item.location} | ${item.keeperName}`"
+            :value="item.id"
+          />
+        </el-select>
+      </div>
+    </template>
+    <template #footer>
+      <el-button @click="basicDialogRef?.closeDialog">取消</el-button>
+      <el-button type="primary" @click="handleSubmit" :loading="submitLoading">提交</el-button>
+    </template>
+  </BasicDialog>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import BasicDialog from '@/components/BasicDialog.vue';
+  import { getEmergencySuppliesInfoList } from '@/api/command-center';
+  import type { QueryEmergencySuppliesInfoListRes } from '@/api/command-center';
+  import { receiveSupplyRequestDetail } from '@/api/emergency-supplier';
+
+  interface Emits {
+    (e: 'success'): void;
+  }
+
+  const emit = defineEmits<Emits>();
+
+  const basicDialogRef = ref<InstanceType<typeof BasicDialog>>();
+  const selectedSupplyId = ref<number | null>(null);
+  const supplyList = ref<QueryEmergencySuppliesInfoListRes[]>([]);
+  const supplyListLoading = ref(false);
+  const submitLoading = ref(false);
+
+  // 当前领用的数据
+  const currentSupplyName = ref('');
+  const currentQuantity = ref(0);
+  const currentDetailId = ref(0);
+  const currentPlanId = ref(0);
+
+  // 打开对话框
+  const openDialog = (supplyName: string, quantity: number, detailId: number, planId: number) => {
+    currentSupplyName.value = supplyName;
+    currentQuantity.value = quantity;
+    currentDetailId.value = detailId;
+    currentPlanId.value = planId;
+    if (supplyName) {
+      loadSupplyList(supplyName);
+    }
+    basicDialogRef.value?.openDialog();
+  };
+
+  // 刷新表单数据
+  const refreshFormData = () => {
+    selectedSupplyId.value = null;
+    supplyList.value = [];
+  };
+
+  // 加载应急物资列表
+  const loadSupplyList = async (supplyName: string) => {
+    if (!supplyName) {
+      return;
+    }
+    supplyListLoading.value = true;
+    try {
+      const res = await getEmergencySuppliesInfoList({
+        supplyName,
+      });
+      supplyList.value = res || [];
+      if (supplyList.value.length === 0) {
+        ElMessage.warning('未找到匹配的应急物资信息');
+      }
+    } catch (error) {
+      console.error('获取应急物资列表失败:', error);
+      ElMessage.error('获取应急物资列表失败');
+    } finally {
+      supplyListLoading.value = false;
+    }
+  };
+
+  // 提交领用
+  const handleSubmit = async () => {
+    if (!selectedSupplyId.value) {
+      ElMessage.warning('请选择领用存放信息');
+      return;
+    }
+
+    submitLoading.value = true;
+    try {
+      await receiveSupplyRequestDetail({
+        supplyId: selectedSupplyId.value,
+        quantity: currentQuantity.value,
+        detailId: currentDetailId.value,
+        planId: currentPlanId.value,
+      });
+      ElMessage.success('领用操作成功');
+      basicDialogRef.value?.closeDialog();
+      emit('success');
+    } catch (error) {
+      console.error('领用失败:', error);
+      ElMessage.error('领用操作失败');
+    } finally {
+      submitLoading.value = false;
+    }
+  };
+
+  defineExpose({
+    openDialog,
+  });
+</script>
+
+<style scoped lang="scss">
+  .claim-dialog-content {
+    padding: 20px 0;
+  }
+
+  .claim-dialog-label {
+    margin-bottom: 12px;
+    font-size: 14px;
+    color: #606266;
+  }
+</style>

+ 5 - 8
src/views/emergency/emergency-supplies/src/components/SupplyRequestForm.vue

@@ -1,5 +1,5 @@
 <template>
-  <BasicDialog ref="basicDialogRef" :title="dialogTitle" @refresh="refreshFormData">
+  <BasicDialog ref="basicDialogRef" :title="dialogTitle" @close="refreshFormData">
     <template #form>
       <BasicForm ref="basicFormRef" :formData="ruleFormData" :formRules="formRules" :formConfig="ruleFormConfig" />
     </template>
@@ -40,7 +40,7 @@
   });
 
   // 打开对话框(创建模式)
-  const openDialog = () => {
+  const openAddDialog = () => {
     isEditMode.value = false;
     editId.value = undefined;
     basicDialogRef.value?.openDialog();
@@ -93,15 +93,12 @@
   // 刷新表单数据
   const refreshFormData = () => {
     basicFormRef.value?.clearValidate();
-    if (!isEditMode.value) {
-      // 重置表单数据
-      ruleFormData.planName = '';
-      ruleFormData.purchaseDate = '';
-    }
+    ruleFormData.planName = '';
+    ruleFormData.purchaseDate = '';
   };
 
   defineExpose({
-    openDialog,
+    openAddDialog,
     openEditDialog,
   });
 </script>

+ 1 - 1
src/views/emergency/emergency-supplies/src/config/table.ts

@@ -258,7 +258,7 @@ export const SUPPLY_REQUEST_TABLE_COLUMNS: TableColumnProps[] = [
   {
     label: '采购日期',
     prop: 'purchaseDate',
-    width: '140px',
+    width: '180px',
   },
   {
     label: '总价(元)',

+ 3 - 0
src/views/emergency/src/constant.ts

@@ -9,4 +9,7 @@ export const EMERGENCY_PERMISSIONS = {
   // 应急处置-应急指挥中心管理权限:打开、编辑
   EMERGENCY_COMMAND_CENTER_MANAGE: 'emergency_business_module:emergency_command_center_manage',
   PLANE_MANAGEMENT: 'emergency_business_module:plan_management',
+
+  // 应急物资申领权限
+  SUPPLY_REQUEST_MANAGE: 'emergency_business_module:supply_request_manage',
 };