Prechádzať zdrojové kódy

feat: hazard list search params

Signed-off-by: carlos <568187512@qq.com>
carlos 2 mesiacov pred
rodič
commit
31bd9ea910

+ 2 - 24
src/app/pages/manager/hazard/hazard-tracking/card/card.component.ts

@@ -2,30 +2,8 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
 import { CommonNzModule } from '../../../../../common.nz.module';
 import { BasicDataService } from '../../../../../services/basic.service';
 import { KnowledgeService } from '../../../../../services/knowledge.service';
+import { getHazardStatusText, hazardLevelColor, hazardStatusColor } from '../../hazard.utils';
 
-const hazardLevelColor: Record<string, string> = {
-  '重大': '#FA5F25',
-  'A': '#FFA100',
-  B: '#6600FF',
-  C: '#3083E6',
-};
-
-const hazardStatusColor: Record<string, string> = {
-  0: '#5E5E5E',
-  1: '#3083E6',
-  2: '#21A858',
-};
-const hazardStatusBgColor: Record<string, string> = {
-  0: '#5E5E5E66',
-  1: '#3083E666',
-  2: '#30E67A66',
-};
-
-const hazardStatusText: Record<string, string> = {
-  0: '已关闭',
-  1: '审核中',
-  2: '已处置',
-};
 @Component({
   selector: 'hazard-card',
   standalone: true,
@@ -58,7 +36,7 @@ export class HazardCardComponent {
       .join('、');
   }
   get hazardStatusText() {
-    return hazardStatusText[this.data.status];
+    return getHazardStatusText(this.data.status);
   }
   get hazardStatusBgColor() {
     return this.hazardStatusColor ? this.hazardStatusColor + '28' : '';

+ 8 - 2
src/app/pages/manager/hazard/hazard-tracking/hazard-form/hazard-form.component.html

@@ -111,7 +111,7 @@
   </nz-form-item>
   <nz-form-item>
     <nz-form-label [nzSpan]="4" nzRequired>问题场所</nz-form-label>
-    <nz-form-control [nzSpan]="20" nzErrorTip="场所/操作/设备至少选择一项">
+    <nz-form-control [nzSpan]="20" nzErrorTip="请选择问题场所">
       <nz-space class="overflow-visible">
         <nz-select
           class="precaution-select"
@@ -158,9 +158,15 @@
       <!-- </div> -->
     </nz-form-control>
   </nz-form-item>
+  <nz-form-item>
+    <nz-form-label [nzSpan]="4">具体地点</nz-form-label>
+    <nz-form-control [nzSpan]="18" nzErrorTip="请输入具体地点">
+      <input nz-input class="precaution-input" formControlName="spot" placeHolder="请输入具体地点" maxlength="50" />
+    </nz-form-control>
+  </nz-form-item>
   <nz-form-item>
     <nz-form-label [nzSpan]="4">现场照片</nz-form-label>
-    <nz-form-control [nzSpan]="4">
+    <nz-form-control [nzSpan]="18">
       <image-upload-group #imageUploadGroup [fileList]="fileList"></image-upload-group>
     </nz-form-control>
   </nz-form-item>

+ 6 - 0
src/app/pages/manager/hazard/hazard-tracking/hazard-form/hazard-form.component.less

@@ -1,3 +1,9 @@
+:host {
+  display: block;
+  max-height: calc(100vh - 55px);
+  overflow-y: auto;
+}
+
 .section-title {
   font-weight: 500;
   font-size: 16px;

+ 7 - 3
src/app/pages/manager/hazard/hazard-tracking/hazard-form/hazard-form.component.ts

@@ -23,6 +23,9 @@ export interface HazardFormComponentProps {
   imports: [CommonNzModule, ImageUploadGroupComponent, RiskItemDetailComponent],
   templateUrl: './hazard-form.component.html',
   styleUrl: './hazard-form.component.less',
+  host: {
+    class: 'custom-scroll-bar',
+  },
 })
 export class HazardFormComponent {
   @ViewChild('detailTpl') detailTpl!: TemplateRef<void>;
@@ -37,9 +40,10 @@ export class HazardFormComponent {
     riskCategory: this.fb.control<number | null>(null, [Validators.required]),
     riskItem: this.fb.control<number>(0, [Validators.required]),
     description: this.fb.control<string>('', [Validators.required, Validators.maxLength(1500)]),
-    position: this.fb.control<number | null>(null, [this.involvedValidator.bind(this)]),
-    operation: this.fb.control<number | null>(null, [this.involvedValidator.bind(this)]),
-    equipment: this.fb.control<number | null>(null, [this.involvedValidator.bind(this)]),
+    position: this.fb.control<number | null>(null, [Validators.required]),
+    spot: this.fb.control<string>('', []),
+    operation: this.fb.control<number | null>(null, []),
+    equipment: this.fb.control<number | null>(null, []),
     department: this.fb.control<number | null>(null, []),
     responsible: this.fb.control<number | null>(null, []),
   });

+ 45 - 0
src/app/pages/manager/hazard/hazard-tracking/hazard-search/hazard-search.component.html

@@ -0,0 +1,45 @@
+<div class="flex gap-4">
+  <span
+    class="link-select"
+    [class.active]="state.status !== -1"
+    nz-dropdown
+    [nzDropdownMenu]="statusMenu"
+    [nzOverlayStyle]="{ top: '-2px' }"
+    nzOverlayClassName="precaution-dropdown"
+  >
+    <span class="mr-2">{{ statusText }}</span>
+    <span nz-icon nzType="caret-down" nzTheme="outline" style="font-size: 10px"></span>
+  </span>
+
+  <span
+    class="link-select"
+    [class.active]="state.level !== ''"
+    nz-dropdown
+    [nzDropdownMenu]="levelMenu"
+    [nzOverlayStyle]="{ top: '2px' }"
+    nzOverlayClassName="precaution-dropdown"
+  >
+    <span class="mr-2">{{ levelText }}</span>
+    <span nz-icon nzType="caret-down" nzTheme="outline" style="font-size: 10px"></span>
+  </span>
+
+  <span>
+    <label nz-checkbox [(ngModel)]="state.onlyMine" (ngModelChange)="handleParamsChange()">只看我的</label>
+  </span>
+</div>
+
+<nz-dropdown-menu #statusMenu="nzDropdownMenu">
+  <ul nz-menu nzSelectable>
+    @for (item of statusOptions; track $index) {
+      <li nz-menu-item (click)="handleStatusChange(item.value)">{{ item.label }}</li>
+    }
+  </ul>
+</nz-dropdown-menu>
+
+<nz-dropdown-menu #levelMenu="nzDropdownMenu">
+  <ul nz-menu nzSelectable>
+    @for (item of levelOptions; track $index) {
+      <li nz-menu-item (click)="handleLevelChange(item.value)">{{ item.label }}</li>
+    }
+  </ul>
+</nz-dropdown-menu>

+ 18 - 0
src/app/pages/manager/hazard/hazard-tracking/hazard-search/hazard-search.component.less

@@ -0,0 +1,18 @@
+:host {
+  flex: 1;
+}
+
+.link-select {
+  display: inline-block;
+  min-width: 15%;
+  color: #333333;
+  font-size: 14px;
+  text-align: center;
+  cursor: pointer;
+  &:hover {
+    text-decoration: underline;
+  }
+  &.active {
+    color: var(--deep-blue);
+  }
+}

+ 23 - 0
src/app/pages/manager/hazard/hazard-tracking/hazard-search/hazard-search.component.spec.ts

@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HazardSearchComponent } from './hazard-search.component';
+
+describe('HazardSearchComponent', () => {
+  let component: HazardSearchComponent;
+  let fixture: ComponentFixture<HazardSearchComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [HazardSearchComponent]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(HazardSearchComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 39 - 0
src/app/pages/manager/hazard/hazard-tracking/hazard-search/hazard-search.component.ts

@@ -0,0 +1,39 @@
+import { Component, EventEmitter, Output } from '@angular/core';
+import { CommonNzModule } from '../../../../../common.nz.module';
+import { hazardLevelOptions, hazardStatusOptions, SearchParams } from '../../hazard.utils';
+
+@Component({
+  selector: 'hazard-search',
+  standalone: true,
+  imports: [CommonNzModule],
+  templateUrl: './hazard-search.component.html',
+  styleUrl: './hazard-search.component.less',
+})
+export class HazardSearchComponent {
+  @Output() onChange = new EventEmitter<SearchParams>();
+  state: SearchParams = {
+    status: -1,
+    level: '',
+    onlyMine: false,
+  };
+  statusOptions = [{ value: -1, label: '全部状态' }, ...hazardStatusOptions];
+  levelOptions = [{ value: '', label: '隐患等级' }, ...hazardLevelOptions];
+
+  handleStatusChange(s: number) {
+    this.state.status = s;
+    this.onChange.emit(this.state);
+  }
+  handleLevelChange(l: string) {
+    this.state.level = l;
+    this.onChange.emit(this.state);
+  }
+  handleParamsChange() {
+    this.onChange.emit(this.state);
+  }
+  get statusText() {
+    return this.statusOptions.find(op => op.value === this.state.status)?.label || '全部状态';
+  }
+  get levelText() {
+    return this.levelOptions.find(op => op.value === this.state.level)?.label || '隐患等级';
+  }
+}

+ 2 - 2
src/app/pages/manager/hazard/hazard-tracking/hazard-tracking.component.html

@@ -1,7 +1,7 @@
 <div class="shared-panel">
   <div class="shared-panel-header">
     <div class="title w-1/5">隐患列表</div>
-    <div class="flex-1">123</div>
+    <hazard-search (onChange)="onSearchParamsChange($event)" />
     <div class="pr-4">
       <button nz-button class="precaution-button add-button" nzType="primary" (click)="handleAddHazard()">
         <span nz-icon nzType="plus" class="text-xs -mr-1"></span>
@@ -11,7 +11,7 @@
   </div>
   <div class="hazard-tracking-content custom-scroll-bar">
     <div class="grid grid-cols-4 gap-4">
-      @for (hazard of hazards; track hazard.id) {
+      @for (hazard of filteredHazards; track hazard.id) {
         <hazard-card [data]="hazard" (onViewDetail)="handleViewDetail($event)"></hazard-card>
       }
     </div>

+ 27 - 5
src/app/pages/manager/hazard/hazard-tracking/hazard-tracking.component.ts

@@ -2,9 +2,11 @@ import { Component } from '@angular/core';
 import { horizontalInOutReverse300ms } from '../../../../common.animation';
 import { CommonNzModule } from '../../../../common.nz.module';
 import { CustomDrawerComponent } from '../../../../shared/custom-drawer/custom-drawer.component';
+import { SearchParams } from '../hazard.utils';
 import { HazardCardComponent } from './card/card.component';
 import { HazardDetailComponent } from './detail/detail.component';
 import { HazardFormComponent, HazardFormComponentProps } from './hazard-form/hazard-form.component';
+import { HazardSearchComponent } from './hazard-search/hazard-search.component';
 
 export interface HazardDetailConfig {
   visible: boolean;
@@ -14,6 +16,8 @@ export interface HazardDetailConfig {
 const hazardLevels = ['重大', 'A', 'B', 'C'];
 const departments = Array.from({ length: 15 }, (_, i) => i + 1);
 
+const getRandomSize = (max: number) => Math.floor(Math.random() * max);
+
 const getMockHazard = (index: number): Hazard.HazardDto => {
   return {
     id: index,
@@ -26,22 +30,29 @@ const getMockHazard = (index: number): Hazard.HazardDto => {
     creator: '张三',
     reportingTime: '2024-09-25 12:00',
     responsible: 1,
-    responsibleDepartments: departments.slice(0, Math.floor(Math.random() * departments.length)),
-    images: Array.from({ length: 11 }).map(() => `https://loremflickr.com/260/150?q=${Math.random()}`),
+    responsibleDepartments: departments.slice(0, getRandomSize(departments.length)),
+    images: Array.from({ length: getRandomSize(11) }).map(() => `https://loremflickr.com/260/150?q=${Math.random()}`),
   };
 };
 
 @Component({
   selector: 'app-hazard-tracking',
   standalone: true,
-  imports: [CommonNzModule, HazardCardComponent, HazardDetailComponent, CustomDrawerComponent, HazardFormComponent],
+  imports: [
+    CommonNzModule,
+    HazardCardComponent,
+    HazardDetailComponent,
+    CustomDrawerComponent,
+    HazardFormComponent,
+    HazardSearchComponent,
+  ],
   templateUrl: './hazard-tracking.component.html',
   styleUrl: './hazard-tracking.component.less',
   animations: [horizontalInOutReverse300ms],
 })
 export class HazardTrackingComponent {
   hazards: Hazard.HazardDto[] = [];
-
+  filteredHazards: Hazard.HazardDto[] = [];
   detailConfig: HazardDetailConfig = {
     visible: false,
     data: {} as Hazard.HazardDto,
@@ -51,7 +62,18 @@ export class HazardTrackingComponent {
     data: undefined,
   };
   ngOnInit() {
-    this.hazards = Array.from({ length: 2 }, (_, i) => getMockHazard(i));
+    this.hazards = Array.from({ length: getRandomSize(11) }, (_, i) => getMockHazard(i));
+    this.filteredHazards = this.hazards.slice();
+  }
+  onSearchParamsChange(p: SearchParams) {
+    const { status, level } = p;
+    this.filteredHazards = this.hazards.filter(h => {
+      if (status !== -1) {
+        if (h.status !== status) return false;
+      }
+      if (level && h.level !== level) return false;
+      return true;
+    });
   }
   handleViewDetail(data: Hazard.HazardDto) {
     this.detailConfig.visible = true;

+ 36 - 1
src/app/pages/manager/hazard/hazard.utils.ts

@@ -1,5 +1,6 @@
 export const hazardLevels = ['重大', 'A', 'B', 'C'];
-export const hazardLevelOptions: Option[] = [
+
+export const hazardLevelOptions: Option<string>[] = [
   { label: '重大', value: '重大' },
   { label: 'A类', value: 'A' },
   { label: 'B类', value: 'B' },
@@ -10,3 +11,37 @@ export const hazardTypeOptions: Option[] = [
   { label: '生产现场类隐患', value: 'product' },
   { label: '基础管理类隐患', value: 'base' },
 ];
+
+export const hazardLevelColor: Record<string, string> = {
+  '重大': '#FA5F25',
+  'A': '#FFA100',
+  B: '#6600FF',
+  C: '#3083E6',
+};
+
+export const hazardStatusColor: Record<string, string> = {
+  0: '#5E5E5E',
+  1: '#3083E6',
+  2: '#21A858',
+};
+export const hazardStatusBgColor: Record<string, string> = {
+  0: '#5E5E5E66',
+  1: '#3083E666',
+  2: '#30E67A66',
+};
+
+export const hazardStatusOptions: Option<number>[] = [
+  { value: 0, label: '已关闭' },
+  { value: 1, label: '审核中' },
+  { value: 2, label: '已处置' },
+];
+
+export const getHazardStatusText = (s: number) => {
+  return hazardStatusOptions.find(hz => hz.value === s)?.label || '';
+};
+
+export interface SearchParams {
+  status: number;
+  level: string;
+  onlyMine: boolean;
+}