Jelajahi Sumber

feat: 应急管理物资申领全流程完成

ai0187 4 bulan lalu
induk
melakukan
42ab47f96b

+ 1 - 1
src/views/emergency/emergency-supplies/PageSupplyRequest.vue

@@ -46,7 +46,7 @@
                 <ActionButton
                   text="发起采购"
                   @click="handleStartPurchase(scope.row.id, scope.row.purchaseDate)"
-                  v-if="supplyRequestManagePermission"
+                  v-if="supplyRequestManagePermission && scope.row.totalPrice > 0"
                 />
                 <ActionButton
                   text="编辑"

+ 20 - 9
src/views/emergency/emergency-supplies/PageSupplyRequestDetail.vue

@@ -81,7 +81,7 @@
     <!-- 领用弹窗 -->
     <ReceiveSupplyDialog ref="receiveSupplyDialogRef" @success="handleClaimSuccess" />
     <!-- 添加物资弹窗 -->
-    <AddSuppliesDrawer ref="addSupplyDrawerRef" />
+    <AddSuppliesDrawer ref="addSupplyDrawerRef" @success="handleAddMaterialSuccess" />
   </div>
 </template>
 
@@ -91,13 +91,14 @@
   import { Plus, Download } from '@element-plus/icons-vue';
   import { ElMessage, ElMessageBox } from 'element-plus';
   import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
-  import { SUPPLY_REQUEST_STATUS, SUPPLY_REQUEST_STATUS_MAP } from './src/constant';
+  import { SUPPLY_REQUEST_STATUS, SUPPLY_REQUEST_DETAIL_STATUS_MAP } from './src/constant';
   import {
     getSupplyRequestInfoById,
     getSupplyRequestDetail,
     deleteSupplyRequestDetail,
     exportSupplyRequestRecord,
   } from '@/api/emergency-supplier';
+  import type { SupplyRequestDetailItem } from '@/types/emergency-supplier';
   import { SupplyRequestListItem } from '@/types/emergency-supplier';
   import { downloadFile } from '@/views/disaster/utils/download';
   import ReceiveSupplyDialog from './src/components/ReceiveSupplyDialog.vue';
@@ -132,19 +133,20 @@
   const loading = ref(false);
   const tableData = ref<TableRowData[]>([]);
   const addSupplyDrawerRef = ref<InstanceType<typeof AddSuppliesDrawer>>();
+  const supplyRequestDetailData = ref<SupplyRequestDetailItem[]>([]);
 
   // 领用弹窗相关
   const receiveSupplyDialogRef = ref<InstanceType<typeof ReceiveSupplyDialog>>();
 
   // 获取状态文本
   const getStatusText = (status: number) => {
-    return SUPPLY_REQUEST_STATUS_MAP[status] || '';
+    return SUPPLY_REQUEST_DETAIL_STATUS_MAP[status] || '';
   };
 
   // 合并单元格方法
   const handleSpanMethod = ({ row, rowIndex, columnIndex }: any) => {
-    // 需要合并的列索引:物资名称(1)、规格(2)、单价(3)、小计(4)
-    const mergeColumns = [1, 2, 3, 4];
+    // 需要合并的列索引:物资名称(1)、规格(2)、单价(3)、小计(4)、物资操作(10)
+    const mergeColumns = [1, 2, 3, 4, 10];
     if (!mergeColumns.includes(columnIndex)) {
       return { rowspan: 1, colspan: 1 };
     }
@@ -174,6 +176,7 @@
     loading.value = true;
     try {
       const res = await getSupplyRequestDetail(id);
+      supplyRequestDetailData.value = res;
 
       // 将 SupplyRequestDetailItem[] 转换为表格需要的扁平化数据结构
       const flatData: TableRowData[] = [];
@@ -209,10 +212,14 @@
 
   // 添加需求物资
   const handleAddMaterial = () => {
-    // TODO: 打开添加物资的对话框
     addSupplyDrawerRef.value?.openDrawer();
   };
 
+  // 添加物资成功回调
+  const handleAddMaterialSuccess = async () => {
+    await getDetailData();
+  };
+
   // 下载采购记录
   const handleDownloadRecord = async () => {
     try {
@@ -238,9 +245,13 @@
 
   // 编辑
   const handleEdit = (row: TableRowData) => {
-    // TODO: 打开编辑对话框,需要传递 requestId 和 detailId
-    console.log('编辑物资:', row);
-    ElMessage.info('编辑功能待实现');
+    // 根据 infoId 找到对应的 SupplyRequestDetailItem
+    const editData = supplyRequestDetailData.value.find((item) => item.info.id === row.infoId);
+    if (!editData) {
+      ElMessage.error('未找到要编辑的数据');
+      return;
+    }
+    addSupplyDrawerRef.value?.openDrawer(editData);
   };
 
   // 删除

+ 83 - 14
src/views/emergency/emergency-supplies/src/components/AddSuppliesDrawer.vue

@@ -2,8 +2,8 @@
   <el-drawer
     v-model="showDrawer"
     direction="rtl"
-    title="添加需求物资"
-    size="50%"
+    :title="isEditMode ? '编辑需求物资' : '添加需求物资'"
+    size="35%"
     :close-on-click-modal="false"
     destroy-on-close
   >
@@ -35,12 +35,12 @@
         <el-input v-model.number="formData.info.unitPrice" placeholder="请输入单价" type="number" min="0" />
       </el-form-item>
       <el-form-item label="小计:" prop="info.subtotal">
-        <el-input v-model="formData.info.subtotal" placeholder="请输入小计" type="textarea" rows: 3, />
+        <el-input v-model="formData.info.subtotal" placeholder="请输入小计" type="textarea" :rows="3" />
       </el-form-item>
       <el-form-item label="需求详情:" v-if="deptTree">
         <AddSuppliesSubForm
           ref="subFormRef"
-          v-for="(item, index) in formData.detailList"
+          v-for="item in formData.detailList"
           :key="item.id"
           :init-data="item"
           :dept-tree="deptTree"
@@ -65,8 +65,12 @@
 <script setup lang="ts">
   import { ElDrawer, ElForm, ElInput, ElSelect, ElMessage } from 'element-plus';
   import { ref, reactive, onMounted, useTemplateRef, computed } from 'vue';
-  import type { AddSupplyRequestForm, AddSupplyRequestSubForm } from '@/types/emergency-supplier';
-  import { createSupplyRequestDetail, getSupplyNameList } from '@/api/emergency-supplier';
+  import type {
+    AddSupplyRequestForm,
+    AddSupplyRequestSubForm,
+    SupplyRequestDetailItem,
+  } from '@/types/emergency-supplier';
+  import { createSupplyRequestDetail, getSupplyNameList, updateSupplyRequestDetail } from '@/api/emergency-supplier';
   import AddSuppliesSubForm from './AddSuppliesSubForm.vue';
 
   import type { DeptTree } from '@/types/dept/type';
@@ -80,6 +84,8 @@
   const subFormRefs = useTemplateRef('subFormRef');
 
   const showDrawer = ref(false);
+  const isEditMode = ref(false);
+  const editInfoId = ref<number | null>(null);
 
   const lastDetailId = ref(0);
 
@@ -120,20 +126,69 @@
         id: lastDetailId.value,
       },
     ];
+    isEditMode.value = false;
+    editInfoId.value = null;
+  };
+
+  // 初始化编辑数据
+  const initEditData = (editData: SupplyRequestDetailItem) => {
+    isEditMode.value = true;
+    editInfoId.value = editData.info.id;
+
+    // 设置 info 数据
+    formData.info = {
+      id: editData.info.id,
+      supplyName: editData.info.supplyName,
+      specs: editData.info.specs,
+      unitPrice: editData.info.unitPrice,
+      subtotal: editData.info.subtotal,
+    };
+
+    // 设置 detailList 数据(编辑时不需要保留 id,使用负数作为临时 id)
+    formData.detailList = editData.detailList.map((detail, index) => {
+      return {
+        id: -(index + 1), // 使用负数作为临时 id
+        deptId: detail.deptId,
+        deptName: detail.deptName,
+        quantity: detail.quantity,
+        sizeDetail: detail.sizeDetail,
+      };
+    });
+
+    // 更新 lastDetailId,确保新增项的 id 不会冲突
+    if (formData.detailList.length > 0) {
+      lastDetailId.value = formData.detailList.length;
+    }
   };
 
   // 打开需求列表&数据初始化
-  const openDrawer = () => {
-    // 创建时清理为初始化 TODO 编辑时通过参数进行初始化
-    clearFormData();
+  const openDrawer = async (editData?: SupplyRequestDetailItem) => {
+    // 先打开 drawer,再加载数据
     showDrawer.value = true;
+
+    // 如果是编辑模式,初始化编辑数据
+    if (editData) {
+      initEditData(editData);
+    } else {
+      // 创建时清理为初始化
+      clearFormData();
+    }
+
+    // 重新加载数据,确保数据是最新的
+    try {
+      await Promise.all([getSuppliesNameList(), getDeptTreeData()]);
+    } catch (error) {
+      console.error('加载数据失败:', error);
+      // 即使数据加载失败,drawer 也应该保持打开状态
+    }
   };
 
   // 表单需求详情加一项
   const addDetail = () => {
     lastDetailId.value += 1;
+    // 使用负数作为新项的 id,避免与已存在的记录 id 冲突
     formData.detailList.push({
-      id: lastDetailId.value,
+      id: -lastDetailId.value,
     });
   };
 
@@ -167,6 +222,7 @@
     return {
       info: { ...formData.info, planId },
       detailList: formData.detailList.map((item) => {
+        // 编辑时不需要保留 detailList 的 id,只保留 info 的 id
         return {
           deptId: item.deptId,
           deptName: item.deptName,
@@ -177,18 +233,31 @@
     };
   };
 
+  interface Emits {
+    (e: 'success'): void;
+  }
+
+  const emit = defineEmits<Emits>();
+
   // 提交表单
   const handleSubmit = async () => {
     const res = await formValidate();
     if (!res) return;
     const data = getFormData();
-    // console.log(data);
     try {
-      await createSupplyRequestDetail(data);
-      ElMessage.success('提交成功');
+      if (isEditMode.value && editInfoId.value) {
+        // 编辑模式
+        await updateSupplyRequestDetail(data);
+        ElMessage.success('编辑成功');
+      } else {
+        // 创建模式
+        await createSupplyRequestDetail(data);
+        ElMessage.success('提交成功');
+      }
       showDrawer.value = false;
+      emit('success');
     } catch (error) {
-      ElMessage.error('提交失败');
+      ElMessage.error(isEditMode.value ? '编辑失败' : '提交失败');
     }
   };
 

+ 3 - 3
src/views/emergency/emergency-supplies/src/components/AddSuppliesSubForm.vue

@@ -33,7 +33,7 @@
           v-model.number="subFormData.quantity"
           placeholder="请输入数量"
           type="number"
-          min="0"
+          min="1"
           @change="emits('updateSubForm', subFormData)"
         />
       </el-form-item>
@@ -101,8 +101,8 @@
 
   const validateFormNumber = (rule: any, value: number, callback: any) => {
     if (!Number.isInteger(value)) return callback(new Error('请输入正整数'));
-    if (value < 0) {
-      callback(new Error('输入不能小于0'));
+    if (value <= 0) {
+      callback(new Error('请输入正整数'));
     } else {
       callback();
     }

+ 1 - 1
src/views/emergency/emergency-supplies/src/components/DiscardSupplies.vue

@@ -58,7 +58,7 @@
         if (value == null) return callback(new Error('请输入报废数量'));
         if (!Number.isInteger(value)) return callback(new Error('请输入正整数'));
         if (value > supply.value?.currentQuantity) {
-          callback(new Error('超过数量上限'));
+          callback(new Error('报废数量不能超过物资当前数量'));
         } else if (value <= 0) {
           callback(new Error('数量不能小于1'));
         } else {

+ 9 - 2
src/views/emergency/emergency-supplies/src/components/ReceiveSupplyDialog.vue

@@ -13,7 +13,7 @@
           <el-option
             v-for="item in supplyList"
             :key="item.id"
-            :label="`${item.supplyName} | ${item.location} | ${item.keeperName}`"
+            :label="`${item.supplyName} | ${getLocation(item.location)} | ${item.keeperName}`"
             :value="item.id"
           />
         </el-select>
@@ -27,12 +27,15 @@
 </template>
 
 <script setup lang="ts">
-  import { ref } from 'vue';
+  import { onMounted, 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';
+  import { useEmergencySuppliesHook } from '@/views/emergency/emergency-supplies/src/hook';
+
+  const { getLocationDict, getLocation } = useEmergencySuppliesHook();
 
   interface Emits {
     (e: 'success'): void;
@@ -121,6 +124,10 @@
   defineExpose({
     openDialog,
   });
+
+  onMounted(() => {
+    getLocationDict('all');
+  });
 </script>
 
 <style scoped lang="scss">

+ 18 - 1
src/views/emergency/emergency-supplies/src/constant/index.ts

@@ -32,12 +32,14 @@ enum CHANGE_TYPE {
   CHECK = 1,
   CHANGE,
   DISCARD,
+  REQUEST,
 }
 
 export const CHANGE_TYPE_MAP = {
   [CHANGE_TYPE.CHECK]: '物资盘点',
   [CHANGE_TYPE.CHANGE]: '数量变更',
   [CHANGE_TYPE.DISCARD]: '报废',
+  [CHANGE_TYPE.REQUEST]: '物资申领',
 };
 
 export enum INVENTORY_RESULT {
@@ -62,7 +64,7 @@ export const INVENTORY_RESULT_OPTIONS = [
 ];
 
 /**
- * 物资申领状态
+ * 物资申领计划状态
  */
 export enum SUPPLY_REQUEST_STATUS {
   ALL = 0,
@@ -87,3 +89,18 @@ export const SUPPLY_REQUEST_STATUS_OPTIONS = [
   { label: SUPPLY_REQUEST_STATUS_MAP[SUPPLY_REQUEST_STATUS.RECEIVING], value: SUPPLY_REQUEST_STATUS.RECEIVING },
   { label: SUPPLY_REQUEST_STATUS_MAP[SUPPLY_REQUEST_STATUS.RECEIVED], value: SUPPLY_REQUEST_STATUS.RECEIVED },
 ];
+
+/**
+ * 物资申领计划物资状态
+ */
+export enum SUPPLY_REQUEST_DETAIL_STATUS {
+  APPLYING = 1, // 申请中
+  PURCHASING = 2, // 采购中
+  RECEIVED = 3, // 已领用
+}
+
+export const SUPPLY_REQUEST_DETAIL_STATUS_MAP = {
+  [SUPPLY_REQUEST_DETAIL_STATUS.APPLYING]: '申请中',
+  [SUPPLY_REQUEST_DETAIL_STATUS.PURCHASING]: '采购中',
+  [SUPPLY_REQUEST_DETAIL_STATUS.RECEIVED]: '已领用',
+};