Kaynağa Gözat

Merge branch 'dev-bxy' into 'dev'

fix: 应急管理优化项+部分bug修复

See merge request product-group-fe/sfy-safety-group/sfy-safety!340
毕欣怡 1 ay önce
ebeveyn
işleme
9012450cb9
27 değiştirilmiş dosya ile 587 ekleme ve 167 silme
  1. 19 2
      src/api/emergency-supplier/index.ts
  2. 54 29
      src/components/CameraGroupListAndTree/CameraGroupList/CameraTreeOfGroupList.vue
  3. 62 40
      src/components/monitor-camera-edit/UpdateMonitorArea.vue
  4. 3 1
      src/layout/components/left-menu/LeftMenu.vue
  5. 18 1
      src/types/emergency-supplier/index.ts
  6. 2 0
      src/views/disaster/overview/components/DisasterCheckingLists.vue
  7. 18 3
      src/views/emergency/emergency-drill/PageDrillPlanList.vue
  8. 2 3
      src/views/emergency/emergency-drill/PageDrillPlanView.vue
  9. 1 0
      src/views/emergency/emergency-drill/constants.ts
  10. 11 0
      src/views/emergency/emergency-plan/src/components/AddManagementDetail.vue
  11. 3 6
      src/views/emergency/emergency-plan/src/config/form.ts
  12. 2 2
      src/views/emergency/emergency-plan/src/config/search.ts
  13. 1 1
      src/views/emergency/emergency-plan/src/config/table.ts
  14. 37 2
      src/views/emergency/emergency-plan/src/constant.ts
  15. 11 10
      src/views/emergency/emergency-supplies/PageSupplyRequest.vue
  16. 47 22
      src/views/emergency/emergency-supplies/PageSupplyRequestDetail.vue
  17. 7 29
      src/views/emergency/emergency-supplies/src/components/AddSuppliesDrawer.vue
  18. 11 0
      src/views/emergency/emergency-supplies/src/components/AddSuppliesSubForm.vue
  19. 56 0
      src/views/emergency/emergency-supplies/src/components/ChangeRecord.vue
  20. 35 9
      src/views/emergency/emergency-supplies/src/components/DiscardSupplies.vue
  21. 95 0
      src/views/emergency/emergency-supplies/src/components/NotifyDepartmentDialog.vue
  22. 1 1
      src/views/emergency/emergency-supplies/src/components/StartPurchaseForm.vue
  23. 1 1
      src/views/emergency/emergency-supplies/src/components/SupplyRequestForm.vue
  24. 56 0
      src/views/emergency/emergency-supplies/src/config/form.ts
  25. 6 0
      src/views/emergency/emergency-supplies/src/config/index.ts
  26. 8 5
      src/views/emergency/emergency-supplies/src/config/table.ts
  27. 20 0
      src/views/emergency/emergency-supplies/src/constant/index.ts

+ 19 - 2
src/api/emergency-supplier/index.ts

@@ -15,6 +15,7 @@ import type {
   SupplyRequestDetailItem,
   SupplyRequestDetailItem,
   ReceiveSupplyRequestDetailForm,
   ReceiveSupplyRequestDetailForm,
   AddSupplyQuery,
   AddSupplyQuery,
+  NotifyDepartmentForm,
 } from '@/types/emergency-supplier';
 } from '@/types/emergency-supplier';
 
 
 /**
 /**
@@ -127,11 +128,16 @@ export const saveInventoryTask = (taskName: string, endTime: string) => {
 /**
 /**
  * 物资报废
  * 物资报废
  */
  */
-export const discardEmergencySupply = (supplyId: number, quantity: number) => {
+export const discardEmergencySupply = (data: {
+  supplyId: number;
+  quantity: number;
+  scrapReason: string;
+  scrapImage: string;
+}) => {
   return http.request({
   return http.request({
     url: '/emergencySupplies/scrapEmergencySupplies',
     url: '/emergencySupplies/scrapEmergencySupplies',
     method: 'post',
     method: 'post',
-    data: { supplyId, quantity },
+    data,
   });
   });
 };
 };
 /**
 /**
@@ -271,3 +277,14 @@ export const getSupplyNameList = (planId: number) => {
     method: 'get',
     method: 'get',
   });
   });
 };
 };
+
+/**
+ * 通知领用物资
+ */
+export const notifySupplyRequestDetail = (data: NotifyDepartmentForm) => {
+  return http.request({
+    url: '/emergencySupplies/noticeReceiveSupplies',
+    method: 'post',
+    data,
+  });
+};

+ 54 - 29
src/components/CameraGroupListAndTree/CameraGroupList/CameraTreeOfGroupList.vue

@@ -29,23 +29,27 @@
               selectedCamera: isSelected(node, data.id),
               selectedCamera: isSelected(node, data.id),
             }"
             }"
           >
           >
-            <div v-if="data.nodeType === CameraTreeNodeType.camera" class="icons">
-              <VideoCamera class="cameraIcon" />
-              <WarningFilled v-if="isInvalid(data)" class="invalidCamera" style="color: red" />
-            </div>
-
-            <div class="cameraName">
-              {{ node.label }}
-            </div>
-
             <Thumbnail
             <Thumbnail
               v-if="data.nodeType === CameraTreeNodeType.camera"
               v-if="data.nodeType === CameraTreeNodeType.camera"
               :imageUrl="data.imageUrl"
               :imageUrl="data.imageUrl"
               :code="data.code"
               :code="data.code"
               position="right"
               position="right"
             >
             >
-              <div class="mask"></div>
+              <div class="iconAndCameraName">
+                <div class="icons">
+                  <VideoCamera class="cameraIcon" />
+                  <WarningFilled v-if="isInvalid(data)" class="invalidCamera" style="color: red" />
+                </div>
+                <div class="cameraName">
+                  {{ node.label }}
+                </div>
+              </div>
             </Thumbnail>
             </Thumbnail>
+            <template v-else>
+              <div class="cameraName">
+                {{ node.label }}
+              </div>
+            </template>
           </div>
           </div>
         </template>
         </template>
       </el-tree>
       </el-tree>
@@ -188,7 +192,34 @@
         display: flex;
         display: flex;
         align-items: center;
         align-items: center;
         color: black;
         color: black;
-        // position: relative;
+        position: relative;
+        width: 100%;
+        .iconAndCameraName {
+          display: flex;
+          align-items: center;
+          width: 100%;
+          .icons {
+            display: flex;
+            align-items: center;
+            position: relative;
+            .cameraIcon {
+              height: 18px;
+              margin-right: 8px;
+              color: black;
+            }
+            .invalidCamera {
+              height: 14px;
+              color: #dd5869;
+              position: absolute;
+              right: 0px;
+              top: -5px;
+            }
+          }
+          .cameraName {
+            width: fit-content;
+            position: relative;
+          }
+        }
         .icons {
         .icons {
           display: flex;
           display: flex;
           align-items: center;
           align-items: center;
@@ -198,30 +229,24 @@
             margin-right: 8px;
             margin-right: 8px;
             color: black;
             color: black;
           }
           }
-          .invalidCamera {
-            height: 14px;
-            color: #dd5869;
-            position: absolute;
-            right: 0px;
-            top: -5px;
-          }
         }
         }
-      }
-      .mask {
-        height: 26px;
-        width: 100%;
+        .cameraName {
+          flex: 1;
+        }
       }
       }
     }
     }
   }
   }
 
 
   :deep(.thumb-nail) {
   :deep(.thumb-nail) {
-    display: block;
-    position: absolute;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    z-index: 99;
+    display: flex;
+    align-items: center;
+    flex: 1;
+    height: 100%;
+  }
+
+  :deep(.thumb-nail > div) {
+    width: 100%;
+    height: 100%;
   }
   }
 
 
   :deep(.el-tree-node__content) {
   :deep(.el-tree-node__content) {

+ 62 - 40
src/components/monitor-camera-edit/UpdateMonitorArea.vue

@@ -27,24 +27,32 @@
                 selectedCamera: isSelected(data),
                 selectedCamera: isSelected(data),
               }"
               }"
             >
             >
-              <div v-if="data.nodeType === CameraTreeNodeType.camera" class="icons">
-                <VideoCamera
-                  class="cameraIcon"
-                  :class="{
-                    selectedCameraIcon: isSelected(data),
-                  }"
-                />
-                <WarningFilled v-if="isInvalid(data)" class="invalidCamera" style="color: red" />
-              </div>
-              <div> {{ node.label }}</div>
               <Thumbnail
               <Thumbnail
                 v-if="data.nodeType === CameraTreeNodeType.camera"
                 v-if="data.nodeType === CameraTreeNodeType.camera"
                 :imageUrl="data.imageUrl"
                 :imageUrl="data.imageUrl"
                 :code="data.code"
                 :code="data.code"
                 position="right"
                 position="right"
               >
               >
-                <div class="mask"></div>
+                <div class="iconAndCameraName">
+                  <div class="icons">
+                    <VideoCamera
+                      class="cameraIcon"
+                      :class="{
+                        selectedCameraIcon: isSelected(data),
+                      }"
+                    />
+                    <WarningFilled v-if="isInvalid(data)" class="invalidCamera" style="color: red" />
+                  </div>
+                  <div class="cameraName">
+                    {{ node.label }}
+                  </div>
+                </div>
               </Thumbnail>
               </Thumbnail>
+              <template v-else>
+                <div class="cameraName">
+                  {{ node.label }}
+                </div>
+              </template>
             </div>
             </div>
           </template>
           </template>
         </el-tree>
         </el-tree>
@@ -106,7 +114,7 @@
   const selectedItems = ref<SelectedCameraIds[]>([]); // 已选择的相机列表
   const selectedItems = ref<SelectedCameraIds[]>([]); // 已选择的相机列表
 
 
   const isSelected = (data: CameraInTree) => {
   const isSelected = (data: CameraInTree) => {
-    if (data.nodeType === 'camera') return selectedItems.value.find((x) => x.id === data.id);
+    if (data.nodeType === CameraTreeNodeType.camera) return selectedItems.value.find((x) => x.id === data.id);
   };
   };
 
 
   const isInvalid = (data) => {
   const isInvalid = (data) => {
@@ -125,7 +133,7 @@
 
 
   // 处理树节点的勾选变化
   // 处理树节点的勾选变化
   function handleCheckChange(data: CameraInTree, checked: boolean) {
   function handleCheckChange(data: CameraInTree, checked: boolean) {
-    if (checked && data.nodeType === 'camera') {
+    if (checked && data.nodeType === CameraTreeNodeType.camera) {
       if (!selectedItems.value.find((x) => x.id === data.id)) {
       if (!selectedItems.value.find((x) => x.id === data.id)) {
         selectedItems.value.push({
         selectedItems.value.push({
           id: data.id,
           id: data.id,
@@ -199,45 +207,59 @@
   .treeNode {
   .treeNode {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
+    position: relative;
+    width: 100%;
 
 
-    .icons {
+    .iconAndCameraName {
       display: flex;
       display: flex;
       align-items: center;
       align-items: center;
-      position: relative;
-
-      .cameraIcon {
-        height: 18px;
-        margin-right: 8px;
-        color: rgb(112, 112, 112);
+      width: 100%;
+
+      .icons {
+        display: flex;
+        align-items: center;
+        position: relative;
+
+        .cameraIcon {
+          height: 18px;
+          margin-right: 8px;
+          color: rgb(112, 112, 112);
+        }
+
+        .selectedCameraIcon {
+          color: #1777ff;
+        }
+
+        .invalidCamera {
+          height: 14px;
+          color: #dd5869;
+          position: absolute;
+          right: 0px;
+          top: -5px;
+        }
       }
       }
 
 
-      .selectedCameraIcon {
-        color: #1777ff;
+      .cameraName {
+        width: fit-content;
+        position: relative;
       }
       }
+    }
 
 
-      .invalidCamera {
-        height: 14px;
-        color: #dd5869;
-        position: absolute;
-        right: 0px;
-        top: -5px;
-      }
+    .cameraName {
+      flex: 1;
     }
     }
   }
   }
 
 
-  .mask {
-    height: 26px;
-    width: 100%;
+  :deep(.thumb-nail) {
+    display: flex;
+    align-items: center;
+    flex: 1;
+    height: 100%;
   }
   }
 
 
-  :deep(.thumb-nail) {
-    display: block;
-    position: absolute;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    z-index: 99;
+  :deep(.thumb-nail > div) {
+    width: 100%;
+    height: 100%;
   }
   }
 
 
   :deep(.el-tree-node__content) {
   :deep(.el-tree-node__content) {

+ 3 - 1
src/layout/components/left-menu/LeftMenu.vue

@@ -2,7 +2,7 @@
 <template>
 <template>
   <div class="component-container home-container">
   <div class="component-container home-container">
     <aside class="aside">
     <aside class="aside">
-      <header class="aside__header" />
+      <header class="aside__header"></header>
       <main class="aside__main">
       <main class="aside__main">
         <el-menu :default-active="selectedKeys" :default-openeds="openKeys" router class="el-menu-vertical">
         <el-menu :default-active="selectedKeys" :default-openeds="openKeys" router class="el-menu-vertical">
           <template v-for="item in subMenus" :key="item.name">
           <template v-for="item in subMenus" :key="item.name">
@@ -117,6 +117,8 @@
     flex-shrink: 0;
     flex-shrink: 0;
     border-radius: 4px;
     border-radius: 4px;
     background-color: $white-color;
     background-color: $white-color;
+    overflow-x: hidden;
+    overflow-y: auto;
     &__header {
     &__header {
       width: inherit;
       width: inherit;
       height: 10px;
       height: 10px;

+ 18 - 1
src/types/emergency-supplier/index.ts

@@ -57,6 +57,8 @@ export interface ExportDiscardForm {
 }
 }
 export interface DiscardSuppliesForm {
 export interface DiscardSuppliesForm {
   quantity: number | null;
   quantity: number | null;
+  scrapReason: string;
+  scrapImage: string;
 }
 }
 
 
 export interface AddEmergencyItemForm
 export interface AddEmergencyItemForm
@@ -93,6 +95,8 @@ export interface ChangeRecordListRes {
   afterQuantity: number;
   afterQuantity: number;
   operatorName: string;
   operatorName: string;
   updatedAt: string;
   updatedAt: string;
+  scrapReason: string;
+  scrapImage?: string;
 }
 }
 
 
 export interface InventoryTaskListRes {
 export interface InventoryTaskListRes {
@@ -160,10 +164,16 @@ export interface SupplyRequestDetailList {
   deptName: string;
   deptName: string;
   /*数量 */
   /*数量 */
   quantity: number;
   quantity: number;
+  /*申请理由 */
+  requestReason: string;
   /*尺寸明细 */
   /*尺寸明细 */
   sizeDetail: string;
   sizeDetail: string;
-  /*状态:1-申请中 2-采购中 3-领用 4-已领用 */
+  /*状态:1-申请中 2-采购中 3-领用 4-已通知领用 。流程为:申请中-采购中-已通知领用-已领用*/
   status: number;
   status: number;
+  /* 通知领用时间 */
+  requestTime: string;
+  /* 通知领用地点 */
+  requestLocation: string;
   /*创建时间 */
   /*创建时间 */
   createdAt: string;
   createdAt: string;
   /*更新时间 */
   /*更新时间 */
@@ -197,3 +207,10 @@ export interface AddSupplyQuery {
   info: Partial<SupplyRequestDetailInfo>;
   info: Partial<SupplyRequestDetailInfo>;
   detailList: Partial<AddSupplyRequestSubForm>[];
   detailList: Partial<AddSupplyRequestSubForm>[];
 }
 }
+
+export interface NotifyDepartmentForm {
+  detailId: number; // 申领物资详情ID
+  noticeScope: number; // 通知范围: 1-通知此物资所有需求部门 2-仅通知此物资当前需求部门
+  requestTime: string; // 通知领用时间
+  requestLocation: string; // 通知领用地点
+}

+ 2 - 0
src/views/disaster/overview/components/DisasterCheckingLists.vue

@@ -244,6 +244,8 @@
         grid-row-gap: 5px;
         grid-row-gap: 5px;
         grid-column-gap: 5px;
         grid-column-gap: 5px;
         padding: 10px;
         padding: 10px;
+        overflow-x: hidden;
+        overflow-y: auto;
 
 
         .sum-item {
         .sum-item {
           display: flex;
           display: flex;

+ 18 - 3
src/views/emergency/emergency-drill/PageDrillPlanList.vue

@@ -49,7 +49,17 @@
                 text="演练执行"
                 text="演练执行"
                 @click="handleToExecute(scope.row.id)"
                 @click="handleToExecute(scope.row.id)"
               />
               />
-              <ActionButton v-else-if="scope.row.status < 7" text="演练记录" @click="handleToRecord(scope.row)" />
+              <ActionButton
+                v-else-if="scope.row.status < EMERGENCY_DRILL_STATUS.COMPLETE"
+                text="演练记录"
+                @click="handleToRecord(scope.row)"
+              />
+              <ActionButton
+                class="has-problem"
+                v-else-if="scope.row.status === EMERGENCY_DRILL_STATUS.HAS_PROBLEM"
+                text="演练问题"
+                @click="handleViewDrillPlan(scope.row.id, 'records')"
+              />
               <ActionButton
               <ActionButton
                 text="删除"
                 text="删除"
                 :popconfirm="{
                 :popconfirm="{
@@ -78,7 +88,7 @@
   import { DRILL_PLAN_LIST_SEARCH_CONFIG } from './configs/plan/search';
   import { DRILL_PLAN_LIST_SEARCH_CONFIG } from './configs/plan/search';
   import { TABLE_OPTIONS, DRILL_PLAN_LIST_TABLE_COLUMNS } from './configs/plan/table';
   import { TABLE_OPTIONS, DRILL_PLAN_LIST_TABLE_COLUMNS } from './configs/plan/table';
   import { DrillPlanListSearch, DrillPlanItem } from './types';
   import { DrillPlanListSearch, DrillPlanItem } from './types';
-  import { EMERGENCY_DRILL_STATUS_DICT } from './constants';
+  import { EMERGENCY_DRILL_STATUS, EMERGENCY_DRILL_STATUS_DICT } from './constants';
   import { useEmergencyDrillHook } from './hook';
   import { useEmergencyDrillHook } from './hook';
   import { QueryPageRequest } from '@/types/basic-query';
   import { QueryPageRequest } from '@/types/basic-query';
 
 
@@ -165,11 +175,12 @@
     });
     });
   }
   }
 
 
-  function handleViewDrillPlan(id: number) {
+  function handleViewDrillPlan(id: number, tab: string = 'activities') {
     router.push({
     router.push({
       name: 'emergency-drill-plan-view',
       name: 'emergency-drill-plan-view',
       query: {
       query: {
         id: id,
         id: id,
+        tab: tab,
       },
       },
     });
     });
   }
   }
@@ -200,4 +211,8 @@
   @use '@/styles/page-details-layout.scss' as *;
   @use '@/styles/page-details-layout.scss' as *;
   @use '@/styles/page-main-layout.scss' as *;
   @use '@/styles/page-main-layout.scss' as *;
   @use '@/styles/basic-table-action.scss' as *;
   @use '@/styles/basic-table-action.scss' as *;
+
+  .has-problem {
+    color: #ec2828;
+  }
 </style>
 </style>

+ 2 - 3
src/views/emergency/emergency-drill/PageDrillPlanView.vue

@@ -28,17 +28,16 @@
 <script setup lang="ts">
 <script setup lang="ts">
   import { ref, computed, defineAsyncComponent } from 'vue';
   import { ref, computed, defineAsyncComponent } from 'vue';
   import { ElMessage } from 'element-plus';
   import { ElMessage } from 'element-plus';
-  import { useRoute, useRouter } from 'vue-router';
+  import { useRoute } from 'vue-router';
   import UploadLoading from '@/components/UploadLoading.vue';
   import UploadLoading from '@/components/UploadLoading.vue';
   import { EMERGENCY_DRILL_DETAIL_SUBPAGE } from './constants';
   import { EMERGENCY_DRILL_DETAIL_SUBPAGE } from './constants';
   import { exportEmergencyDrillRecord } from '@/api/emergency-drill/emergency-drill';
   import { exportEmergencyDrillRecord } from '@/api/emergency-drill/emergency-drill';
   import { downloadFile } from '@/views/disaster/utils/download';
   import { downloadFile } from '@/views/disaster/utils/download';
 
 
   const formLoading = ref(false);
   const formLoading = ref(false);
-  const activeName = ref(EMERGENCY_DRILL_DETAIL_SUBPAGE[0].value);
-  const router = useRouter();
   const route = useRoute();
   const route = useRoute();
   const id = route.query.id;
   const id = route.query.id;
+  const activeName = ref(route.query.tab || EMERGENCY_DRILL_DETAIL_SUBPAGE[0].value);
 
 
   const dynamicComponent = computed(() => {
   const dynamicComponent = computed(() => {
     switch (activeName.value) {
     switch (activeName.value) {

+ 1 - 0
src/views/emergency/emergency-drill/constants.ts

@@ -7,6 +7,7 @@ export const EMERGENCY_DRILL_STATUS = {
   WAIT_CHECK: 5, // 记录待审批
   WAIT_CHECK: 5, // 记录待审批
   RETURN: 6, // 已退回
   RETURN: 6, // 已退回
   COMPLETE: 7, // 已完成
   COMPLETE: 7, // 已完成
+  HAS_PROBLEM: 8, // 已完成且有问题分析
 };
 };
 
 
 export const EMERGENCY_DRILL_STATUS_DICT = {
 export const EMERGENCY_DRILL_STATUS_DICT = {

+ 11 - 0
src/views/emergency/emergency-plan/src/components/AddManagementDetail.vue

@@ -29,6 +29,16 @@
           @change="handleChangeDept"
           @change="handleChangeDept"
         />
         />
       </template>
       </template>
+      <template #professionalTestSite>
+        <el-select v-model="ruleFormData.professionalTestSite" placeholder="请选择作业场所">
+          <el-option
+            v-for="item in PROFESSIONAL_TEST_SITE_DICE"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </template>
       <template #approvalTemplateId>
       <template #approvalTemplateId>
         <el-select
         <el-select
           v-model="ruleFormData.approvalTemplateId"
           v-model="ruleFormData.approvalTemplateId"
@@ -56,6 +66,7 @@
   import type { AddEmergencyPlanForm } from '@/types/emergency-plan';
   import type { AddEmergencyPlanForm } from '@/types/emergency-plan';
   import type { FileItem } from '@/components/UploadFiles/types.ts';
   import type { FileItem } from '@/components/UploadFiles/types.ts';
   import { PLAN_MANAGEMENT_FORM_CONFIG, PLAN_MANAGEMENT_FORM_DATA, PLAN_MANAGEMENT_FORM_RULES } from '../config';
   import { PLAN_MANAGEMENT_FORM_CONFIG, PLAN_MANAGEMENT_FORM_DATA, PLAN_MANAGEMENT_FORM_RULES } from '../config';
+  import { PROFESSIONAL_TEST_SITE_DICE } from '../constant';
 
 
   const formRef = ref();
   const formRef = ref();
   const cascaderRef = ref();
   const cascaderRef = ref();

+ 3 - 6
src/views/emergency/emergency-plan/src/config/form.ts

@@ -35,12 +35,9 @@ export const PLAN_MANAGEMENT_FORM_CONFIG: FormConfig[] = [
     slot: 'deptId',
     slot: 'deptId',
   },
   },
   {
   {
-    label: '试验专业场所:',
+    label: '业场所:',
     prop: 'professionalTestSite',
     prop: 'professionalTestSite',
-    component: 'ElInput',
-    componentProps: {
-      placeholder: '请输入试验专业场所',
-    },
+    slot: 'professionalTestSite',
   },
   },
   {
   {
     label: '审批流程:',
     label: '审批流程:',
@@ -88,7 +85,7 @@ export const PLAN_MANAGEMENT_FORM_CONFIG_EDIT: FormConfig[] = [
     },
     },
   },
   },
   {
   {
-    label: '试验专业场所:',
+    label: '业场所:',
     prop: 'professionalTestSite',
     prop: 'professionalTestSite',
     component: 'ElInput',
     component: 'ElInput',
     componentProps: {
     componentProps: {

+ 2 - 2
src/views/emergency/emergency-plan/src/config/search.ts

@@ -21,11 +21,11 @@ export const EMERGENCY_PLAN_MANAGEMENT_SEARCH_CONFIG: SearchConfig[] = [
     slot: 'eventType',
     slot: 'eventType',
   },
   },
   {
   {
-    label: '试验专业场所:',
+    label: '业场所:',
     prop: 'professionalTestSite',
     prop: 'professionalTestSite',
     component: 'ElInput',
     component: 'ElInput',
     componentProps: {
     componentProps: {
-      placeholder: '请输入试验专业场所',
+      placeholder: '请输入业场所',
     },
     },
   },
   },
   {
   {

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

@@ -65,7 +65,7 @@ export const EMERGENCY_PLAN_MANAGEMENT_TABLE_COLUMNS: TableColumnProps[] = [
   BASIC_TABLE_COLUMNS.ENENT_TYPE,
   BASIC_TABLE_COLUMNS.ENENT_TYPE,
   BASIC_TABLE_COLUMNS.DEPT_NAME,
   BASIC_TABLE_COLUMNS.DEPT_NAME,
   {
   {
-    label: '试验专业场所',
+    label: '业场所',
     prop: 'professionalTestSite',
     prop: 'professionalTestSite',
     minWidth: '160px',
     minWidth: '160px',
   },
   },

+ 37 - 2
src/views/emergency/emergency-plan/src/constant.ts

@@ -41,7 +41,7 @@ export const APPROVAL_TYPE_MAP = {
   [APPROVAL_TYPE.ORDINARY_SIGN]: '或签',
   [APPROVAL_TYPE.ORDINARY_SIGN]: '或签',
 };
 };
 
 
-export enum APPROVER_TYPE{
+export enum APPROVER_TYPE {
   FIX = 0,
   FIX = 0,
   CUSTOM,
   CUSTOM,
 }
 }
@@ -82,4 +82,39 @@ export const APPROVAL_STATUS_OPTIONS = [
     label: APPROVAL_STATUS_MAP[APPROVAL_STATUS.OHTER],
     label: APPROVAL_STATUS_MAP[APPROVAL_STATUS.OHTER],
     value: APPROVAL_STATUS.OHTER,
     value: APPROVAL_STATUS.OHTER,
   },
   },
-];
+];
+
+export const PROFESSIONAL_TEST_SITE_DICE = [
+  {
+    label: '仓储',
+    value: '仓储',
+  },
+  {
+    label: '住宿',
+    value: '住宿',
+  },
+  {
+    label: '办公',
+    value: '办公',
+  },
+  {
+    label: '服务',
+    value: '服务',
+  },
+  {
+    label: '试验',
+    value: '试验',
+  },
+  {
+    label: '文体活动',
+    value: '文体活动',
+  },
+  {
+    label: '施工',
+    value: '施工',
+  },
+  {
+    label: '其他',
+    value: '其他',
+  },
+];

+ 11 - 10
src/views/emergency/emergency-supplies/PageSupplyRequest.vue

@@ -42,20 +42,15 @@
           <template #action="scope">
           <template #action="scope">
             <div class="action-container--div">
             <div class="action-container--div">
               <ActionButton text="详情" @click="handleViewDetail(scope.row.id)" />
               <ActionButton text="详情" @click="handleViewDetail(scope.row.id)" />
-              <template v-if="scope.row.status === SUPPLY_REQUEST_STATUS.APPLYING">
-                <ActionButton
-                  text="发起采购"
-                  @click="handleStartPurchase(scope.row)"
-                  v-if="supplyRequestManagePermission && scope.row.totalPrice > 0"
-                />
-                <ActionButton text="编辑" @click="handleEdit(scope.row)" v-if="supplyRequestManagePermission" />
+              <template v-if="scope.row.status === SUPPLY_REQUEST_STATUS.APPLYING && supplyRequestManagePermission">
+                <ActionButton text="发起采购" @click="handleStartPurchase(scope.row)" />
+                <ActionButton text="编辑" @click="handleEdit(scope.row)" />
                 <ActionButton
                 <ActionButton
                   text="删除"
                   text="删除"
                   :popconfirm="{
                   :popconfirm="{
                     title: '确定删除?',
                     title: '确定删除?',
                   }"
                   }"
                   @confirm="handleDelete(scope.row.id)"
                   @confirm="handleDelete(scope.row.id)"
-                  v-if="supplyRequestManagePermission"
                 />
                 />
               </template>
               </template>
               <template
               <template
@@ -98,7 +93,7 @@
   import { SUPPLY_REQUEST_STATUS, SUPPLY_REQUEST_STATUS_MAP } from './src/constant';
   import { SUPPLY_REQUEST_STATUS, SUPPLY_REQUEST_STATUS_MAP } from './src/constant';
   import type { QueryPageRequest } from '@/types/basic-query';
   import type { QueryPageRequest } from '@/types/basic-query';
   import type { SupplyRequestListQuery, SupplyRequestListItem } from '@/types/emergency-supplier';
   import type { SupplyRequestListQuery, SupplyRequestListItem } from '@/types/emergency-supplier';
-  import { getSupplyRequestList, deleteSupplyRequest } from '@/api/emergency-supplier';
+  import { getSupplyRequestList, deleteSupplyRequest, getSupplyRequestDetail } from '@/api/emergency-supplier';
 
 
   const router = useRouter();
   const router = useRouter();
   const { permissions } = useUserInfoHook();
   const { permissions } = useUserInfoHook();
@@ -175,7 +170,13 @@
   };
   };
 
 
   // 发起采购
   // 发起采购
-  const handleStartPurchase = (row: SupplyRequestListItem) => {
+  const handleStartPurchase = async (row: SupplyRequestListItem) => {
+    if (!row.id) return;
+    const res = await getSupplyRequestDetail(row.id);
+    if (res.length === 0 || res.some((item) => item.detailList.length === 0)) {
+      ElMessage.error('此计划详情内没有物资,无法发起采购');
+      return;
+    }
     startPurchaseFormRef.value?.openDialog(row);
     startPurchaseFormRef.value?.openDialog(row);
   };
   };
 
 

+ 47 - 22
src/views/emergency/emergency-supplies/PageSupplyRequestDetail.vue

@@ -22,21 +22,33 @@
           <el-table :data="tableData" :span-method="handleSpanMethod" border v-loading="loading" style="width: 100%">
           <el-table :data="tableData" :span-method="handleSpanMethod" border v-loading="loading" style="width: 100%">
             <el-table-column type="index" label="序号" width="80" align="center" />
             <el-table-column type="index" label="序号" width="80" align="center" />
             <el-table-column prop="materialName" label="物资名称" min-width="140" align="center" />
             <el-table-column prop="materialName" label="物资名称" min-width="140" align="center" />
-            <el-table-column prop="specification" label="规格" min-width="200" align="center" />
-            <el-table-column prop="unitPrice" label="单价 (元)" width="120" align="center" />
-            <el-table-column prop="subtotal" label="小计" min-width="200" align="center" />
+            <el-table-column prop="specification" label="规格" min-width="100" align="center" />
             <el-table-column prop="department" label="需求部门" min-width="140" align="center" />
             <el-table-column prop="department" label="需求部门" min-width="140" align="center" />
             <el-table-column prop="quantity" label="数量" width="100" align="center" />
             <el-table-column prop="quantity" label="数量" width="100" align="center" />
+            <el-table-column prop="requestReason" label="申请理由" min-width="200" align="center" />
             <el-table-column prop="sizeDetails" label="尺寸明细" min-width="200" align="center" />
             <el-table-column prop="sizeDetails" label="尺寸明细" min-width="200" align="center" />
             <el-table-column prop="status" label="状态" width="120" align="center">
             <el-table-column prop="status" label="状态" width="120" align="center">
               <template #default="scope">
               <template #default="scope">
                 <span>{{ getStatusText(scope.row.status) }}</span>
                 <span>{{ getStatusText(scope.row.status) }}</span>
               </template>
               </template>
             </el-table-column>
             </el-table-column>
-            <el-table-column prop="claim" label="领用" width="100" align="center" v-if="supplyRequestManagePermission">
+            <el-table-column
+              prop="claim"
+              label="通知领用"
+              width="150"
+              align="center"
+              v-if="supplyRequestManagePermission"
+            >
               <template #default="scope">
               <template #default="scope">
                 <el-link
                 <el-link
-                  v-if="scope.row.status === SUPPLY_REQUEST_STATUS.PURCHASING && !scope.row.isClaimed"
+                  v-if="scope.row.status === SUPPLY_REQUEST_DETAIL_STATUS.PURCHASING"
+                  type="primary"
+                  @click="handleNotify(scope.row)"
+                >
+                  通知
+                </el-link>
+                <el-link
+                  v-else-if="scope.row.status === SUPPLY_REQUEST_DETAIL_STATUS.NOTIFIED"
                   type="primary"
                   type="primary"
                   @click="handleClaim(scope.row)"
                   @click="handleClaim(scope.row)"
                 >
                 >
@@ -56,21 +68,21 @@
               <template #default="scope">
               <template #default="scope">
                 <div class="action-container">
                 <div class="action-container">
                   <el-link
                   <el-link
-                    v-if="scope.row.status === SUPPLY_REQUEST_STATUS.APPLYING"
+                    v-if="scope.row.status === SUPPLY_REQUEST_DETAIL_STATUS.APPLYING"
                     type="primary"
                     type="primary"
                     @click="handleEdit(scope.row)"
                     @click="handleEdit(scope.row)"
                   >
                   >
                     编辑
                     编辑
                   </el-link>
                   </el-link>
                   <el-link
                   <el-link
-                    v-if="scope.row.status === SUPPLY_REQUEST_STATUS.APPLYING"
+                    v-if="scope.row.status === SUPPLY_REQUEST_DETAIL_STATUS.APPLYING"
                     type="primary"
                     type="primary"
                     style="margin-left: 8px"
                     style="margin-left: 8px"
                     @click="handleDelete(scope.row)"
                     @click="handleDelete(scope.row)"
                   >
                   >
                     删除
                     删除
                   </el-link>
                   </el-link>
-                  <span v-if="scope.row.status !== SUPPLY_REQUEST_STATUS.APPLYING">-</span>
+                  <span v-if="scope.row.status !== SUPPLY_REQUEST_DETAIL_STATUS.APPLYING">-</span>
                 </div>
                 </div>
               </template>
               </template>
             </el-table-column>
             </el-table-column>
@@ -78,11 +90,13 @@
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
-    <!-- 领用弹窗 -->
-    <ReceiveSupplyDialog ref="receiveSupplyDialogRef" @success="handleClaimSuccess" />
-    <!-- 添加物资弹窗 -->
-    <AddSuppliesDrawer ref="addSupplyDrawerRef" @success="handleAddMaterialSuccess" />
   </div>
   </div>
+  <!-- 领用弹窗 -->
+  <ReceiveSupplyDialog ref="receiveSupplyDialogRef" @success="handleClaimSuccess" />
+  <!-- 添加物资弹窗 -->
+  <AddSuppliesDrawer ref="addSupplyDrawerRef" @success="handleAddMaterialSuccess" />
+  <!-- 通知部门领用弹窗 -->
+  <NotifyDepartmentDialog ref="notifyDepartmentDialogRef" @success="handleNotifySuccess" />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
@@ -91,7 +105,11 @@
   import { Plus, Download } from '@element-plus/icons-vue';
   import { Plus, Download } from '@element-plus/icons-vue';
   import { ElMessage, ElMessageBox } from 'element-plus';
   import { ElMessage, ElMessageBox } from 'element-plus';
   import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
   import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
-  import { SUPPLY_REQUEST_STATUS, SUPPLY_REQUEST_DETAIL_STATUS_MAP } from './src/constant';
+  import {
+    SUPPLY_REQUEST_STATUS,
+    SUPPLY_REQUEST_DETAIL_STATUS,
+    SUPPLY_REQUEST_DETAIL_STATUS_MAP,
+  } from './src/constant';
   import {
   import {
     getSupplyRequestInfoById,
     getSupplyRequestInfoById,
     getSupplyRequestDetail,
     getSupplyRequestDetail,
@@ -105,6 +123,7 @@
   import { useUserInfoHook } from '@/hooks/useUserInfoHook';
   import { useUserInfoHook } from '@/hooks/useUserInfoHook';
   import { EMERGENCY_PERMISSIONS } from '@/views/emergency/src/constant';
   import { EMERGENCY_PERMISSIONS } from '@/views/emergency/src/constant';
   import AddSuppliesDrawer from './src/components/AddSuppliesDrawer.vue';
   import AddSuppliesDrawer from './src/components/AddSuppliesDrawer.vue';
+  import NotifyDepartmentDialog from './src/components/NotifyDepartmentDialog.vue';
 
 
   const { permissions } = useUserInfoHook();
   const { permissions } = useUserInfoHook();
   const supplyRequestManagePermission = ref<Boolean>(false);
   const supplyRequestManagePermission = ref<Boolean>(false);
@@ -116,13 +135,11 @@
     infoId: number; // info 的 id,用于合并单元格
     infoId: number; // info 的 id,用于合并单元格
     materialName: string; // info 的 supplyName
     materialName: string; // info 的 supplyName
     specification: string; // info 的 specs
     specification: string; // info 的 specs
-    unitPrice: number; // info 的 unitPrice
-    subtotal: string; // info 的 subtotal
     department: string; // detailList 的 deptName
     department: string; // detailList 的 deptName
     quantity: number; // detailList 的 quantity
     quantity: number; // detailList 的 quantity
+    requestReason: string; // detailList 的 requestReason
     sizeDetails: string; // detailList 的 sizeDetail
     sizeDetails: string; // detailList 的 sizeDetail
     status: number; // detailList 的 status
     status: number; // detailList 的 status
-    isClaimed?: boolean; // 是否已领用(根据状态判断)
   }
   }
 
 
   const route = useRoute();
   const route = useRoute();
@@ -134,7 +151,8 @@
   const tableData = ref<TableRowData[]>([]);
   const tableData = ref<TableRowData[]>([]);
   const addSupplyDrawerRef = ref<InstanceType<typeof AddSuppliesDrawer>>();
   const addSupplyDrawerRef = ref<InstanceType<typeof AddSuppliesDrawer>>();
   const supplyRequestDetailData = ref<SupplyRequestDetailItem[]>([]);
   const supplyRequestDetailData = ref<SupplyRequestDetailItem[]>([]);
-
+  // 通知部门领用弹窗相关
+  const notifyDepartmentDialogRef = ref<InstanceType<typeof NotifyDepartmentDialog>>();
   // 领用弹窗相关
   // 领用弹窗相关
   const receiveSupplyDialogRef = ref<InstanceType<typeof ReceiveSupplyDialog>>();
   const receiveSupplyDialogRef = ref<InstanceType<typeof ReceiveSupplyDialog>>();
 
 
@@ -145,8 +163,8 @@
 
 
   // 合并单元格方法
   // 合并单元格方法
   const handleSpanMethod = ({ row, rowIndex, columnIndex }: any) => {
   const handleSpanMethod = ({ row, rowIndex, columnIndex }: any) => {
-    // 需要合并的列索引:物资名称(1)、规格(2)、单价(3)、小计(4)、物资操作(10)
-    const mergeColumns = [1, 2, 3, 4, 10];
+    // 需要合并的列索引:物资名称(1)、规格(2)、物资操作(9)
+    const mergeColumns = [1, 2, 9];
     if (!mergeColumns.includes(columnIndex)) {
     if (!mergeColumns.includes(columnIndex)) {
       return { rowspan: 1, colspan: 1 };
       return { rowspan: 1, colspan: 1 };
     }
     }
@@ -190,13 +208,11 @@
             infoId: item.info.id, // 用于合并单元格
             infoId: item.info.id, // 用于合并单元格
             materialName: item.info.supplyName,
             materialName: item.info.supplyName,
             specification: item.info.specs,
             specification: item.info.specs,
-            unitPrice: item.info.unitPrice,
-            subtotal: item.info.subtotal,
             department: detail.deptName,
             department: detail.deptName,
             quantity: detail.quantity,
             quantity: detail.quantity,
+            requestReason: detail.requestReason,
             sizeDetails: detail.sizeDetail,
             sizeDetails: detail.sizeDetail,
             status: detail.status,
             status: detail.status,
-            isClaimed: detail.status === SUPPLY_REQUEST_STATUS.RECEIVED,
           });
           });
         });
         });
       });
       });
@@ -233,6 +249,15 @@
     }
     }
   };
   };
 
 
+  // 通知领用
+  const handleNotify = (row: TableRowData) => {
+    notifyDepartmentDialogRef.value?.openDialog(row.id);
+  };
+  // 通知领用成功回调
+  const handleNotifySuccess = async () => {
+    await getDetailData();
+  };
+
   // 领用
   // 领用
   const handleClaim = (row: TableRowData) => {
   const handleClaim = (row: TableRowData) => {
     receiveSupplyDialogRef.value?.openDialog(row.materialName, row.quantity, row.id, id);
     receiveSupplyDialogRef.value?.openDialog(row.materialName, row.quantity, row.id, id);

+ 7 - 29
src/views/emergency/emergency-supplies/src/components/AddSuppliesDrawer.vue

@@ -20,19 +20,6 @@
       <el-form-item label="规格:" prop="info.specs">
       <el-form-item label="规格:" prop="info.specs">
         <el-input v-model="formData.info.specs" placeholder="请输入规格" />
         <el-input v-model="formData.info.specs" placeholder="请输入规格" />
       </el-form-item>
       </el-form-item>
-      <el-form-item
-        label="单价(元):"
-        prop="info.unitPrice"
-        :rules="[
-          { required: true, message: '请输入单价', trigger: 'blur' },
-          { validator: validateFormNumber, trigger: 'blur' },
-        ]"
-      >
-        <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-form-item>
       <el-form-item label="需求详情:" v-if="deptTree">
       <el-form-item label="需求详情:" v-if="deptTree">
         <AddSuppliesSubForm
         <AddSuppliesSubForm
           ref="subFormRef"
           ref="subFormRef"
@@ -59,19 +46,18 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import { ElDrawer, ElForm, ElInput, ElSelect, ElMessage } from 'element-plus';
   import { ref, reactive, onMounted, useTemplateRef, computed } from 'vue';
   import { ref, reactive, onMounted, useTemplateRef, computed } from 'vue';
+  import { useRoute } from 'vue-router';
+  import { ElDrawer, ElForm, ElInput, ElSelect, ElMessage } from 'element-plus';
   import type {
   import type {
     AddSupplyRequestForm,
     AddSupplyRequestForm,
     AddSupplyRequestSubForm,
     AddSupplyRequestSubForm,
     SupplyRequestDetailItem,
     SupplyRequestDetailItem,
   } from '@/types/emergency-supplier';
   } from '@/types/emergency-supplier';
-  import { createSupplyRequestDetail, getSupplyNameList, updateSupplyRequestDetail } from '@/api/emergency-supplier';
-  import AddSuppliesSubForm from './AddSuppliesSubForm.vue';
-
   import type { DeptTree } from '@/types/dept/type';
   import type { DeptTree } from '@/types/dept/type';
+  import { createSupplyRequestDetail, getSupplyNameList, updateSupplyRequestDetail } from '@/api/emergency-supplier';
   import { getAllDepartments } from '@/api/auth/dept';
   import { getAllDepartments } from '@/api/auth/dept';
-  import { useRoute } from 'vue-router';
+  import AddSuppliesSubForm from './AddSuppliesSubForm.vue';
 
 
   const route = useRoute();
   const route = useRoute();
   const planId = Number(route.params.id);
   const planId = Number(route.params.id);
@@ -96,7 +82,7 @@
   const deptTree = ref<DeptTree[]>();
   const deptTree = ref<DeptTree[]>();
   const getDeptTreeData = async () => {
   const getDeptTreeData = async () => {
     const res = await getAllDepartments();
     const res = await getAllDepartments();
-    deptTree.value = res[0].children;
+    deptTree.value = res.flatMap((item) => item.children ?? []);
   };
   };
 
 
   // 表单数据
   // 表单数据
@@ -136,8 +122,6 @@
       id: editData.info.id,
       id: editData.info.id,
       supplyName: editData.info.supplyName,
       supplyName: editData.info.supplyName,
       specs: editData.info.specs,
       specs: editData.info.specs,
-      unitPrice: editData.info.unitPrice,
-      subtotal: editData.info.subtotal,
     };
     };
 
 
     // 设置 detailList 数据(编辑时不需要保留 id,使用负数作为临时 id)
     // 设置 detailList 数据(编辑时不需要保留 id,使用负数作为临时 id)
@@ -147,6 +131,7 @@
         deptId: detail.deptId,
         deptId: detail.deptId,
         deptName: detail.deptName,
         deptName: detail.deptName,
         quantity: detail.quantity,
         quantity: detail.quantity,
+        requestReason: detail.requestReason,
         sizeDetail: detail.sizeDetail,
         sizeDetail: detail.sizeDetail,
       };
       };
     });
     });
@@ -223,6 +208,7 @@
           deptId: item.deptId,
           deptId: item.deptId,
           deptName: item.deptName,
           deptName: item.deptName,
           quantity: item.quantity,
           quantity: item.quantity,
+          requestReason: item.requestReason,
           sizeDetail: item.sizeDetail,
           sizeDetail: item.sizeDetail,
         };
         };
       }),
       }),
@@ -257,14 +243,6 @@
     }
     }
   };
   };
 
 
-  const validateFormNumber = (rule: any, value: number, callback: any) => {
-    if (value < 0) {
-      callback(new Error('输入不能小于0'));
-    } else {
-      callback();
-    }
-  };
-
   onMounted(() => {
   onMounted(() => {
     getSuppliesNameList();
     getSuppliesNameList();
     getDeptTreeData();
     getDeptTreeData();

+ 11 - 0
src/views/emergency/emergency-supplies/src/components/AddSuppliesSubForm.vue

@@ -37,6 +37,17 @@
           @change="emits('updateSubForm', subFormData)"
           @change="emits('updateSubForm', subFormData)"
         />
         />
       </el-form-item>
       </el-form-item>
+      <el-form-item
+        label="申请理由:"
+        prop="requestReason"
+        :rules="[{ required: true, message: '请输入申请理由', trigger: 'blur' }]"
+      >
+        <el-input
+          v-model="subFormData.requestReason"
+          placeholder="请输入申请理由"
+          @change="emits('updateSubForm', subFormData)"
+        />
+      </el-form-item>
       <el-form-item label="尺寸明细:" prop="sizeDetail">
       <el-form-item label="尺寸明细:" prop="sizeDetail">
         <el-input
         <el-input
           v-model="subFormData.sizeDetail"
           v-model="subFormData.sizeDetail"

+ 56 - 0
src/views/emergency/emergency-supplies/src/components/ChangeRecord.vue

@@ -8,7 +8,33 @@
         <span v-if="scope.row.changeQuantity > 0">+</span>
         <span v-if="scope.row.changeQuantity > 0">+</span>
         <span>{{ scope.row.changeQuantity }}</span>
         <span>{{ scope.row.changeQuantity }}</span>
       </template>
       </template>
+      <template #action="scope">
+        <ActionButton text="报废详情" @click="handleViewDetail(scope.row)" />
+      </template>
     </BasicTable>
     </BasicTable>
+    <el-drawer title="报废详情" v-model="drawerVisible" size="40%" :close-on-click-modal="true" :show-close="true">
+      <div class="drawer-body">
+        <div class="drawer-item">
+          <span class="drawer-item-label">报废理由:</span>
+          <span>{{ record?.scrapReason }}</span>
+        </div>
+        <div v-if="uploadedImages.length > 0" class="drawer-item">
+          <span class="drawer-item-label">报废照片:</span>
+          <div class="image-container">
+            <div v-for="(image, index) in uploadedImages" :key="image" class="image-preview">
+              <el-image
+                :src="image"
+                style="width: 100px; height: 100px"
+                :preview-src-list="uploadedImages"
+                show-progress
+                :initial-index="index"
+                fit="cover"
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+    </el-drawer>
   </div>
   </div>
 </template>
 </template>
 
 
@@ -20,6 +46,8 @@
   import { ChangeRecordListRes } from '@/types/emergency-supplier';
   import { ChangeRecordListRes } from '@/types/emergency-supplier';
   import { getSupplyChangeRecord } from '@/api/emergency-supplier';
   import { getSupplyChangeRecord } from '@/api/emergency-supplier';
   import { CHANGE_TYPE_MAP } from '../constant';
   import { CHANGE_TYPE_MAP } from '../constant';
+  import ActionButton from '@/components/ActionButton.vue';
+  import { unformatImage } from '@/components/UploadImages/utils';
 
 
   const { tableConfig } = useTableConfig(CHANGE_RECORD_TABLE_COLUMNS, CHANGE_RECORD_TABLE_OPTIONS, false);
   const { tableConfig } = useTableConfig(CHANGE_RECORD_TABLE_COLUMNS, CHANGE_RECORD_TABLE_OPTIONS, false);
   const props = defineProps<{
   const props = defineProps<{
@@ -32,6 +60,17 @@
     tableData.value = res;
     tableData.value = res;
     tableConfig.loading = false;
     tableConfig.loading = false;
   };
   };
+
+  const uploadedImages = ref<string[]>([]);
+
+  const drawerVisible = ref(false);
+  const record = ref<ChangeRecordListRes>();
+  const handleViewDetail = (row: ChangeRecordListRes) => {
+    record.value = row;
+    uploadedImages.value = unformatImage(row.scrapImage) || [];
+
+    drawerVisible.value = true;
+  };
   onMounted(() => {
   onMounted(() => {
     getChangeRecord();
     getChangeRecord();
   });
   });
@@ -39,4 +78,21 @@
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
   @use '../styles/info.scss' as *;
   @use '../styles/info.scss' as *;
+  .drawer-item {
+    margin-bottom: 20px;
+  }
+  .drawer-item-label {
+    color: rgba(0, 0, 0, 0.88);
+  }
+  .image-container {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 5px;
+    width: 100%;
+  }
+  .image-preview {
+    width: 100px;
+    height: 100px;
+    border-radius: 4px;
+  }
 </style>
 </style>

+ 35 - 9
src/views/emergency/emergency-supplies/src/components/DiscardSupplies.vue

@@ -11,9 +11,16 @@
         <template #quantity>
         <template #quantity>
           <el-input v-model.number="ruleFormData.quantity" placeholder="请输入报废数量" type="number" min="1" step="1">
           <el-input v-model.number="ruleFormData.quantity" placeholder="请输入报废数量" type="number" min="1" step="1">
           </el-input>
           </el-input>
+          <div>最多可报废数量:{{ supply?.currentQuantity }}</div>
+        </template>
+        <template #scrapReason>
+          <el-input v-model="ruleFormData.scrapReason" placeholder="请输入报废理由" type="textarea" rows="3">
+          </el-input>
+        </template>
+        <template #scrapImage>
+          <UploadImages :maxCount="9" ref="uploadImagesRef" @uploadSuccess="handleUploadSuccess" />
         </template>
         </template>
       </BasicForm>
       </BasicForm>
-      <div class="discard-note">最多可报废数量:{{ supply?.currentQuantity }}</div>
     </template>
     </template>
     <template #footer>
     <template #footer>
       <el-button type="primary" @click="handleSumbit">提交</el-button>
       <el-button type="primary" @click="handleSumbit">提交</el-button>
@@ -27,10 +34,17 @@
   import { ElMessage } from 'element-plus';
   import { ElMessage } from 'element-plus';
   import BasicDialog from '@/components/BasicDialog.vue';
   import BasicDialog from '@/components/BasicDialog.vue';
   import BasicForm from '@/components/BasicForm.vue';
   import BasicForm from '@/components/BasicForm.vue';
+  import UploadImages from '@/components/UploadImages/UploadImages.vue';
   import { useFormConfigHook } from '@/hooks/useFormConfigHook';
   import { useFormConfigHook } from '@/hooks/useFormConfigHook';
   import { DiscardSuppliesForm, EmergencySupplyListResponse } from '@/types/emergency-supplier';
   import { DiscardSuppliesForm, EmergencySupplyListResponse } from '@/types/emergency-supplier';
   import { discardEmergencySupply } from '@/api/emergency-supplier';
   import { discardEmergencySupply } from '@/api/emergency-supplier';
-  import { SUPPLIES_DISCARD_FROM_CONFIG, SUPPLIES_DISCARD_FROM_DATA, SUPPLIES_DISCARD_FROM_RULES } from '../config';
+  import {
+    SUPPLIES_DISCARD_FROM_CONFIG,
+    SUPPLIES_DISCARD_FROM_DATA,
+    SUPPLIES_DISCARD_FROM_RULES,
+  } from '../config/form';
+  import { formatImageList } from '@/components/UploadImages/utils';
+  import type { ImageItem } from '@/types/disaster-control';
 
 
   const emit = defineEmits<{
   const emit = defineEmits<{
     (e: 'refreshList'): void;
     (e: 'refreshList'): void;
@@ -38,6 +52,7 @@
 
 
   const basicDialogRef = ref<InstanceType<typeof BasicDialog>>();
   const basicDialogRef = ref<InstanceType<typeof BasicDialog>>();
   const basicFormRef = ref<InstanceType<typeof BasicForm>>();
   const basicFormRef = ref<InstanceType<typeof BasicForm>>();
+  const uploadImagesRef = ref<InstanceType<typeof UploadImages>>();
   const { ruleFormConfig, ruleFormData, formRules } = useFormConfigHook<DiscardSuppliesForm>(
   const { ruleFormConfig, ruleFormData, formRules } = useFormConfigHook<DiscardSuppliesForm>(
     SUPPLIES_DISCARD_FROM_CONFIG,
     SUPPLIES_DISCARD_FROM_CONFIG,
     SUPPLIES_DISCARD_FROM_DATA,
     SUPPLIES_DISCARD_FROM_DATA,
@@ -49,6 +64,7 @@
   const openDialog = (item: EmergencySupplyListResponse) => {
   const openDialog = (item: EmergencySupplyListResponse) => {
     supply.value = item;
     supply.value = item;
     basicDialogRef.value?.openDialog();
     basicDialogRef.value?.openDialog();
+    uploadImagesRef.value?.removeAllImages();
     (formRules.quantity as Array<any>).push({
     (formRules.quantity as Array<any>).push({
       validator: (_rule, value, callback) => {
       validator: (_rule, value, callback) => {
         if (!supply.value) {
         if (!supply.value) {
@@ -68,12 +84,28 @@
       trigger: 'blur',
       trigger: 'blur',
     });
     });
   };
   };
+
+  const scrapImage = ref<ImageItem[]>([]);
+  const handleUploadSuccess = (files: ImageItem[]) => {
+    scrapImage.value = files;
+  };
+
   const handleSumbit = async () => {
   const handleSumbit = async () => {
     const validate = await basicFormRef.value?.validateForm();
     const validate = await basicFormRef.value?.validateForm();
     if (!validate) return;
     if (!validate) return;
     if (!supply.value) return;
     if (!supply.value) return;
     try {
     try {
-      await discardEmergencySupply(supply.value.id, ruleFormData.quantity!);
+      const scrapImageString = await formatImageList(scrapImage.value);
+      if (scrapImageString) {
+        ruleFormData.scrapImage = JSON.stringify(scrapImageString as string[]);
+      }
+      const query = {
+        supplyId: supply.value.id,
+        quantity: ruleFormData.quantity as number,
+        scrapReason: ruleFormData.scrapReason,
+        scrapImage: ruleFormData.scrapImage,
+      };
+      await discardEmergencySupply(query);
       ElMessage.success('物资报废成功');
       ElMessage.success('物资报废成功');
       emit('refreshList');
       emit('refreshList');
       basicDialogRef.value?.closeDialog();
       basicDialogRef.value?.closeDialog();
@@ -88,9 +120,3 @@
     openDialog,
     openDialog,
   });
   });
 </script>
 </script>
-
-<style scoped lang="scss">
-  .discard-note {
-    padding-left: 80px;
-  }
-</style>

+ 95 - 0
src/views/emergency/emergency-supplies/src/components/NotifyDepartmentDialog.vue

@@ -0,0 +1,95 @@
+<template>
+  <BasicDialog ref="basicDialogRef" title="物资领用通知" @refresh="refreshFormData">
+    <template #form>
+      <BasicForm ref="basicFormRef" :formData="ruleFormData" :formRules="formRules" :formConfig="ruleFormConfig">
+        <template #noticeScope>
+          <el-radio-group v-model="ruleFormData.noticeScope">
+            <el-radio v-for="item in notifyRangeOptions" :key="item.value" :label="item.value">{{
+              item.label
+            }}</el-radio>
+          </el-radio-group>
+        </template>
+      </BasicForm>
+    </template>
+    <template #footer>
+      <el-button @click="basicDialogRef?.closeDialog">取消</el-button>
+      <el-button type="primary" @click="handleSubmit">提交</el-button>
+    </template>
+  </BasicDialog>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { ElMessage, ElRadioGroup, ElRadio } from 'element-plus';
+  import BasicDialog from '@/components/BasicDialog.vue';
+  import BasicForm from '@/components/BasicForm.vue';
+  import { useFormConfigHook } from '@/hooks/useFormConfigHook';
+  import { NOTIFY_DEPARTMENT_FORM_CONFIG, NOTIFY_DEPARTMENT_FORM_DATA, NOTIFY_DEPARTMENT_FORM_RULES } from '../config';
+  import { NOTIFY_RANGE, NOTIFY_RANGE_OPTIONS } from '../constant';
+  import { notifySupplyRequestDetail } from '@/api/emergency-supplier';
+
+  const emits = defineEmits<{
+    (e: 'success'): void;
+  }>();
+
+  const basicDialogRef = ref<InstanceType<typeof BasicDialog>>();
+  const basicFormRef = ref<InstanceType<typeof BasicForm>>();
+  const tmpDetailId = ref<number | undefined>(undefined);
+
+  const { ruleFormConfig, ruleFormData, formRules } = useFormConfigHook(
+    NOTIFY_DEPARTMENT_FORM_CONFIG,
+    NOTIFY_DEPARTMENT_FORM_DATA,
+    NOTIFY_DEPARTMENT_FORM_RULES,
+  );
+
+  const notifyRangeOptions = ref<{ label: string; value: number }[]>(NOTIFY_RANGE_OPTIONS);
+
+  // 打开对话框
+  const openDialog = (id: number) => {
+    tmpDetailId.value = id;
+    basicDialogRef.value?.openDialog();
+  };
+
+  // 提交表单
+  const handleSubmit = async () => {
+    const validate = await basicFormRef.value?.validateForm();
+    if (!validate) return;
+
+    if (!tmpDetailId.value) {
+      ElMessage.error('缺少必要参数');
+      return;
+    }
+
+    try {
+      await notifySupplyRequestDetail({
+        detailId: tmpDetailId.value,
+        noticeScope: ruleFormData.noticeScope,
+        requestTime: ruleFormData.requestTime,
+        requestLocation: ruleFormData.requestLocation,
+      });
+      ElMessage.success('通知成功');
+      basicDialogRef.value?.closeDialog();
+      // 清理数据
+      tmpDetailId.value = undefined;
+      ruleFormData.noticeScope = NOTIFY_RANGE.ALL;
+      ruleFormData.requestTime = '';
+      ruleFormData.requestLocation = '';
+      // 触发父组件刷新列表
+      emits('success');
+    } catch (error) {
+      console.error('通知失败:', error);
+      ElMessage.error('通知失败');
+    }
+  };
+
+  // 刷新表单数据(在对话框打开时调用,用于清除验证状态)
+  const refreshFormData = () => {
+    basicFormRef.value?.clearValidate();
+  };
+
+  defineExpose({
+    openDialog,
+  });
+</script>
+
+<style scoped lang="scss"></style>

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

@@ -4,8 +4,8 @@
       <BasicForm ref="basicFormRef" :formData="ruleFormData" :formRules="formRules" :formConfig="ruleFormConfig" />
       <BasicForm ref="basicFormRef" :formData="ruleFormData" :formRules="formRules" :formConfig="ruleFormConfig" />
     </template>
     </template>
     <template #footer>
     <template #footer>
-      <el-button type="primary" @click="handleSubmit">提交</el-button>
       <el-button @click="basicDialogRef?.closeDialog">取消</el-button>
       <el-button @click="basicDialogRef?.closeDialog">取消</el-button>
+      <el-button type="primary" @click="handleSubmit">提交</el-button>
     </template>
     </template>
   </BasicDialog>
   </BasicDialog>
 </template>
 </template>

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

@@ -4,8 +4,8 @@
       <BasicForm ref="basicFormRef" :formData="ruleFormData" :formRules="formRules" :formConfig="ruleFormConfig" />
       <BasicForm ref="basicFormRef" :formData="ruleFormData" :formRules="formRules" :formConfig="ruleFormConfig" />
     </template>
     </template>
     <template #footer>
     <template #footer>
-      <el-button type="primary" @click="handleSubmit">提交</el-button>
       <el-button @click="basicDialogRef?.closeDialog">取消</el-button>
       <el-button @click="basicDialogRef?.closeDialog">取消</el-button>
+      <el-button type="primary" @click="handleSubmit">提交</el-button>
     </template>
     </template>
   </BasicDialog>
   </BasicDialog>
 </template>
 </template>

+ 56 - 0
src/views/emergency/emergency-supplies/src/config/form.ts

@@ -3,6 +3,7 @@
  */
  */
 import type { FormConfig } from '@/types/basic-form';
 import type { FormConfig } from '@/types/basic-form';
 import { validateFormTime } from '@/utils/validateFormTime';
 import { validateFormTime } from '@/utils/validateFormTime';
+import { NOTIFY_RANGE } from '../constant';
 
 
 // 盘点任务表单信息
 // 盘点任务表单信息
 export const INVENTORY_TASK_FROM_CONFIG: FormConfig[] = [
 export const INVENTORY_TASK_FROM_CONFIG: FormConfig[] = [
@@ -316,16 +317,29 @@ export const SUPPLIES_DISCARD_FROM_CONFIG: FormConfig[] = [
     prop: 'quantity',
     prop: 'quantity',
     slot: 'quantity',
     slot: 'quantity',
   },
   },
+  {
+    label: '报废理由:',
+    prop: 'scrapReason',
+    slot: 'scrapReason',
+  },
+  {
+    label: '报废照片:',
+    prop: 'scrapImage',
+    slot: 'scrapImage',
+  },
 ];
 ];
 
 
 // 物资报废单数据
 // 物资报废单数据
 export const SUPPLIES_DISCARD_FROM_DATA = {
 export const SUPPLIES_DISCARD_FROM_DATA = {
   quantity: null,
   quantity: null,
+  scrapReason: '',
+  scrapImage: '',
 };
 };
 
 
 // 物资报废单规则
 // 物资报废单规则
 export const SUPPLIES_DISCARD_FROM_RULES = {
 export const SUPPLIES_DISCARD_FROM_RULES = {
   quantity: [{ required: true, message: '请输入报废数量', trigger: 'blur' }],
   quantity: [{ required: true, message: '请输入报废数量', trigger: 'blur' }],
+  scrapReason: [{ required: true, message: '请输入报废理由', trigger: 'blur' }],
 };
 };
 
 
 // 物资申领计划表单配置
 // 物资申领计划表单配置
@@ -386,3 +400,45 @@ export const START_PURCHASE_FORM_DATA = {
 export const START_PURCHASE_FORM_RULES = {
 export const START_PURCHASE_FORM_RULES = {
   purchaseDate: [{ required: true, message: '请选择采购日期', trigger: 'change' }],
   purchaseDate: [{ required: true, message: '请选择采购日期', trigger: 'change' }],
 };
 };
+
+// 物资领用通知表单配置
+export const NOTIFY_DEPARTMENT_FORM_CONFIG: FormConfig[] = [
+  {
+    label: '通知范围:',
+    prop: 'noticeScope',
+    slot: 'noticeScope',
+  },
+  {
+    label: '领用时间:',
+    prop: 'requestTime',
+    component: 'ElDatePicker',
+    componentProps: {
+      placeholder: '请选择领用时间',
+      type: 'date',
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+    },
+  },
+  {
+    label: '领用地点:',
+    prop: 'requestLocation',
+    component: 'ElInput',
+    componentProps: {
+      placeholder: '请输入领用地点',
+    },
+  },
+];
+
+// 物资领用通知表单数据
+export const NOTIFY_DEPARTMENT_FORM_DATA = {
+  noticeScope: NOTIFY_RANGE.ALL,
+  requestTime: '',
+  requestLocation: '',
+};
+
+// 物资领用通知表单规则
+export const NOTIFY_DEPARTMENT_FORM_RULES = {
+  noticeScope: [{ required: true, message: '请选择通知范围', trigger: 'change' }],
+  requestTime: [{ required: true, message: '请选择领用时间', trigger: 'change' }],
+  requestLocation: [{ required: true, message: '请输入领用地点', trigger: 'blur' }],
+};

+ 6 - 0
src/views/emergency/emergency-supplies/src/config/index.ts

@@ -39,6 +39,9 @@ import {
   START_PURCHASE_FORM_CONFIG,
   START_PURCHASE_FORM_CONFIG,
   START_PURCHASE_FORM_DATA,
   START_PURCHASE_FORM_DATA,
   START_PURCHASE_FORM_RULES,
   START_PURCHASE_FORM_RULES,
+  NOTIFY_DEPARTMENT_FORM_CONFIG,
+  NOTIFY_DEPARTMENT_FORM_DATA,
+  NOTIFY_DEPARTMENT_FORM_RULES,
 } from './form';
 } from './form';
 
 
 export {
 export {
@@ -82,4 +85,7 @@ export {
   START_PURCHASE_FORM_CONFIG,
   START_PURCHASE_FORM_CONFIG,
   START_PURCHASE_FORM_DATA,
   START_PURCHASE_FORM_DATA,
   START_PURCHASE_FORM_RULES,
   START_PURCHASE_FORM_RULES,
+  NOTIFY_DEPARTMENT_FORM_CONFIG,
+  NOTIFY_DEPARTMENT_FORM_DATA,
+  NOTIFY_DEPARTMENT_FORM_RULES,
 };
 };

+ 8 - 5
src/views/emergency/emergency-supplies/src/config/table.ts

@@ -150,6 +150,14 @@ export const CHANGE_RECORD_TABLE_COLUMNS: TableColumnProps[] = [
     label: '变更时间',
     label: '变更时间',
     prop: 'updatedAt',
     prop: 'updatedAt',
   },
   },
+  {
+    label: '操作',
+    prop: 'action',
+    slot: 'action',
+    align: 'center',
+    fixed: 'right',
+    width: '120px',
+  },
 ];
 ];
 
 
 export const CHANGE_RECORD_TABLE_OPTIONS = {
 export const CHANGE_RECORD_TABLE_OPTIONS = {
@@ -260,11 +268,6 @@ export const SUPPLY_REQUEST_TABLE_COLUMNS: TableColumnProps[] = [
     prop: 'purchaseDate',
     prop: 'purchaseDate',
     width: '180px',
     width: '180px',
   },
   },
-  {
-    label: '总价(元)',
-    prop: 'totalPrice',
-    minWidth: '140px',
-  },
   {
   {
     label: '状态',
     label: '状态',
     prop: 'status',
     prop: 'status',

+ 20 - 0
src/views/emergency/emergency-supplies/src/constant/index.ts

@@ -97,10 +97,30 @@ export enum SUPPLY_REQUEST_DETAIL_STATUS {
   APPLYING = 1, // 申请中
   APPLYING = 1, // 申请中
   PURCHASING = 2, // 采购中
   PURCHASING = 2, // 采购中
   RECEIVED = 3, // 已领用
   RECEIVED = 3, // 已领用
+  NOTIFIED = 4, // 已通知领用
 }
 }
 
 
 export const SUPPLY_REQUEST_DETAIL_STATUS_MAP = {
 export const SUPPLY_REQUEST_DETAIL_STATUS_MAP = {
   [SUPPLY_REQUEST_DETAIL_STATUS.APPLYING]: '申请中',
   [SUPPLY_REQUEST_DETAIL_STATUS.APPLYING]: '申请中',
   [SUPPLY_REQUEST_DETAIL_STATUS.PURCHASING]: '采购中',
   [SUPPLY_REQUEST_DETAIL_STATUS.PURCHASING]: '采购中',
   [SUPPLY_REQUEST_DETAIL_STATUS.RECEIVED]: '已领用',
   [SUPPLY_REQUEST_DETAIL_STATUS.RECEIVED]: '已领用',
+  [SUPPLY_REQUEST_DETAIL_STATUS.NOTIFIED]: '已通知领用',
 };
 };
+
+/**
+ * 物资领用通知范围
+ */
+export enum NOTIFY_RANGE {
+  ALL = 1,
+  CURRENT = 2,
+}
+
+export const NOTIFY_RANGE_MAP = {
+  [NOTIFY_RANGE.ALL]: '通知此物资所有需求部门',
+  [NOTIFY_RANGE.CURRENT]: '仅通知此物资当前需求部门',
+};
+
+export const NOTIFY_RANGE_OPTIONS = [
+  { label: NOTIFY_RANGE_MAP[NOTIFY_RANGE.ALL], value: NOTIFY_RANGE.ALL },
+  { label: NOTIFY_RANGE_MAP[NOTIFY_RANGE.CURRENT], value: NOTIFY_RANGE.CURRENT },
+];