Browse Source

feat: overview of inspection plan page

Signed-off-by: Carlos <568187512@qq.com>
Carlos 1 week ago
parent
commit
471da9c6cc

+ 16 - 18
src/app/pages/manager/hazard/inspection-plan/inspection-plan.component.html

@@ -27,7 +27,7 @@
             }
           </div>
           <div class="text">
-            {{ semiAnnualText }}
+            {{ semiAnnual?.label || '' }}
           </div>
           <div class="arrow" (click)="handleSemiAnnualChange('right')">
             @if (rightArrowVisible) {
@@ -40,36 +40,34 @@
           </div>
         </div>
         <div class="pb-2">
-          @for (row of firstSection; track $index) {
-            <div class="plan-type" [class.active]="currentPlanKey === row.key" (click)="handlePlanTypeChange(row.key)">
+          @for (row of types; track $index) {
+            <div class="plan-type" [class.active]="currentType === row.key" (click)="handlePlanTypeChange(row.key)">
               <span class="section-label">{{ row.label }}:</span>
-              <span class="unit-number">{{ row.value + '起' }}</span>
+              <span class="unit-number">{{ row.count + '起' }}</span>
             </div>
           }
         </div>
       </div>
       <div class="flex flex-1">
-        <div class="flex flex-col justify-between py-4 w-[393px] pl-[6%] pr-[4%]">
+        <!-- <div class="flex flex-col justify-between py-4 w-[393px] pl-[6%] pr-[4%]">
           @for (row of secondSection; track $index) {
             <div class="flex items-center h-1/3 justify-between">
               <span class="section-label">{{ row.label }}:</span>
               <span class="link-number text-left flex-1 pl-[20%]">{{ row.value + '%' }}</span>
             </div>
           }
-        </div>
-        <div class="flex flex-col py-6 w-[456px] pr-[8%] pl-[2%]">
-          <div class="flex justify-between">
-            <div class="section-label">排查中发现隐患:</div>
-            <div class="flex justify-between text-3 font-medium w-[40%]">
-              <div>
-                <div class="pb-4">重大:{{ discovery.significant }}</div>
-                <div>A类:{{ discovery.a }}</div>
-              </div>
-              <div>
-                <div class="pb-4">B类:{{ discovery.b }}</div>
-                <div>C类:{{ discovery.c }}</div>
+        </div> -->
+
+        <div class="flex pt-8 pr-24 pl-[2%]">
+          <div class="section-label pt-2 mr-8">排查中发现隐患:</div>
+          <div class="grid grid-cols-2 gap-x-12 text-3 relative -top-2">
+            @for (item of discoveryList; track item.level) {
+              <div class="pb-3 text-right">
+                <span>{{ item.level }}隐患</span>
+                <span class="text-3xl ml-1 font-alibaba-heavy" [style.color]="item.color">{{ item.count }}</span>
+                <span class="text-xs">起</span>
               </div>
-            </div>
+            }
           </div>
         </div>
         <div class="flex-1"></div>

+ 2 - 0
src/app/pages/manager/hazard/inspection-plan/inspection-plan.component.less

@@ -76,6 +76,8 @@
   }
   .arrow {
     min-width: 24px;
+    min-height: 24px;
+    cursor: pointer;
     user-select: none;
   }
 }

+ 123 - 73
src/app/pages/manager/hazard/inspection-plan/inspection-plan.component.ts

@@ -2,11 +2,12 @@ import { Component } from '@angular/core';
 import { horizontalInOutReverse300ms } from '../../../../common.animation';
 import { CommonNzModule } from '../../../../common.nz.module';
 import { DataEmptyComponent } from '../../../../shared/data-empty/data-empty.component';
+import { hazardLevelColor } from '../hazard.utils';
 import { ApiService } from './../../../../services/api.service';
 import { SettingService } from './../../../../services/setting.service';
 import { CustomDrawerComponent } from './../../../../shared/custom-drawer/custom-drawer.component';
 import { InspectionPlanDetailComponent } from './detail/detail.component';
-import { cycleOptions, hazardPlanTypeOptions, planLevelOptions } from './inspection-plan.utils';
+import { hazardPlanTypeOptions, PlanType } from './inspection-plan.utils';
 import { PlanCardComponent } from './plan-card/plan-card.component';
 import { PlanFormComponent, PlanFormComponentProps } from './plan-form/plan-form.component';
 
@@ -15,39 +16,13 @@ export interface InspectionPlanDetailConfig {
   data: Hazard.InspectionPlanDetail;
 }
 
+type SemiAnnual = { label: string; start: Date; end: Date; key: number };
+
 const notificationConfig = [
   { type: 1, text: '通知', color: '#165DFF', backgroundColor: '#E8F3FF' },
   { type: 2, text: '消息', color: '#0FC6C2', backgroundColor: '#E8FFFB' },
 ];
 
-// const departments = Array.from({ length: 15 }, (_, i) => i + 1);
-const getRandomSizeN = (max: number) => Math.floor(Math.random() * max);
-const getRandomSize = (arr: Option<string | number>[]) => arr[Math.floor(Math.random() * arr.length)];
-
-const getMockPlan = (index: number, name: string): Hazard.InspectionPlan => {
-  return {
-    id: index,
-    type: getRandomSize(hazardPlanTypeOptions).value as string,
-    title: `${name}隐患排查计划`,
-    status: '',
-    startDate: parseInt(name) + '-11-12T00:00:00',
-    endDate: parseInt(name) + '-11-14T00:00:00',
-    createdDate: parseInt(name) + '-11-10T00:00:00',
-    departments: [getRandomSizeN(13) + 1],
-    createUserId: '123',
-    createUserName: '张三',
-    createUserDept: 1,
-    targets: [323, 346, 349, 16, 12, 8, 5, 4],
-    level: getRandomSize(planLevelOptions).value as string,
-    cycle: getRandomSize(cycleOptions).value as string,
-    frequency: getRandomSizeN(4) || 1,
-    frequencyTimes: getRandomSizeN(4) || 1,
-    // time: [],
-    content: '',
-    isUnited: false,
-  };
-};
-
 @Component({
   selector: 'app-inspection-plan',
   standalone: true,
@@ -73,29 +48,44 @@ export class InspectionPlanComponent {
     data: undefined,
     isEdit: false,
   };
-  semiAnnualOptions = [
-    { label: '2023上半年度', value: 202301 },
-    { label: '2023下半年度', value: 202306 },
-    { label: '2024上半年度', value: 202401 },
-    { label: '2024下半年度', value: 202406 },
-    { label: '2025上半年度', value: 202501 },
-  ];
-  firstSection = [
-    { label: '日常隐患排查计划', value: 3, key: 1 },
-    { label: '专项隐患排查计划', value: 6, key: 2 },
-    { label: '其他隐患排查计划', value: 9, key: 3 },
-  ];
-  secondSection = [
-    { label: '计划站点排查覆盖', value: 65 },
-    { label: '其他场所排查覆盖', value: 100 },
-    { label: '计划人员排查占比', value: 95 },
+
+  hazardLevelColor = hazardLevelColor;
+
+  semiAnnualOptions: SemiAnnual[] = [];
+
+  // firstSection = [
+  //   { label: '日常隐患排查计划', value: 3, key: 1 },
+  //   { label: '专项隐患排查计划', value: 6, key: 2 },
+  //   { label: '其他隐患排查计划', value: 9, key: 3 },
+  // ];
+  types = hazardPlanTypeOptions.map(t => ({ label: t.label, key: t.value as PlanType, count: 0 }));
+  // secondSection = [
+  //   { label: '计划站点排查覆盖', value: 65 },
+  //   { label: '其他场所排查覆盖', value: 100 },
+  //   { label: '计划人员排查占比', value: 95 },
+  // ];
+  discoveryList = [
+    {
+      level: '重大',
+      count: 0,
+      color: hazardLevelColor['重大'],
+    },
+    {
+      level: 'A级',
+      count: 3,
+      color: hazardLevelColor['A'],
+    },
+    {
+      level: 'B级',
+      count: 8,
+      color: hazardLevelColor['B'],
+    },
+    {
+      level: 'C级',
+      count: 112,
+      color: hazardLevelColor['C'],
+    },
   ];
-  discovery = {
-    significant: 1,
-    a: 2,
-    b: 3,
-    c: 5,
-  };
   notifications = [
     {
       type: 1,
@@ -122,12 +112,9 @@ export class InspectionPlanComponent {
       createDate: '2024-11-16T00:00:00',
     },
   ];
-  currentPlanKey = 2;
-  semiAnnualKey = 202406;
+  currentType?: PlanType;
+  semiAnnual?: SemiAnnual;
 
-  // plans: Hazard.InspectionPlan[] = Array.from({ length: getRandomSizeN(30) }, (_, i) =>
-  //   getMockPlan(i, this.getSemiAnnualLabel(this.semiAnnualKey))
-  // );
   plans: Hazard.InspectionPlan[] = [];
   fetching = false;
   fetchingDetail = false;
@@ -141,27 +128,92 @@ export class InspectionPlanComponent {
     private setting: SettingService
   ) {
     setting.getUsersOfCurrentCompany();
+    this.setSemiAnnualOptions();
   }
 
   ngOnInit() {
     this.fetchPlans();
   }
+  setSemiAnnualOptions() {
+    const upperHalf = '上半年度';
+    const lowerHalf = '下半年度';
+    const upperHalfRange = ['-01-01T00:00:00', '-06-30T23:59:59'];
+    const lowerHalfRange = ['-07-01T00:00:00', '-12-31T23:59:59'];
+
+    // 从 2024/12/27 开始,每半年一个周期
+    const start = new Date('2024-12-27T00:00:00');
+    const now = new Date();
+    let semiAnnualOptions = [
+      {
+        label: '2024下半年度',
+        start: new Date(2024 + lowerHalfRange[0]),
+        end: new Date(2024 + lowerHalfRange[1]),
+        key: 202407,
+      },
+    ];
+    const fullYears = now.getFullYear() - start.getFullYear() - 1;
+
+    function pushSemiAnnualOptions(year: number, half: string) {
+      const range = half === upperHalf ? upperHalfRange : lowerHalfRange;
+      semiAnnualOptions.push({
+        label: `${year}${half}`,
+        start: new Date(year + range[0]),
+        end: new Date(year + range[1]),
+        key: year,
+      });
+    }
+    for (let i = 0; i < fullYears; i++) {
+      pushSemiAnnualOptions(start.getFullYear() + i + 1, upperHalf);
+      pushSemiAnnualOptions(start.getFullYear() + i + 1, lowerHalf);
+    }
+
+    const isWhole = now.getMonth() > 6;
+
+    pushSemiAnnualOptions(now.getFullYear(), upperHalf);
+    if (isWhole) {
+      pushSemiAnnualOptions(now.getFullYear(), lowerHalf);
+    }
+    this.semiAnnualOptions = semiAnnualOptions;
+    this.semiAnnual = this.semiAnnualOptions[this.semiAnnualOptions.length - 1];
+  }
+  getParams() {
+    const params: Hazard.GetPlansParams = {
+      startDate: this.semiAnnual?.start.toISOString(),
+      endDate: this.semiAnnual?.end.toISOString(),
+      page: this.pageConfig.page,
+      size: this.pageConfig.size,
+    };
+    if (this.currentType) {
+      params.type = this.currentType;
+    }
+    return params;
+  }
 
   fetchPlans() {
     this.fetching = true;
     this.api.hazard
-      .getPlans({
-        page: this.pageConfig.page,
-        size: this.pageConfig.size,
-      })
+      .getPlans(this.getParams())
       .then(res => {
         this.plans = res.items;
         this.pageConfig.total = res.total;
+        this.setTypesCount(res);
+        this.setDiscoveryCount(res);
       })
       .finally(() => {
         this.fetching = false;
       });
   }
+  setTypesCount(res: Hazard.PlanListResponse) {
+    this.types.find(t => t.key === PlanType.COMPREHENSIVE)!.count = res.troubleshootTypeNumber.typeSpecial;
+    this.types.find(t => t.key === PlanType.DAILY)!.count = res.troubleshootTypeNumber.typeCommon;
+    this.types.find(t => t.key === PlanType.OTHER)!.count = res.troubleshootTypeNumber.typeOther;
+  }
+  setDiscoveryCount(res: Hazard.PlanListResponse) {
+    this.discoveryList.find(d => d.level === '重大')!.count = res.troubleLevelNumber.levelImportantTotal;
+    this.discoveryList.find(d => d.level === 'A级')!.count = res.troubleLevelNumber.levelATotal;
+    this.discoveryList.find(d => d.level === 'B级')!.count = res.troubleLevelNumber.levelBTotal;
+    this.discoveryList.find(d => d.level === 'C级')!.count = res.troubleLevelNumber.levelCTotal;
+  }
   onCurrentPageChange(page: number) {
     this.pageConfig.page = page;
     this.fetchPlans();
@@ -170,11 +222,14 @@ export class InspectionPlanComponent {
     this.pageConfig.page = 0;
     this.fetchPlans();
   }
-  getSemiAnnualLabel(key: number) {
-    return this.semiAnnualOptions.find(s => s.value === key)?.label || '2025上半年度';
-  }
-  handlePlanTypeChange(type: number) {
-    this.currentPlanKey = type;
+
+  handlePlanTypeChange(type: PlanType) {
+    if (this.currentType === type) {
+      this.currentType = undefined;
+    } else {
+      this.currentType = type;
+    }
+    this.handleRefreshList();
   }
   handleSemiAnnualChange(direction: 'left' | 'right') {
     const index = this.currentSemiAnnualOptionIndex;
@@ -184,7 +239,7 @@ export class InspectionPlanComponent {
     if (direction === 'right') {
       if (!this.rightArrowVisible) return;
     }
-    this.semiAnnualKey = this.semiAnnualOptions[index + (direction === 'left' ? -1 : 1)]?.value || 0;
+    this.semiAnnual = this.semiAnnualOptions[index + (direction === 'left' ? -1 : 1)];
   }
 
   getNotificationTypeText(t: number) {
@@ -223,11 +278,6 @@ export class InspectionPlanComponent {
   closeFormDrawer() {
     this.formConfig.visible = false;
   }
-  handleFetch() {
-    this.plans = Array.from({ length: getRandomSizeN(30) }, (_, i) =>
-      getMockPlan(i, this.getSemiAnnualLabel(this.semiAnnualKey))
-    );
-  }
   async handleFetchDetail(id: number) {
     this.fetchingDetail = true;
     await this.api.hazard
@@ -244,10 +294,10 @@ export class InspectionPlanComponent {
       });
   }
   get currentSemiAnnualOptionIndex() {
-    return this.semiAnnualOptions.findIndex(se => se.value === this.semiAnnualKey);
+    return this.semiAnnualOptions.findIndex(se => se.key === this.semiAnnual?.key);
   }
   get semiAnnualText() {
-    return this.semiAnnualOptions.find(se => se.value === this.semiAnnualKey)?.label || '';
+    return this.semiAnnualOptions.find(se => se.key === this.semiAnnual?.key)?.label || '';
   }
   get leftArrowVisible() {
     const index = this.currentSemiAnnualOptionIndex;

+ 2 - 2
src/app/pages/manager/hazard/inspection-plan/plan-form/plan-form.component.html

@@ -105,10 +105,10 @@
           class="precaution-secondary !rounded-xl ml-2"
           (click)="handleSelectTargets($event)"
         >
-          选择风险条目
+          {{ riskItemList.length > 0 ? '已选择' + riskItemList.length + '条' : '选择' }}
         </button>
       </risk-item-picker>
-      <div class="text-sm text-gray-500 px-4 pt-2">
+      <div class="text-sm text-gray-500 px-4 pt-4">
         <risk-items-visual-classify [data]="riskItemList"></risk-items-visual-classify>
       </div>
     </nz-form-control>

+ 1 - 1
src/app/services/apis/hazard.ts

@@ -57,7 +57,7 @@ export class HazardApi extends _BaseApi {
     return this.http.get<Promise<Hazard.InspectionPlanDetail>>('/troubleshoot/detail', { id });
   }
   async getPlans(params: Hazard.GetPlansParams) {
-    return this.http.get<Promise<{ items: Hazard.InspectionPlan[]; total: number }>>('/troubleshoot/list', params);
+    return this.http.get<Promise<Hazard.PlanListResponse>>('/troubleshoot/list', params);
   }
 
   async createPlanHandle(data: Partial<Hazard.InspectionPlanHandleDto>) {

+ 27 - 1
src/types/hazard.d.ts

@@ -241,11 +241,37 @@ declare namespace Hazard {
   }
 
   interface GetPlansParams {
-    company?: string;
+    // company?: string;
+    type?: string;
+    startDate?: string;
+    endDate?: string;
     page: number;
     size: number;
   }
 
+  interface PlanListResponse {
+    items: InspectionPlan[];
+    total: number;
+    troubleLevelNumber: {
+      /** C类总数 */
+      levelCTotal: number;
+      /** B类总数 */
+      levelBTotal: number;
+      /** A类总数 */
+      levelATotal: number;
+      /** 重大总数 */
+      levelImportantTotal: number;
+    };
+    troubleshootTypeNumber: {
+      /** 其他总数 */
+      typeOther: number;
+      /** 日常隐患总数 */
+      typeCommon: number;
+      /** 专项总数 */
+      typeSpecial: number;
+    };
+  }
+
   interface ReportTaskHazard {
     id?: number;
     troubleshootPlanHandleId: number;