Browse Source

feat: hazard tracking page

Signed-off-by: carlos <568187512@qq.com>
carlos 2 months ago
parent
commit
7e680e6968
45 changed files with 1349 additions and 38 deletions
  1. 0 1
      src/app/app.routes.ts
  2. 11 0
      src/app/common.animation.ts
  3. 1 0
      src/app/pages/manager/basic/basic.route.ts
  4. 47 0
      src/app/pages/manager/hazard/hazard-tracking/card/card.component.html
  5. 84 0
      src/app/pages/manager/hazard/hazard-tracking/card/card.component.less
  6. 23 0
      src/app/pages/manager/hazard/hazard-tracking/card/card.component.spec.ts
  7. 72 0
      src/app/pages/manager/hazard/hazard-tracking/card/card.component.ts
  8. 75 0
      src/app/pages/manager/hazard/hazard-tracking/detail/detail-header/detail-header.component.html
  9. 27 0
      src/app/pages/manager/hazard/hazard-tracking/detail/detail-header/detail-header.component.less
  10. 23 0
      src/app/pages/manager/hazard/hazard-tracking/detail/detail-header/detail-header.component.spec.ts
  11. 49 0
      src/app/pages/manager/hazard/hazard-tracking/detail/detail-header/detail-header.component.ts
  12. 78 0
      src/app/pages/manager/hazard/hazard-tracking/detail/detail.component.html
  13. 50 0
      src/app/pages/manager/hazard/hazard-tracking/detail/detail.component.less
  14. 23 0
      src/app/pages/manager/hazard/hazard-tracking/detail/detail.component.spec.ts
  15. 109 0
      src/app/pages/manager/hazard/hazard-tracking/detail/detail.component.ts
  16. 27 0
      src/app/pages/manager/hazard/hazard-tracking/detail/handle-acceptance/handle-acceptance.component.html
  17. 61 0
      src/app/pages/manager/hazard/hazard-tracking/detail/handle-acceptance/handle-acceptance.component.less
  18. 23 0
      src/app/pages/manager/hazard/hazard-tracking/detail/handle-acceptance/handle-acceptance.component.spec.ts
  19. 20 0
      src/app/pages/manager/hazard/hazard-tracking/detail/handle-acceptance/handle-acceptance.component.ts
  20. 23 0
      src/app/pages/manager/hazard/hazard-tracking/detail/handle-progress/handle-progress.component.html
  21. 29 0
      src/app/pages/manager/hazard/hazard-tracking/detail/handle-progress/handle-progress.component.less
  22. 23 0
      src/app/pages/manager/hazard/hazard-tracking/detail/handle-progress/handle-progress.component.spec.ts
  23. 31 0
      src/app/pages/manager/hazard/hazard-tracking/detail/handle-progress/handle-progress.component.ts
  24. 27 1
      src/app/pages/manager/hazard/hazard-tracking/hazard-tracking.component.html
  25. 14 0
      src/app/pages/manager/hazard/hazard-tracking/hazard-tracking.component.less
  26. 47 3
      src/app/pages/manager/hazard/hazard-tracking/hazard-tracking.component.ts
  27. 1 0
      src/app/pages/manager/hazard/hazard.route.ts
  28. 4 18
      src/app/pages/manager/layout/header/header.component.ts
  29. 1 0
      src/app/pages/manager/risk/risk.route.ts
  30. 1 12
      src/app/pages/manager/workbench/problem-assessment/problem-assessment.component.ts
  31. 1 0
      src/app/pages/manager/workbench/workbench.route.ts
  32. 3 3
      src/app/shared/data-empty/data-empty.component.html
  33. 2 0
      src/app/shared/data-empty/data-empty.component.ts
  34. 19 0
      src/app/shared/scroll-images/scroll-images.component.html
  35. 36 0
      src/app/shared/scroll-images/scroll-images.component.less
  36. 23 0
      src/app/shared/scroll-images/scroll-images.component.spec.ts
  37. 59 0
      src/app/shared/scroll-images/scroll-images.component.ts
  38. 100 0
      src/assets/icons/data-empty.svg
  39. 22 0
      src/assets/icons/hazard-level.svg
  40. 18 0
      src/assets/icons/hazard/audit.svg
  41. 9 0
      src/assets/icons/hazard/plan-audit.svg
  42. 15 0
      src/assets/icons/hazard/plan.svg
  43. 21 0
      src/assets/icons/hazard/submit.svg
  44. 1 0
      src/assets/icons/left.svg
  45. 16 0
      src/types/hazard.d.ts

+ 0 - 1
src/app/app.routes.ts

@@ -18,5 +18,4 @@ export const routes: Routes = [
     path: 'error',
     loadComponent: () => import('./pages/error/error.component').then(m => m.ErrorComponent),
   },
-  { path: '**', redirectTo: '/error' },
 ];

+ 11 - 0
src/app/common.animation.ts

@@ -41,3 +41,14 @@ export const scaleInOut = trigger('scaleInOut', [
   transition('void => *', [style({ opacity: 0, transform: 'scale(0)' }), animate('200ms')]),
   transition('* => void', [style({ opacity: 0, transform: 'scale(0)' }), animate('200ms')]),
 ]);
+
+export const horizontalInOutReverse300ms = trigger('horizontalInOutReverse300ms', [
+  state('in', style({ transform: 'translate(0, 0%)', opacity: 1 })),
+  transition('void => *', [
+    style({ transform: `translate(-100%, 0)` }),
+    animate('300ms cubic-bezier(0.35, 0, 0.25, 1)', style({ transform: 'translate(0, 0%)' })),
+  ]),
+  transition('* => void', [
+    animate('300ms cubic-bezier(0.35, 0, 0.25, 1)', style({ transform: `translate(-100%, 0)` })),
+  ]),
+]);

+ 1 - 0
src/app/pages/manager/basic/basic.route.ts

@@ -66,4 +66,5 @@ export const basicRoutes: Routes = [
       weight: 9,
     },
   },
+  { path: '', pathMatch: 'full', redirectTo: 'line' },
 ];

+ 47 - 0
src/app/pages/manager/hazard/hazard-tracking/card/card.component.html

@@ -0,0 +1,47 @@
+<div class="hazard-card">
+  <div class="card-header">
+    <div class="mr-2">
+      <div class="level-tag" [style.background-color]="hazardLevelColor[data.level]">{{ data.level }}</div>
+    </div>
+    <div class="title">{{ data.name }}</div>
+    <div class="ml-2">
+      <div class="time">提交时间:{{ data.reportingTime | date: 'MM/dd HH:mm' }}</div>
+    </div>
+  </div>
+  <div class="card-body">
+    <div class="row mt-2">
+      <span class="label">风险类别:</span>
+      <span class="value mr-6">{{ typeName }}</span>
+      <span class="label">风险项:</span>
+      <span class="value">{{ categoryName }}</span>
+    </div>
+    <div class="mb-2 flex justify-between">
+      <span class="label">隐患图片:</span>
+      <span class="value">
+        <div class="inline-flex flex-wrap gap-x-1">
+          @for (url of images; track $index) {
+            <div class="image-box">
+              <img width="46px" height="26px" [src]="url" alt="" />
+            </div>
+          }
+        </div>
+      </span>
+      <span>
+        <span class="text-blue-500 underline text-xs inline-block mx-4 cursor-pointer" (click)="handleViewDetail(data)">
+          查看更多
+        </span>
+      </span>
+    </div>
+    <div class="row">
+      <span class="label">责任部门:</span>
+      <span class="value">{{ responsibleDepartmentsName }}</span>
+    </div>
+    <div class="flex mb-2">
+      <span class="label">主要责任人:</span>
+      <span class="value flex-1 line-clamp-1">{{ data.responsible }}</span>
+      <span class="status-tag mr-2" [style.background-color]="hazardStatusBgColor" [style.color]="hazardStatusColor">{{
+        hazardStatusText
+      }}</span>
+    </div>
+  </div>
+</div>

+ 84 - 0
src/app/pages/manager/hazard/hazard-tracking/card/card.component.less

@@ -0,0 +1,84 @@
+.hazard-card {
+  box-shadow: 0px 2px 4px 2px rgba(53, 53, 53, 0.1);
+  border-radius: 8px;
+  border: 1px solid transparent;
+  padding: 8px 0 2px 8px;
+  transition: border 0.3s;
+  background-color: rgba(223, 223, 223, 0.2);
+  &:hover {
+    border: 1px solid #2953e830;
+  }
+  .card-header {
+    @apply flex items-center;
+    line-height: 31px;
+    .title {
+      @apply flex-1  font-medium line-clamp-1;
+      font-size: 16px;
+      line-height: 30px;
+      color: #333333;
+    }
+    .time {
+      display: inline-block;
+      background: #ffffff;
+      box-shadow:
+        0px 0px 8px 0px rgba(223, 223, 223, 0.5),
+        0px 2px 4px 0px rgba(187, 187, 187, 0.5);
+      border-radius: 12px 0px 0px 12px;
+      backdrop-filter: blur(1.838235294117647px);
+      font-weight: 400;
+      font-size: 10px;
+      color: #333333;
+      line-height: 14px;
+      text-shadow: 0px 0px 8px rgba(223, 223, 223, 0.5);
+      text-stroke: 0.5px #000000;
+      text-align: left;
+      font-style: normal;
+      -webkit-text-stroke: 0.5px #000000;
+      // padding: 8px 0 8px 16px;
+      padding-left: 16px;
+      padding-right: 4px;
+      line-height: 30px;
+    }
+  }
+}
+
+.level-tag {
+  @apply flex-center;
+  box-shadow: 0px 0px 8px 0px rgba(223, 223, 223, 0.5);
+  border-radius: 10px;
+  height: 20px;
+  min-width: 40px;
+  padding: 0 8px;
+  font-size: 12px;
+  color: #ffffff;
+}
+
+.status-tag {
+  @apply flex-center;
+  box-shadow: 0px 0px 8px 0px rgba(223, 223, 223, 0.5);
+  border-radius: 10px;
+  height: 23px;
+  min-width: 50px;
+  padding: 0 8px;
+  font-size: 12px;
+  color: #ffffff;
+}
+
+.label {
+  color: #333333;
+  font-size: 14px;
+}
+.value {
+  color: #666666;
+  font-size: 14px;
+}
+.row {
+  @apply mb-2 line-clamp-1 pr-2;
+}
+
+.image-box {
+  box-shadow: 0px 0px 8px 0px rgba(223, 223, 223, 0.5);
+  border-radius: 5px;
+  border: 0px solid #979797;
+  overflow: hidden;
+}

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

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

+ 72 - 0
src/app/pages/manager/hazard/hazard-tracking/card/card.component.ts

@@ -0,0 +1,72 @@
+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';
+
+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,
+  imports: [CommonNzModule],
+  templateUrl: './card.component.html',
+  styleUrl: './card.component.less',
+})
+export class HazardCardComponent {
+  @Input() data!: Hazard.HazardDto;
+  @Output() onViewDetail = new EventEmitter<Hazard.HazardDto>();
+  hazardLevelColor = hazardLevelColor;
+
+  constructor(
+    private knowledge: KnowledgeService,
+    private basic: BasicDataService
+  ) {}
+  handleViewDetail(data: Hazard.HazardDto) {
+    this.onViewDetail.emit(data);
+  }
+  get typeName() {
+    return this.knowledge.getTypeName(this.data.type);
+  }
+  get categoryName() {
+    return this.knowledge.getCategoryName(this.data.category, this.data.type);
+  }
+  get responsibleDepartmentsName() {
+    return this.basic.departments
+      .filter(d => this.data.responsibleDepartments.includes(d.id))
+      .map(d => d.name)
+      .join('、');
+  }
+  get hazardStatusText() {
+    return hazardStatusText[this.data.status];
+  }
+  get hazardStatusBgColor() {
+    return this.hazardStatusColor ? this.hazardStatusColor + '28' : '';
+  }
+  get hazardStatusColor() {
+    return hazardStatusColor[this.data.status] ? hazardStatusColor[this.data.status] : '';
+  }
+  get images() {
+    return this.data.images.slice(0, 4).map(i => i);
+  }
+}

+ 75 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/detail-header/detail-header.component.html

@@ -0,0 +1,75 @@
+<div class="flex">
+  <div class="box-wrapper left-box mr-2">
+    <div
+      class="flex-center h-full px-3 transition-all cursor-pointer text-gray-600 hover:text-primary hover:bg-blue-100"
+      (click)="onClose.emit()"
+    >
+      <span nz-icon nzType="icons:left" nzTheme="outline" class="text-2xl scale-y-150"></span>
+    </div>
+  </div>
+  <div class="box-wrapper flex flex-1 py-3 pl-6 pr-4" style="height: 155px">
+    <div class="mr-8">
+      <div class="text-xl font-medium mb-3">隐患详情</div>
+      <div class="field">
+        <div class="label">隐患工单:</div>
+        <div class="value">{{ data.id }}</div>
+      </div>
+      <div class="field">
+        <div class="label">提交人:</div>
+        <div class="value">{{ data.creator }}</div>
+      </div>
+      <div class="field">
+        <div class="label">提交时间:</div>
+        <div class="value">{{ data.reportingTime | date: 'yyyy/MM/dd HH:mm' }}</div>
+      </div>
+      <div class="field">
+        <div class="label">风险类别:</div>
+        <div class="value">{{ typeName }}</div>
+      </div>
+    </div>
+    <div class="pt-5">
+      <div class="field">
+        <div class="label">隐患名称:</div>
+        <div class="value">{{ data.name }}</div>
+      </div>
+      <div class="field">
+        <div class="label">场景/设备:</div>
+        <div class="value">{{ data.creator }}</div>
+      </div>
+      <div class="field">
+        <div class="label">提交时间:</div>
+        <div class="value">{{ data.reportingTime | date: 'yyyy/MM/dd HH:mm' }}</div>
+      </div>
+      <div class="field">
+        <div class="label">风险类别:</div>
+        <div class="value">{{ typeName }}</div>
+      </div>
+    </div>
+  </div>
+  <div class="box-wrapper px-12 ml-4 pt-4">
+    <div class="text-lg font-medium mb-3">隐患评级</div>
+    <div class="flex" [style.--color]="hazardLevelColor">
+      <div>
+        <span nz-icon nzType="icons:hazard-level" nzTheme="outline" style="font-size: 60px; color: var(--color)"></span>
+      </div>
+      <div class="w-[240px]">
+        <div class="flex justify-between items-end text-[#999999] mb-2" style="font-size: 12px; line-height: 1">
+          @for (level of ['C', 'B', 'A', '重']; track $index) {
+            <span class="flex-1 text-right" [class.active-level-text]="data.level.indexOf(level) > -1">{{
+              level
+            }}</span>
+          }
+        </div>
+        <div>
+          <div class="relative h-[4px] bg-[#F2F2F2] rounded-[4px]" [style.background-color]="hazardLevelBgColor">
+            <div
+              class="absolute left-0 top-0 h-[4px] rounded-[4px]"
+              [style.background-color]="hazardLevelColor"
+              [style.width]="levelProgress"
+            ></div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

+ 27 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/detail-header/detail-header.component.less

@@ -0,0 +1,27 @@
+.box-wrapper {
+  background: #ffffff;
+  box-shadow:
+    0px 0px 8px 0px rgba(223, 223, 223, 0.5),
+    0px 3px 4px 0px rgba(195, 195, 195, 0.14);
+  border-radius: 20px;
+  overflow: hidden;
+}
+
+.field {
+  display: flex;
+  align-items: center;
+  font-size: 14px;
+  .label {
+    color: #666666;
+    font-size: 14px;
+  }
+  .value {
+    color: #000000;
+    font-size: 14px;
+  }
+}
+
+.active-level-text {
+  font-size: 34px;
+  color: var(--color);
+}

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

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

+ 49 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/detail-header/detail-header.component.ts

@@ -0,0 +1,49 @@
+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';
+
+const hazardLevelColor: Record<string, string> = {
+  '重大': '#FA5F25',
+  'A': '#FFA100',
+  B: '#6600FF',
+  C: '#3083E6',
+};
+
+@Component({
+  selector: 'hazard-detail-header',
+  standalone: true,
+  imports: [CommonNzModule],
+  templateUrl: './detail-header.component.html',
+  styleUrl: './detail-header.component.less',
+})
+export class DetailHeaderComponent {
+  @Input() data: Hazard.HazardDto = {} as Hazard.HazardDto;
+  @Output() onClose = new EventEmitter<void>();
+  constructor(
+    private knowledge: KnowledgeService,
+    private basic: BasicDataService
+  ) {}
+
+  get typeName() {
+    return this.knowledge.getTypeName(this.data.type);
+  }
+  get categoryName() {
+    return this.knowledge.getCategoryName(this.data.category, this.data.type);
+  }
+  get responsibleDepartmentsName() {
+    return this.basic.departments
+      .filter(d => this.data.responsibleDepartments.includes(d.id))
+      .map(d => d.name)
+      .join('、');
+  }
+  get hazardLevelBgColor() {
+    return this.hazardLevelColor ? this.hazardLevelColor + '28' : '';
+  }
+  get hazardLevelColor() {
+    return hazardLevelColor[this.data.level] ? hazardLevelColor[this.data.level] : '';
+  }
+  get levelProgress() {
+    return ['C', 'B', 'A', '重'].findIndex(l => this.data.level.indexOf(l) > -1) * 25 + 25 + '%';
+  }
+}

+ 78 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/detail.component.html

@@ -0,0 +1,78 @@
+<hazard-detail-header [data]="data" (onClose)="onClose.emit()"></hazard-detail-header>
+<div class="flex mt-4">
+  <div class="box-wrapper mr-4 py-4" style="width: 360px">
+    <div class="text-xl font-medium mb-3 px-6">治理流程</div>
+    <div>
+      @for (item of flowList; track item.status) {
+        <div class="flex items-center justify-between px-6">
+          <div class="flex">
+            <div>
+              <div>
+                <span
+                  nz-icon
+                  [nzType]="getIcon(item)"
+                  [style.color]="getIconColor(item)"
+                  style="font-size: 48px"
+                ></span>
+              </div>
+              <div class="text-center pt-2">
+                <div
+                  class="inline-block w-[3px] h-[40px] rounded-[4px] bg-[#F2F2F2]"
+                  [style.background-color]="getIconColor(item)"
+                ></div>
+              </div>
+            </div>
+            <div class="ml-4 text-[#333333]">
+              <div class="text-xl pb-3">{{ item.title }}</div>
+              <div class="text-base text-[#242731]">
+                {{ item.creator }}
+                <span class="mx-1 text-[#ECECEC]">|</span>
+                {{ item.createTime }}
+              </div>
+            </div>
+          </div>
+        </div>
+      }
+    </div>
+  </div>
+  <div class="box-wrapper flex-1 py-4">
+    <div class="text-xl font-medium mb-3 px-6">隐患信息</div>
+    <div class="px-6 text-[#333333] text-sm mb-4">
+      {{ data.description }}
+    </div>
+    <div class="text-xl font-medium mb-3 px-6">现场照片</div>
+    <!-- <div class="px-6 flex flex-wrap mb-4">
+      @for (item of data.images; track item) {
+        <div class="inline-block bg-gray-300 rounded-lg overflow-hidden mr-3 last:mr-0">
+          <img nz-image [nzSrc]="item" alt="现场照片" width="154" height="88" />
+        </div>
+      }
+    </div> -->
+    <div class="px-6 mb-4">
+      <scroll-images [data]="data.images" [width]="154" [height]="88"></scroll-images>
+    </div>
+    <div class="text-xl font-medium mb-3 px-6">治理方案</div>
+    <div class="grid grid-cols-2 gap-x-6 px-6">
+      <div class="measure-item">
+        <div class="title">措施</div>
+        <div class="content">
+          <data-empty [isEmpty]="true" text="暂无措施..."></data-empty>
+        </div>
+      </div>
+      <div class="target-item">
+        <div class="title">治理目标</div>
+        <div class="content">
+          <data-empty [isEmpty]="true" text="暂无目标..."></data-empty>
+        </div>
+      </div>
+    </div>
+    <div class="pt-4 px-6 text-right">
+      <span class="label">治理人:</span>
+      <span class="value">张丹山</span>
+      <span class="label ml-6">治理时间:</span>
+      <span class="value">2024-01-01 12:00</span>
+    </div>
+  </div>
+</div>
+<hazard-handle-progress></hazard-handle-progress>
+<hazard-handle-acceptance></hazard-handle-acceptance>

+ 50 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/detail.component.less

@@ -0,0 +1,50 @@
+:host {
+  position: absolute;
+  top: -1.5rem;
+  left: calc(-1.5rem + 1px);
+  width: calc(100% + 3rem - 1px);
+  height: calc(100vh - var(--header-height) - 16px);
+  padding: 1.5rem;
+  // background-color: #f8f8f8;
+  background-color: #ffffff;
+  overflow-y: auto;
+}
+
+.box-wrapper {
+  background: #ffffff;
+  box-shadow:
+    0px 0px 8px 0px rgba(223, 223, 223, 0.5),
+    0px 3px 4px 0px rgba(195, 195, 195, 0.14);
+  border-radius: 20px;
+  overflow: hidden;
+}
+
+.common-item {
+  height: 229px;
+  padding: 12px 16px;
+  border-radius: 16px;
+  color: #ffffff;
+  .title {
+    font-size: 20px;
+    font-weight: 500;
+  }
+}
+.measure-item {
+  background: #6184fe;
+  box-shadow: 0px 25px 80px 0px rgba(84, 81, 255, 0.21);
+  .common-item();
+}
+.target-item {
+  background: #2884ff;
+  box-shadow: 0px 25px 80px 0px rgba(40, 132, 255, 0.15);
+  .common-item();
+}
+
+.label {
+  color: #666666;
+  font-size: 14px;
+}
+.value {
+  color: #333333;
+  font-size: 14px;
+}

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

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

+ 109 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/detail.component.ts

@@ -0,0 +1,109 @@
+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 { DataEmptyComponent } from '../../../../../shared/data-empty/data-empty.component';
+import { ScrollImagesComponent } from '../../../../../shared/scroll-images/scroll-images.component';
+import { DetailHeaderComponent } from './detail-header/detail-header.component';
+import { HandleAcceptanceComponent } from './handle-acceptance/handle-acceptance.component';
+import { HandleProgressComponent } from './handle-progress/handle-progress.component';
+
+interface FlowItem {
+  title: string;
+  creator: string;
+  createTime: string;
+  status: string;
+  action: string;
+}
+
+interface ActionConfig {
+  title: string;
+  activeColor: string;
+  icon: string;
+}
+const flowActionMap: Record<string, ActionConfig> = {
+  submit: {
+    title: '提交问题',
+    activeColor: '#2953E8',
+    icon: 'icons:hazard/submit',
+  },
+  audit: {
+    title: '审隐患核',
+    activeColor: '#DF0000',
+    icon: 'icons:hazard/audit',
+  },
+  plan: {
+    title: '治理方案',
+    activeColor: '#FA642A',
+    icon: 'icons:hazard/plan',
+  },
+  planAudit: {
+    title: '方案审核',
+    activeColor: '#01C49B',
+    icon: 'icons:hazard/plan-audit',
+  },
+};
+
+@Component({
+  selector: 'hazard-detail',
+  standalone: true,
+  imports: [
+    CommonNzModule,
+    DetailHeaderComponent,
+    DataEmptyComponent,
+    ScrollImagesComponent,
+    HandleProgressComponent,
+    HandleAcceptanceComponent,
+  ],
+  templateUrl: './detail.component.html',
+  styleUrl: './detail.component.less',
+  host: {
+    class: 'custom-scroll-bar',
+  },
+})
+export class HazardDetailComponent {
+  @Input() data: Hazard.HazardDto = {} as Hazard.HazardDto;
+  @Output() onClose = new EventEmitter<void>();
+
+  flowList: FlowItem[] = [
+    {
+      title: '提交问题',
+      creator: '张三',
+      createTime: '2024-01-01 12:00',
+      status: 'submit',
+      action: 'submit',
+    },
+    {
+      title: '审隐患核',
+      creator: '李四',
+      createTime: '2024-01-01 12:00',
+      status: 'audit',
+      action: 'audit',
+    },
+    {
+      title: '治理方案',
+      creator: '王五',
+      createTime: '2024-01-01 12:00',
+      status: 'plan',
+      action: 'plan',
+    },
+    {
+      title: '方案审核',
+      creator: '赵六',
+      createTime: '2024-01-01 12:00',
+      status: 'planAudit',
+      action: 'planAudit',
+    },
+  ];
+  constructor(
+    private knowledge: KnowledgeService,
+    private basic: BasicDataService
+  ) {}
+
+  getIcon(item: FlowItem) {
+    return flowActionMap[item.action]?.icon;
+  }
+  getIconColor(item: FlowItem) {
+    return flowActionMap[item.action]?.activeColor || '#A5A5A5';
+  }
+}

+ 27 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/handle-acceptance/handle-acceptance.component.html

@@ -0,0 +1,27 @@
+<div class="box-wrapper py-4 mt-4 relative">
+  <div class="text-xl font-medium mb-3 px-6">治理验收</div>
+
+  <div class="handle-data">
+    <div class="text-base font-medium text-[#333333]">验收结论</div>
+    <div class="text-[#666666] text-sm py-4">
+      {{ data.description }}
+    </div>
+    <div class="text-base font-medium text-[#333333]">照片</div>
+    <div class="py-4">
+      <scroll-images [data]="data.images"></scroll-images>
+    </div>
+
+    <div class="px-6 text-right">
+      <span class="label">验收人:</span>
+      <span class="value">{{ data.creator }}</span>
+    </div>
+  </div>
+
+  <div class="part">
+    <div class="outer-box">
+      <div class="inner-box">
+        <span>{{ data.status }}</span>
+      </div>
+    </div>
+  </div>
+</div>

+ 61 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/handle-acceptance/handle-acceptance.component.less

@@ -0,0 +1,61 @@
+.box-wrapper {
+  background: #ffffff;
+  box-shadow:
+    0px 0px 8px 0px rgba(223, 223, 223, 0.5),
+    0px 3px 4px 0px rgba(195, 195, 195, 0.14);
+  border-radius: 20px;
+  overflow: hidden;
+}
+
+.label {
+  color: #666666;
+  font-size: 14px;
+}
+.value {
+  color: #333333;
+  font-size: 14px;
+}
+
+.handle-data {
+  padding: 0 24px;
+}
+
+.part {
+  position: absolute;
+  top: 0;
+  right: 0;
+  width: 100px;
+  height: 100px;
+  background-color: transparent;
+  .outer-box {
+    width: 100%;
+    height: 100%;
+    color: red;
+
+    min-height: 100px;
+    min-height: 100px;
+    text-align: center;
+    position: relative;
+    overflow: hidden;
+    background-color: transparent;
+  }
+
+  .inner-box {
+    overflow: hidden;
+    white-space: nowrap;
+    position: absolute;
+    right: -45px;
+    top: 20px;
+    transform: rotate(45deg);
+    background-color: #2fc25b;
+
+    span {
+      color: #fff;
+      display: block;
+      // margin: 1px 0;
+      padding: 5px 65px;
+      text-align: center;
+      font-size: 18px;
+    }
+  }
+}

+ 23 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/handle-acceptance/handle-acceptance.component.spec.ts

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

+ 20 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/handle-acceptance/handle-acceptance.component.ts

@@ -0,0 +1,20 @@
+import { Component } from '@angular/core';
+import { CommonNzModule } from '../../../../../../common.nz.module';
+import { ScrollImagesComponent } from '../../../../../../shared/scroll-images/scroll-images.component';
+
+@Component({
+  selector: 'hazard-handle-acceptance',
+  standalone: true,
+  imports: [CommonNzModule, ScrollImagesComponent],
+  templateUrl: './handle-acceptance.component.html',
+  styleUrl: './handle-acceptance.component.less',
+})
+export class HandleAcceptanceComponent {
+  data = {
+    description: '隐患整改',
+    time: '2024-01-01 12:00',
+    status: '合格',
+    creator: '张三',
+    images: Array.from({ length: 11 }).map(() => `https://loremflickr.com/260/150?q=${Math.random()}`),
+  };
+}

+ 23 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/handle-progress/handle-progress.component.html

@@ -0,0 +1,23 @@
+<div class="box-wrapper py-4 mt-4">
+  <div class="text-xl font-medium mb-3 px-6">治理进度</div>
+
+  @for (item of data; track item) {
+    <div class="handle-item">
+      <div class="text-base font-medium text-[#333333]">现场处理描述</div>
+      <div class="text-[#666666] text-sm py-4">
+        {{ item.description }}
+      </div>
+      <div class="text-base font-medium text-[#333333]">照片</div>
+      <div class="py-4">
+        <scroll-images [data]="item.images"></scroll-images>
+      </div>
+
+      <div class="px-6 text-right">
+        <span class="label">治理人:</span>
+        <span class="value">张丹山</span>
+        <span class="label ml-6">治理时间:</span>
+        <span class="value">2024-01-01 12:00</span>
+      </div>
+    </div>
+  }
+</div>

+ 29 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/handle-progress/handle-progress.component.less

@@ -0,0 +1,29 @@
+.box-wrapper {
+  background: #ffffff;
+  box-shadow:
+    0px 0px 8px 0px rgba(223, 223, 223, 0.5),
+    0px 3px 4px 0px rgba(195, 195, 195, 0.14);
+  border-radius: 20px;
+  overflow: hidden;
+}
+
+.label {
+  color: #666666;
+  font-size: 14px;
+}
+.value {
+  color: #333333;
+  font-size: 14px;
+}
+
+.handle-item {
+  padding: 24px 24px 16px;
+  border-bottom: 1px solid #e5e5e5;
+  &:last-child {
+    border-bottom: none;
+    padding-bottom: 0;
+  }
+  &:first-child {
+    padding-top: 0;
+  }
+}

+ 23 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/handle-progress/handle-progress.component.spec.ts

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

+ 31 - 0
src/app/pages/manager/hazard/hazard-tracking/detail/handle-progress/handle-progress.component.ts

@@ -0,0 +1,31 @@
+import { Component } from '@angular/core';
+import { CommonNzModule } from '../../../../../../common.nz.module';
+import { ScrollImagesComponent } from '../../../../../../shared/scroll-images/scroll-images.component';
+
+@Component({
+  selector: 'hazard-handle-progress',
+  standalone: true,
+  imports: [CommonNzModule, ScrollImagesComponent],
+  templateUrl: './handle-progress.component.html',
+  styleUrl: './handle-progress.component.less',
+})
+export class HandleProgressComponent {
+  data = [
+    {
+      description: '隐患整改',
+      time: '2024-01-01 12:00',
+      status: '已完成',
+      creator: '张三',
+      images: Array.from({ length: 11 }).map(() => `https://loremflickr.com/260/150?q=${Math.random()}`),
+    },
+
+    {
+      description:
+        '事故隐患分为一般事故隐患和重大事故隐患。一般事故隐患,是指危害和整改难度较小,发现后能够立即整改排除的隐患。重大事故隐患,是指危害和整改难度较大,应当全部或者局部停产停业,并经过一定时间整改治理方能排除的隐患,或者因外部因素影响致使生产经营单位自身难以排除的隐患。',
+      time: '2024-11-23 17:40',
+      status: '已完成',
+      creator: '李酒鸿',
+      images: Array.from({ length: 11 }).map(() => `https://loremflickr.com/260/150?q=${Math.random()}`),
+    },
+  ];
+}

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

@@ -1 +1,27 @@
-<p>hazard-tracking works!</p>
+<div class="shared-panel">
+  <div class="shared-panel-header">
+    <div class="title w-1/5">隐患列表</div>
+    <div class="flex-1">123</div>
+    <div class="pr-4">
+      <button nz-button class="precaution-button add-button" nzType="primary">
+        <span nz-icon nzType="plus" class="text-xs -mr-1"></span>
+        <span class="text-base leading-normal">新增隐患</span>
+      </button>
+    </div>
+  </div>
+  <div class="hazard-tracking-content custom-scroll-bar">
+    <div class="grid grid-cols-4 gap-4">
+      @for (hazard of hazards; track hazard.id) {
+        <hazard-card [data]="hazard" (onViewDetail)="handleViewDetail($event)"></hazard-card>
+      }
+    </div>
+  </div>
+</div>
+
+@if (detailConfig.visible) {
+  <hazard-detail
+    @horizontalInOutReverse300ms
+    [data]="detailConfig.data"
+    (onClose)="handleCloseDetail()"
+  ></hazard-detail>
+}

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

@@ -0,0 +1,14 @@
+.add-button {
+  padding: 20px 36px;
+  box-shadow:
+    0px 0px 8px 0px rgba(223, 223, 223, 0.5),
+    0px 4px 20px 0px rgba(0, 0, 0, 0.33);
+  border-radius: 18px;
+  background-color: var(--deep-blue);
+}
+
+.hazard-tracking-content {
+  height: calc(100vh - var(--header-height) - 146px);
+  padding: 12px 16px;
+  overflow-y: auto;
+}

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

@@ -1,12 +1,56 @@
 import { Component } from '@angular/core';
+import { horizontalInOutReverse300ms } from '../../../../common.animation';
+import { CommonNzModule } from '../../../../common.nz.module';
+import { HazardCardComponent } from './card/card.component';
+import { HazardDetailComponent } from './detail/detail.component';
+
+export interface HazardDetailConfig {
+  visible: boolean;
+  data: Hazard.HazardDto;
+}
+
+const hazardLevels = ['重大', 'A', 'B', 'C'];
+const departments = Array.from({ length: 15 }, (_, i) => i + 1);
+
+const getMockHazard = (index: number): Hazard.HazardDto => {
+  return {
+    id: index,
+    type: 1,
+    category: 1,
+    level: hazardLevels[Math.floor(Math.random() * hazardLevels.length)],
+    name: `隐患问题隐患问题隐患问题${index}`,
+    description: '隐患1描述',
+    status: index % 3,
+    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()}`),
+  };
+};
 
 @Component({
   selector: 'app-hazard-tracking',
   standalone: true,
-  imports: [],
+  imports: [CommonNzModule, HazardCardComponent, HazardDetailComponent],
   templateUrl: './hazard-tracking.component.html',
-  styleUrl: './hazard-tracking.component.less'
+  styleUrl: './hazard-tracking.component.less',
+  animations: [horizontalInOutReverse300ms],
 })
 export class HazardTrackingComponent {
-
+  hazards: Hazard.HazardDto[] = [];
+  detailConfig: HazardDetailConfig = {
+    visible: false,
+    data: {} as Hazard.HazardDto,
+  };
+  ngOnInit() {
+    this.hazards = Array.from({ length: 2 }, (_, i) => getMockHazard(i));
+  }
+  handleViewDetail(data: Hazard.HazardDto) {
+    this.detailConfig.visible = true;
+    this.detailConfig.data = data;
+  }
+  handleCloseDetail() {
+    this.detailConfig.visible = false;
+  }
 }

+ 1 - 0
src/app/pages/manager/hazard/hazard.route.ts

@@ -19,4 +19,5 @@ export const hazardRoutes: Routes = [
       weight: 2,
     },
   },
+  { path: '', pathMatch: 'full', redirectTo: 'hazard-tracking' },
 ];

+ 4 - 18
src/app/pages/manager/layout/header/header.component.ts

@@ -25,23 +25,18 @@ export interface IPath {
 export class HeaderComponent {
   @ViewChild('issueReportForm') issueReportForm!: IssueReportFormComponent;
   menuList: IPath[] = [
-    // {
-    //   path: '/manager/dashboard',
-    //   label: '风险看板',
-    //   icon: 'assets/images/nav/dashboard.png',
-    // },
     {
-      path: '/manager/workbench/my',
+      path: '/manager/workbench',
       label: '我的工作台',
       icon: 'assets/images/nav/workbench.png',
     },
     {
-      path: '/manager/risk/risk-knowledge',
+      path: '/manager/risk',
       label: '风险管控',
       icon: 'assets/images/nav/knowledge.png',
     },
     {
-      path: '/manager/potential-hazard/hazard-tracking',
+      path: '/manager/potential-hazard',
       label: '隐患管理',
       icon: 'assets/images/nav/potential-hazard.png',
     },
@@ -51,15 +46,10 @@ export class HeaderComponent {
       icon: 'assets/images/nav/user.png',
     },
     {
-      path: '/manager/basic-data/line',
+      path: '/manager/basic-data',
       label: '基础数据管理',
       icon: 'assets/images/nav/basic-data.png',
     },
-    // {
-    //   path: '/manager/risk-knowledge/list',
-    //   label: '风险知识管理',
-    //   icon: 'assets/images/nav/knowledge.png',
-    // },
   ];
   issueReport = {
     visible: false,
@@ -70,10 +60,6 @@ export class HeaderComponent {
     private readonly basic: BasicDataService,
     private readonly router: Router
   ) {}
-  // toCenter(ev: MouseEvent) {
-  //   ev.preventDefault();
-  //   this.router.navigate(['/manager/center']);
-  // }
   handleLogout(ev: MouseEvent) {
     ev.preventDefault();
     this.setting.clear();

+ 1 - 0
src/app/pages/manager/risk/risk.route.ts

@@ -46,4 +46,5 @@ export const riskRoutes: Routes = [
       weight: 5,
     },
   },
+  { path: '', pathMatch: 'full', redirectTo: 'risk-knowledge' },
 ];

+ 1 - 12
src/app/pages/manager/workbench/problem-assessment/problem-assessment.component.ts

@@ -1,5 +1,5 @@
-import { animate, state, style, transition, trigger } from '@angular/animations';
 import { Component } from '@angular/core';
+import { horizontalInOutReverse300ms } from '../../../../common.animation';
 import { CommonNzModule } from '../../../../common.nz.module';
 import { FilterButtonGroupComponent } from '../../../../shared/filter-button-group/filter-button-group.component';
 import { ProblemAssessmentDetailComponent, ProblemAssessmentDetailConfig } from './detail/detail.component';
@@ -28,17 +28,6 @@ const getMockData = (): ProblemAssessment.ProblemAssessmentDto => {
   };
 };
 
-const horizontalInOutReverse300ms = trigger('horizontalInOutReverse300ms', [
-  state('in', style({ transform: 'translate(0, 0%)', opacity: 1 })),
-  transition('void => *', [
-    style({ transform: `translate(-100%, 0)` }),
-    animate('300ms cubic-bezier(0.35, 0, 0.25, 1)', style({ transform: 'translate(0, 0%)' })),
-  ]),
-  transition('* => void', [
-    animate('300ms cubic-bezier(0.35, 0, 0.25, 1)', style({ transform: `translate(-100%, 0)` })),
-  ]),
-]);
-
 @Component({
   selector: 'app-problem-assessment',
   standalone: true,

+ 1 - 0
src/app/pages/manager/workbench/workbench.route.ts

@@ -21,4 +21,5 @@ export const workbenchRoutes: Routes = [
       weight: 2,
     },
   },
+  { path: '', pathMatch: 'full', redirectTo: 'my' },
 ];

+ 3 - 3
src/app/shared/data-empty/data-empty.component.html

@@ -2,11 +2,11 @@
   <div class="pt-6">
     <nz-empty [nzNotFoundImage]="emptyImage" [nzNotFoundContent]="emptyContent"></nz-empty>
     <ng-template #emptyImage>
-      <img style="height: 164px" src="/assets/images/empty.png" alt="" />
+      <img style="width: 115px" src="/assets/icons/data-empty.svg" alt="" />
     </ng-template>
     <ng-template #emptyContent>
-      <div class="pt-12">
-        <span class="text-[#666666]">{{ text }}</span>
+      <div [style.padding-top]="textPaddingTop">
+        <span [style.color]="textColor">{{ text }}</span>
       </div>
     </ng-template>
   </div>

+ 2 - 0
src/app/shared/data-empty/data-empty.component.ts

@@ -11,4 +11,6 @@ import { CommonNzModule } from '../../common.nz.module';
 export class DataEmptyComponent {
   @Input() isEmpty = false;
   @Input() text = '暂无数据...';
+  @Input() textPaddingTop = 12;
+  @Input() textColor = 'white';
 }

+ 19 - 0
src/app/shared/scroll-images/scroll-images.component.html

@@ -0,0 +1,19 @@
+<div class="relative outer-container">
+  <div #outerContainer class="overflow-hidden">
+    <div #innerContainer class="text-nowrap inline-block" [style.height]="height">
+      @for (item of data; track item) {
+        <div class="inline-block bg-gray-300 rounded-lg overflow-hidden mr-3 last:mr-0 align-bottom">
+          <img nz-image [nzSrc]="item" alt="" [width]="width" [height]="height" />
+        </div>
+      }
+    </div>
+  </div>
+  @if (directorVisible) {
+    <div class="left-arrow" (click)="handleScroll('left')">
+      <i nz-icon nzType="left" nzTheme="outline"></i>
+    </div>
+    <div class="right-arrow" (click)="handleScroll('right')">
+      <i nz-icon nzType="right" nzTheme="outline"></i>
+    </div>
+  }
+</div>

+ 36 - 0
src/app/shared/scroll-images/scroll-images.component.less

@@ -0,0 +1,36 @@
+.outer-container {
+  &:hover {
+    .left-arrow,
+    .right-arrow {
+      visibility: visible;
+    }
+  }
+}
+
+.arrow {
+  width: 32px;
+  height: 32px;
+  border-radius: 50%;
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: rgba(214, 214, 214, 0.575);
+  color: rgba(0, 0, 0, 0.7);
+  cursor: pointer;
+  visibility: hidden;
+  &:hover {
+    background-color: rgba(214, 214, 214, 0.9);
+    color: var(--ant-primary-color);
+  }
+}
+.left-arrow {
+  .arrow();
+  left: 2px;
+}
+.right-arrow {
+  .arrow();
+  right: 2px;
+}

+ 23 - 0
src/app/shared/scroll-images/scroll-images.component.spec.ts

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

+ 59 - 0
src/app/shared/scroll-images/scroll-images.component.ts

@@ -0,0 +1,59 @@
+import { Component, ElementRef, Input, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core';
+import { CommonNzModule } from '../../common.nz.module';
+
+@Component({
+  selector: 'scroll-images',
+  standalone: true,
+  imports: [CommonNzModule],
+  templateUrl: './scroll-images.component.html',
+  styleUrl: './scroll-images.component.less',
+})
+export class ScrollImagesComponent {
+  @ViewChild('innerContainer') innerContainer!: ElementRef<HTMLDivElement>;
+  @ViewChild('outerContainer') outerContainer!: ElementRef<HTMLDivElement>;
+  @Input() data: string[] = [];
+  @Input() width = 154;
+  @Input() height = 88;
+
+  directorVisible = false;
+
+  constructor(private viewContainerRef: ViewContainerRef) {}
+
+  ngAfterViewInit() {
+    this.setDirectorVisible();
+  }
+
+  ngOnChanges(changes: SimpleChanges) {
+    if (changes['data']) {
+      this.setDirectorVisible();
+    }
+  }
+
+  setDirectorVisible() {
+    setTimeout(() => {
+      if (this.outer.scrollWidth > this.outer.offsetWidth) {
+        this.directorVisible = true;
+      }
+    });
+  }
+
+  handleScroll(direction: 'left' | 'right') {
+    const scrollLeft =
+      direction === 'left' ? this.outer.scrollLeft - this.width * 3 : this.outer.scrollLeft + this.width * 3;
+    console.log(direction, scrollLeft);
+    this.outer.scrollTo({
+      left: scrollLeft,
+      behavior: 'smooth',
+    });
+  }
+
+  get dom() {
+    return this.viewContainerRef.element.nativeElement;
+  }
+  get container() {
+    return this.innerContainer.nativeElement;
+  }
+  get outer() {
+    return this.outerContainer.nativeElement;
+  }
+}

+ 100 - 0
src/assets/icons/data-empty.svg

@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="115px" height="94px" viewBox="0 0 115 94" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title></title>
+    <defs>
+        <linearGradient x1="49.6145636%" y1="-16.2812278%" x2="50.6388307%" y2="159.311199%" id="linearGradient-1">
+            <stop stop-color="#CAE0FF" stop-opacity="0" offset="0%"></stop>
+            <stop stop-color="#BCD8FF" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="50%" y1="-3.06161713e-15%" x2="50%" y2="100%" id="linearGradient-2">
+            <stop stop-color="#EDF4FF" offset="0%"></stop>
+            <stop stop-color="#ABCCFF" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="50%" y1="-1.9142844e-14%" x2="50%" y2="100%" id="linearGradient-3">
+            <stop stop-color="#ABCCFF" offset="0%"></stop>
+            <stop stop-color="#70A8FF" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="50%" y1="3.06161713e-15%" x2="50%" y2="100%" id="linearGradient-4">
+            <stop stop-color="#70A8FF" offset="0%"></stop>
+            <stop stop-color="#5597FD" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="89.1472936%" y1="44.4395164%" x2="23.6434132%" y2="22.259628%" id="linearGradient-5">
+            <stop stop-color="#C6DDFF" offset="0%"></stop>
+            <stop stop-color="#FFFFFF" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="89.1472936%" y1="44.729899%" x2="23.6434132%" y2="23.7083012%" id="linearGradient-6">
+            <stop stop-color="#C6DDFF" offset="0%"></stop>
+            <stop stop-color="#FFFFFF" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="-11.4583336%" y1="18.75%" x2="144.791663%" y2="154.166663%" id="linearGradient-7">
+            <stop stop-color="#94BFFF" offset="0%"></stop>
+            <stop stop-color="#4C94FE" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="50%" y1="3.06161713e-15%" x2="50%" y2="100%" id="linearGradient-8">
+            <stop stop-color="#70A8FF" offset="0%"></stop>
+            <stop stop-color="#5597FD" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="50%" y1="3.06161713e-15%" x2="50%" y2="100%" id="linearGradient-9">
+            <stop stop-color="#70A8FF" offset="0%"></stop>
+            <stop stop-color="#5597FD" offset="100%"></stop>
+        </linearGradient>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="隐患管理-隐患追踪" transform="translate(-1539.000000, -741.000000)">
+            <g id="编组" transform="translate(1539.000000, 741.000000)">
+                <path d="M104,47 C104,21.0426221 82.9575418,0 57,0 C31.0426221,0 10,21.0426221 10,47 C10,72.9575418 31.0426221,94 57,94 C82.9575418,94 104,72.9575418 104,47 Z" id="路径" fill="url(#linearGradient-1)" fill-rule="nonzero"></path>
+                <path d="M96,31.8508386 C96,31.8508386 55.4999176,60 55.4999176,60 C55.4999176,60 15,31.8508386 15,31.8508386 C15,31.8508386 52.0744905,6.07208265 52.0744905,6.07208265 C53.0799549,5.37416956 54.2760298,5 55.5015658,5 C56.7270605,5 57.9232179,5.37416956 58.9285998,6.07208265 C58.9285998,6.07208265 66.3239259,11.2211399 66.3239259,11.2211399 C66.3239259,11.2211399 68.2605223,12.5602943 68.2605223,12.5602943 C68.2605223,12.5602943 87.7571039,26.119268 87.7571039,26.119268 C87.7571039,26.119268 89.8111323,27.5555154 89.8111323,27.5555154 C89.8111323,27.5555154 96,31.8508386 96,31.8508386 C96,31.8508386 96,31.8508386 96,31.8508386 Z" id="路径" fill="url(#linearGradient-2)" fill-rule="nonzero"></path>
+                <path d="M49,55.7753764 C49,55.7753764 15,80 15,80 C15,80 15,32 15,32 C15,32 49,55.7753764 49,55.7753764 C49,55.7753764 49,55.7753764 49,55.7753764 Z" id="路径" fill="url(#linearGradient-3)" fill-rule="nonzero"></path>
+                <path d="M62,55.7753764 C62,55.7753764 96,80 96,80 C96,80 96,32 96,32 C96,32 62,55.7753764 62,55.7753764 C62,55.7753764 62,55.7753764 62,55.7753764 Z" id="路径" fill="url(#linearGradient-3)" fill-rule="nonzero"></path>
+                <path d="M15,80 C15,80 52.0307729,53.1322541 52.0307729,53.1322541 C53.044231,52.3957741 54.2574468,52 55.5015658,52 C56.7456023,52 57.9590654,52.3957741 58.9722762,53.1322541 C58.9722762,53.1322541 96,80 96,80 C96,80 15,80 15,80 C15,80 15,80 15,80 Z" id="路径" fill="url(#linearGradient-4)" fill-rule="nonzero"></path>
+                <g id="路径" transform="translate(23.000000, 36.500000) scale(-1, 1) translate(-23.000000, -36.500000) translate(0.000000, 23.000000)" fill="url(#linearGradient-5)" fill-rule="nonzero">
+                    <path d="M26.4143072,0 C32.5839713,2.90307059 39.2138035,4.6901414 46,5.27926118 C45.0828579,7.42334582 38.5677915,18.1558364 19.2275979,26.9991404 C18.7047859,27.0800703 6.62770296,21.4286142 0,17.5530842 C0.225213664,17.8767212 20.5869336,10.3401148 26.4143072,0 C26.4143072,0 26.4143072,0 26.4143072,0 Z"></path>
+                </g>
+                <g id="路径" transform="translate(13.966169, 30.646112) scale(-1, 1) rotate(19.172732) translate(-13.966169, -30.646112) translate(7.521717, 29.974751)" fill="#C9DFFF" fill-rule="nonzero">
+                    <path d="M0.0780230154,0.702297606 C0.281661949,0.422779127 0.60409429,0.254324732 0.949842445,0.246811356 C0.949842445,0.246811356 12.3013946,0.000142064227 12.3013946,0.000142064227 C12.7080414,-0.00869422727 12.9948332,0.396244233 12.8514988,0.7768946 C12.8514988,0.7768946 12.8514988,0.7768946 12.8514988,0.7768946 C12.7361101,1.08342043 12.4434997,1.28692004 12.1159368,1.28843616 C12.1159368,1.28843616 0.405672024,1.34272244 0.405672024,1.34272244 C0.0752366404,1.34424561 -0.116555693,0.969371645 0.0780230154,0.702297606 C0.0780230154,0.702297606 0.0780230154,0.702297606 0.0780230154,0.702297606 C0.0780230154,0.702297606 0.0780230154,0.702297606 0.0780230154,0.702297606 Z"></path>
+                </g>
+                <g id="路径" transform="translate(15.423441, 34.367801) scale(-1, 1) rotate(19.172732) translate(-15.423441, -34.367801) translate(10.758980, 33.783480)" fill="#C9DFFF" fill-rule="nonzero">
+                    <path d="M0.0428110519,0.625725565 C0.21712134,0.29273532 0.560434285,0.0825910214 0.936271189,0.0788367909 C0.936271189,0.0788367909 8.82674412,2.59580244e-05 8.82674412,2.59580244e-05 C9.17471317,-0.00344921666 9.4183161,0.342622496 9.29764147,0.668984037 C9.29764147,0.668984037 9.29764147,0.668984037 9.29764147,0.668984037 C9.19909396,0.935619629 8.94565676,1.11332889 8.66140555,1.11516052 C8.66140555,1.11516052 0.373801662,1.16864241 0.373801662,1.16864241 C0.0940160182,1.17042908 -0.0869475605,0.873606397 0.0428110519,0.625725565 C0.0428110519,0.625725565 0.0428110519,0.625725565 0.0428110519,0.625725565 C0.0428110519,0.625725565 0.0428110519,0.625725565 0.0428110519,0.625725565 Z"></path>
+                </g>
+                <g id="路径" transform="translate(18.891686, 36.851736) scale(-1, 1) rotate(20.475484) translate(-18.891686, -36.851736) translate(13.582981, 36.267074)" fill="#C9DFFF" fill-rule="nonzero">
+                    <path d="M0.0545559205,0.591932579 C0.242308831,0.277410671 0.580430516,0.0835043794 0.946715893,0.0802963604 C0.946715893,0.0802963604 10.1122464,2.01881507e-05 10.1122464,2.01881507e-05 C10.4719756,-0.00313052504 10.7173405,0.363087195 10.577571,0.694581813 C10.577571,0.694581813 10.577571,0.694581813 10.577571,0.694581813 C10.4706643,0.948191106 10.2228818,1.1136567 9.94764537,1.1152138 C9.94764537,1.1152138 0.384479515,1.16932342 0.384479515,1.16932342 C0.0871921567,1.17099047 -0.0978280577,0.847201403 0.0545559205,0.591932579 C0.0545559205,0.591932579 0.0545559205,0.591932579 0.0545559205,0.591932579 C0.0545559205,0.591932579 0.0545559205,0.591932579 0.0545559205,0.591932579 Z"></path>
+                </g>
+                <g id="路径" transform="translate(23.707548, 42.602171) scale(-1, 1) rotate(21.690609) translate(-23.707548, -42.602171) translate(20.372097, 42.019053)" fill="#C9DFFF" fill-rule="nonzero">
+                    <path d="M0.0222450505,0.696622417 C0.160130085,0.326758188 0.5109351,0.0794817547 0.905629208,0.073946293 C0.905629208,0.073946293 6.17464783,4.99700601e-05 6.17464783,4.99700601e-05 C6.49819513,-0.00448766201 6.73688092,0.300751066 6.65451897,0.613666299 C6.65451897,0.613666299 6.65451897,0.613666299 6.65451897,0.613666299 C6.57731999,0.90690358 6.31343388,1.11221434 6.01021073,1.11496384 C6.01021073,1.11496384 0.351507331,1.16622084 0.351507331,1.16622084 C0.107463501,1.16842945 -0.0630073281,0.925305947 0.0222450505,0.696622417 C0.0222450505,0.696622417 0.0222450505,0.696622417 0.0222450505,0.696622417 C0.0222450505,0.696622417 0.0222450505,0.696622417 0.0222450505,0.696622417 Z"></path>
+                </g>
+                <g id="路径" transform="translate(59.000000, 38.000000)" fill-rule="nonzero">
+                    <path d="M32.1567655,0 C39.6676771,3.44067251 47.7386753,5.55869039 56,6.25692832 C54.8839381,8.79803932 46.9522935,21.5180462 23.407662,31.9989818 C22.7711985,32.0948712 8.06857278,25.3968594 0,20.8036658 C0.27417469,21.1872236 25.0625167,12.2549309 32.1567655,0 C32.1567655,0 32.1567655,0 32.1567655,0 Z" fill="url(#linearGradient-6)"></path>
+                    <g id="编组" transform="translate(38.902877, 8.780277) rotate(19.172732) translate(-38.902877, -8.780277) translate(31.063586, 7.980520)" fill="#C9DFFF">
+                        <path d="M0.0949079515,0.83660937 C0.342624363,0.50363302 0.73484551,0.302963191 1.15543025,0.294013106 C1.15543025,0.294013106 14.963903,0.000169234293 14.963903,0.000169234293 C15.4585876,-0.0103569869 15.8074238,0.472024118 15.6330879,0.925470156 C15.6330879,0.925470156 15.6330879,0.925470156 15.6330879,0.925470156 C15.4926655,1.29061899 15.1367178,1.53303576 14.7383064,1.53484324 C14.7383064,1.53484324 0.493480169,1.59951331 0.493480169,1.59951331 C0.0915248246,1.6013258 -0.141782673,1.15475949 0.0949079515,0.83660937 C0.0949079515,0.83660937 0.0949079515,0.83660937 0.0949079515,0.83660937 C0.0949079515,0.83660937 0.0949079515,0.83660937 0.0949079515,0.83660937 Z" id="路径"></path>
+                    </g>
+                    <g id="编组" transform="translate(33.223003, 11.513088) rotate(19.172732) translate(-33.223003, -11.513088) translate(27.548964, 10.817018)" fill="#C9DFFF">
+                        <path d="M0.0520770951,0.745393616 C0.264112824,0.348720014 0.681739074,0.0983861284 1.1389216,0.0939141048 C1.1389216,0.0939141048 10.7372142,3.09226561e-05 10.7372142,3.09226561e-05 C11.1604545,-0.00410887527 11.4567967,0.408146119 11.3100438,0.796929168 C11.3100438,0.796929168 11.3100438,0.796929168 11.3100438,0.796929168 C11.1901339,1.11455596 10.8818706,1.32624945 10.5360764,1.32843534 C10.5360764,1.32843534 0.454707832,1.39213954 0.454707832,1.39213954 C0.114364859,1.39426978 -0.105766532,1.04068243 0.0520770951,0.745393616 C0.0520770951,0.745393616 0.0520770951,0.745393616 0.0520770951,0.745393616 C0.0520770951,0.745393616 0.0520770951,0.745393616 0.0520770951,0.745393616 Z" id="路径"></path>
+                    </g>
+                    <g id="编组" transform="translate(30.105819, 14.777885) rotate(20.475484) translate(-30.105819, -14.777885) translate(23.648098, 14.081408)" fill="#C9DFFF">
+                        <path d="M0.0663638786,0.70513776 C0.294755772,0.33046479 0.706062199,0.0994746474 1.15162371,0.0956531585 C1.15162371,0.0956531585 12.3009341,2.4049165e-05 12.3009341,2.4049165e-05 C12.7385209,-0.00372924665 13.0370006,0.432529097 12.866981,0.827418965 C12.866981,0.827418965 12.866981,0.827418965 12.866981,0.827418965 C12.7369588,1.12952712 12.4355193,1.32663592 12.1007007,1.32849172 C12.1007007,1.32849172 0.467697684,1.39295277 0.467697684,1.39295277 C0.106065263,1.39493804 -0.119002288,1.00922651 0.0663638786,0.70513776 C0.0663638786,0.70513776 0.0663638786,0.70513776 0.0663638786,0.70513776 C0.0663638786,0.70513776 0.0663638786,0.70513776 0.0663638786,0.70513776 Z" id="路径"></path>
+                    </g>
+                    <g id="编组" transform="translate(19.402874, 19.621778) rotate(21.690609) translate(-19.402874, -19.621778) translate(15.345504, 18.927142)" fill="#C9DFFF">
+                        <path d="M0.0270594979,0.829850413 C0.194788433,0.389250019 0.621525283,0.0946825889 1.10164566,0.0880886789 C1.10164566,0.0880886789 7.51107609,5.95263519e-05 7.51107609,5.95263519e-05 C7.90463692,-0.00534589816 8.19501856,0.358268705 8.09479908,0.7310263 C8.09479908,0.7310263 8.09479908,0.7310263 8.09479908,0.7310263 C8.000869,1.08034653 7.67990357,1.32492101 7.31104821,1.32819381 C7.31104821,1.32819381 0.42758949,1.3892539 0.42758949,1.3892539 C0.130723602,1.39188664 -0.0766443982,1.10226585 0.0270594979,0.829850413 C0.0270594979,0.829850413 0.0270594979,0.829850413 0.0270594979,0.829850413 C0.0270594979,0.829850413 0.0270594979,0.829850413 0.0270594979,0.829850413 Z" id="路径"></path>
+                    </g>
+                    <g id="编组" transform="translate(27.113244, 18.110628) rotate(20.475484) translate(-27.113244, -18.110628) translate(18.871526, 17.413569)" fill="#C9DFFF">
+                        <path d="M0.102311182,0.618623891 C0.356629421,0.293175383 0.750281515,0.101115074 1.16874923,0.0983152786 C1.16874923,0.0983152786 15.8607402,1.50037025e-05 15.8607402,1.50037025e-05 C16.3303495,-0.00312682571 16.6315423,0.487780266 16.4082889,0.892343617 C16.4082889,0.892343617 16.4082889,0.892343617 16.4082889,0.892343617 C16.2606316,1.15992835 15.9755528,1.3272156 15.6649875,1.32856015 C15.6649875,1.32856015 0.495646749,1.39411891 0.495646749,1.39411891 C0.0865547722,1.395876 -0.146304659,0.936778035 0.102311182,0.618623891 C0.102311182,0.618623891 0.102311182,0.618623891 0.102311182,0.618623891 C0.102311182,0.618623891 0.102311182,0.618623891 0.102311182,0.618623891 Z" id="路径"></path>
+                    </g>
+                </g>
+                <g id="路径" transform="translate(23.562098, 38.835496) scale(-1, 1) rotate(20.475484) translate(-23.562098, -38.835496) translate(16.786831, 38.250343)" fill="#C9DFFF" fill-rule="nonzero">
+                    <path d="M0.0841064014,0.519310633 C0.293174671,0.246108207 0.616784668,0.0848815862 0.960795438,0.082531197 C0.960795438,0.082531197 13.0386368,1.25953068e-05 13.0386368,1.25953068e-05 C13.4246727,-0.00262486356 13.6722913,0.409468045 13.4887593,0.749088224 C13.4887593,0.749088224 13.4887593,0.749088224 13.4887593,0.749088224 C13.3673881,0.973711022 13.1330458,1.11414432 12.8777237,1.11527116 C12.8777237,1.11527116 0.407457021,1.17030648 0.407457021,1.17030648 C0.0711538555,1.1717813 -0.120272153,0.786384672 0.0841064014,0.519310633 C0.0841064014,0.519310633 0.0841064014,0.519310633 0.0841064014,0.519310633 C0.0841064014,0.519310633 0.0841064014,0.519310633 0.0841064014,0.519310633 Z"></path>
+                </g>
+                <path d="M93.5,13 C94.8807052,13 96,11.8807052 96,10.5 C96,9.11928664 94.8807052,8 93.5,8 C92.1192866,8 91,9.11928664 91,10.5 C91,11.8807052 92.1192866,13 93.5,13 Z M94.5101534,9.48984655 C94.7891,9.76879304 94.9285732,10.1055095 94.9285732,10.4999959 C94.9285732,10.894485 94.7890986,11.2312029 94.5101493,11.5101494 C94.2312029,11.7890986 93.894485,11.9285732 93.4999959,11.9285732 C93.1055095,11.9285732 92.768793,11.7890999 92.4898466,11.5101534 C92.2109,11.2312042 92.0714268,10.894485 92.0714268,10.4999959 C92.0714268,10.1055067 92.2109,9.7687903 92.4898466,9.48984655 C92.7687904,9.21090005 93.1055068,9.07142682 93.4999959,9.07142682 C93.894485,9.07142682 94.2312043,9.21090005 94.5101534,9.48984655 Z" id="形状" fill="url(#linearGradient-7)" fill-rule="nonzero"></path>
+                <circle id="椭圆形" stroke="url(#linearGradient-7)" stroke-width="2.61275935" cx="24" cy="84" r="2"></circle>
+                <circle id="椭圆形" stroke="url(#linearGradient-7)" stroke-width="2.61275935" cx="84" cy="88" r="2"></circle>
+                <path d="M30.1344315,12.6814767 C30.3537763,11.7728411 31.6462022,11.7728411 31.8655471,12.6814767 C31.8655471,12.6814767 32.6037536,15.7396048 32.6037536,15.7396048 C32.6821367,16.0643408 32.935689,16.3178418 33.2603822,16.3962249 C33.2603822,16.3962249 36.3185146,17.1344315 36.3185146,17.1344315 C37.2271618,17.3537763 37.2271618,18.6462022 36.3185146,18.865547 C36.3185146,18.865547 33.2603822,19.6037536 33.2603822,19.6037536 C32.935689,19.6821367 32.6821367,19.935689 32.6037536,20.2603822 C32.6037536,20.2603822 31.8655471,23.3185146 31.8655471,23.3185146 C31.6462022,24.2271618 30.3537763,24.2271618 30.1344315,23.3185146 C30.1344315,23.3185146 29.3962249,20.2603822 29.3962249,20.2603822 C29.3178418,19.935689 29.0643407,19.6821367 28.7396048,19.6037536 C28.7396048,19.6037536 25.6814767,18.865547 25.6814767,18.865547 C24.7728411,18.6462022 24.7728411,17.3537763 25.6814767,17.1344315 C25.6814767,17.1344315 28.7396048,16.3962249 28.7396048,16.3962249 C29.0643407,16.3178418 29.3178418,16.0643408 29.3962249,15.7396048 C29.3962249,15.7396048 30.1344315,12.6814767 30.1344315,12.6814767 C30.1344315,12.6814767 30.1344315,12.6814767 30.1344315,12.6814767 Z" id="路径" fill="url(#linearGradient-8)" fill-rule="nonzero"></path>
+                <path d="M102.134431,75.6814767 C102.353776,74.7728411 103.646202,74.7728411 103.865547,75.6814767 C103.865547,75.6814767 104.603754,78.7396048 104.603754,78.7396048 C104.682137,79.0643407 104.935689,79.3178418 105.260382,79.3962249 C105.260382,79.3962249 108.318515,80.1344315 108.318515,80.1344315 C109.227162,80.3537763 109.227162,81.6462022 108.318515,81.8655471 C108.318515,81.8655471 105.260382,82.6037536 105.260382,82.6037536 C104.935689,82.6821367 104.682137,82.935689 104.603754,83.2603822 C104.603754,83.2603822 103.865547,86.3185146 103.865547,86.3185146 C103.646202,87.2271618 102.353776,87.2271618 102.134431,86.3185146 C102.134431,86.3185146 101.396225,83.2603822 101.396225,83.2603822 C101.317842,82.935689 101.064341,82.6821367 100.739605,82.6037536 C100.739605,82.6037536 97.6814767,81.8655471 97.6814767,81.8655471 C96.7728411,81.6462022 96.7728411,80.3537763 97.6814767,80.1344315 C97.6814767,80.1344315 100.739605,79.3962249 100.739605,79.3962249 C101.064341,79.3178418 101.317842,79.0643407 101.396225,78.7396048 C101.396225,78.7396048 102.134431,75.6814767 102.134431,75.6814767 C102.134431,75.6814767 102.134431,75.6814767 102.134431,75.6814767 Z" id="路径" fill="url(#linearGradient-8)" fill-rule="nonzero"></path>
+                <g id="路径" transform="translate(9.628984, 70.068677) rotate(50.901743) translate(-9.628984, -70.068677) translate(7.059834, 67.749020)" fill="url(#linearGradient-9)" fill-rule="nonzero">
+                    <path d="M1.95103214,0.356870098 C2.22575231,-0.118956699 2.91254458,-0.118956699 3.18727296,0.356870098 C3.18727296,0.356870098 5.04161781,3.56867415 5.04161781,3.56867415 C5.31632159,4.04452963 4.97294186,4.63931414 4.42349329,4.63931414 C4.42349329,4.63931414 0.714799509,4.63931414 0.714799509,4.63931414 C0.165368568,4.63931414 -0.17803166,4.04452963 0.0966885226,3.56867415 C0.0966885226,3.56867415 1.95103214,0.356870098 1.95103214,0.356870098 C1.95103214,0.356870098 1.95103214,0.356870098 1.95103214,0.356870098 Z"></path>
+                </g>
+                <g id="路径" transform="translate(105.220620, 34.005753) rotate(-44.303822) translate(-105.220620, -34.005753) translate(102.651470, 31.686096)" fill="url(#linearGradient-9)" fill-rule="nonzero">
+                    <path d="M1.95103216,0.356870102 C2.22575235,-0.118956701 2.91254462,-0.118956701 3.187273,0.356870102 C3.187273,0.356870102 5.04161786,3.56867418 5.04161786,3.56867418 C5.31632166,4.04452966 4.97294191,4.63931419 4.42349335,4.63931419 C4.42349335,4.63931419 0.714799518,4.63931419 0.714799518,4.63931419 C0.16536857,4.63931419 -0.178031662,4.04452966 0.0966885237,3.56867418 C0.0966885237,3.56867418 1.95103216,0.356870102 1.95103216,0.356870102 C1.95103216,0.356870102 1.95103216,0.356870102 1.95103216,0.356870102 Z"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 22 - 0
src/assets/icons/hazard-level.svg

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="59px" height="59px" viewBox="0 0 59 59" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title></title>
+    <defs>
+        <filter x="-29.3%" y="-29.3%" width="158.5%" height="158.5%" filterUnits="objectBoundingBox" id="filter-1">
+            <feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.710091853   0 0 0 0 0.710091853   0 0 0 0 0.710091853  0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixOuter1"></feMergeNode>
+                <feMergeNode in="SourceGraphic"></feMergeNode>
+            </feMerge>
+        </filter>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="隐患管理-隐患追踪" transform="translate(-1535.000000, -216.000000)" fill="currentColor" fill-rule="nonzero">
+            <g id="编组" transform="translate(1544.000000, 225.000000)" filter="url(#filter-1)">
+                <path d="M20.5,0 C27.8239444,0 34.5915484,3.90727779 38.2535207,10.2499999 C41.9154931,16.592722 41.9154931,24.407278 38.2535207,30.7500001 C34.5915484,37.0927222 27.8239444,41 20.5,41 C9.17816244,41 0,31.8218373 0,20.5 C0,9.17816275 9.17816244,0 20.5,0 L20.5,0 Z M20.4871872,11.275 C19.2828122,11.275 11.7183122,12.766375 11.7183122,12.766375 C11.5240989,12.8095488 11.3677501,12.9531943 11.3083122,13.1430625 L11.2826872,13.2814375 L11.2826872,21.0714375 C11.2826872,23.7774375 13.0277497,27.1496875 15.2545622,28.8563125 L16.3692497,29.637875 C17.4993122,30.4015 19.6569372,31.775 20.5409997,31.775 C21.4224997,31.775 23.5391247,30.4143125 24.6461247,29.6558125 L25.3482497,29.1638125 C27.6339997,27.6621875 29.4892497,24.3335 29.6865622,21.5583125 L29.7044997,21.0714375 L29.7044997,13.2814375 C29.6966278,13.0342857 29.5228394,12.823633 29.2816872,12.7689375 L26.0631872,12.1539375 C23.8389372,11.7439375 21.1611247,11.275 20.4871872,11.275 L20.4871872,11.275 Z M22.3398747,16.507625 C22.4961872,16.507625 22.5884372,16.599875 22.5807497,16.728 L22.5551247,16.8279375 L21.2405622,19.8004375 C21.1764997,19.9388125 21.2251872,20.0618125 21.3481872,20.1079375 L21.4558122,20.1284375 L23.7953747,20.187375 C23.9516872,20.1899375 24.0259997,20.2745 23.9926872,20.3898125 L23.9465622,20.4795 L20.2027497,25.6736875 C19.7876247,26.2476875 19.5493122,26.2118125 19.6287497,25.6275625 L19.6646247,25.435375 L20.2719372,22.5166875 C20.3052497,22.3629375 20.2283747,22.23225 20.0976872,22.1835625 L19.9926247,22.1681875 L18.1143122,22.1681875 C17.9554372,22.1681875 17.8631872,22.0708125 17.8656977,21.940125 L17.8888122,21.837625 L19.8439997,16.835625 C19.9141363,16.6830557 20.0483635,16.5694788 20.2104372,16.5255625 L20.3283122,16.507625 L22.3398747,16.507625 L22.3398747,16.507625 Z" id="形状"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 18 - 0
src/assets/icons/hazard/audit.svg

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title></title>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="隐患管理-隐患追踪" transform="translate(-340.000000, -476.000000)" fill="currentColor" fill-rule="nonzero">
+            <g id="编组-89" transform="translate(340.000000, 476.000000)">
+                <g id="编组-84" fill-opacity="0.169606882">
+                    <g id="编组-15">
+                        <circle id="Ellipse-265" cx="24" cy="24" r="24"></circle>
+                    </g>
+                </g>
+                <g id="编组" transform="translate(13.000000, 11.000000)">
+                    <path d="M20.9851083,14.678504 L17.2484314,14.678504 C16.4192871,14.678504 15.6719517,14.1685911 15.3491382,13.3828052 L14.0700449,10.2769721 C13.8866221,9.82687055 14.0019729,9.30734945 14.3574816,8.98240398 C15.619992,7.81446603 16.3054179,5.99754809 15.9030066,4.03477918 C15.5050173,2.0923616 13.9793918,0.521920344 12.0933649,0.116025193 C8.8585967,-0.57931057 6.01076246,1.94538005 6.01076246,5.15636139 C6.00932184,6.63759396 6.63361626,8.04725512 7.7232218,9.02310654 C8.08804527,9.35098843 8.21628626,9.87446886 8.02724137,10.3335035 L6.77136416,13.3828052 C6.45072091,14.1660818 5.70236684,14.6766093 4.87207099,14.678504 L1.01378624,14.678504 C0.743744888,14.6797008 0.485228904,14.7905592 0.295110149,14.986691 C0.104991393,15.1828227 -0.00115620054,15.4481614 9.50065078e-06,15.7243342 L9.50065078e-06,19.8205966 C9.50065078e-06,20.3983472 0.45328471,20.8664268 1.01378624,20.8664268 L20.9851083,20.8664268 C21.2553415,20.8655292 21.5141415,20.7548027 21.7044991,20.5586384 C21.8948568,20.3624741 22.001156,20.0969646 21.9999905,19.8205966 L21.9999905,15.7243342 C21.9999905,15.1477143 21.5467153,14.678504 20.9851083,14.678504 L20.9851083,14.678504 Z M20.2720442,22.5001832 L1.72795585,22.5001832 C1.59337528,22.5004827 1.46442317,22.555446 1.36946766,22.6529816 C1.27451215,22.7505172 1.22133157,22.8826354 1.22162388,23.0202717 L1.22162388,24.4787809 C1.22162388,24.7670908 1.44825786,25 1.72795585,25 L20.2720442,25 C20.551608,24.9981353 20.7772707,24.765829 20.7772707,24.4799115 L20.7772707,23.0214023 C20.7778841,22.735044 20.5520397,22.5020501 20.2720442,22.5001832 L20.2720442,22.5001832 Z" id="形状"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

File diff suppressed because it is too large
+ 9 - 0
src/assets/icons/hazard/plan-audit.svg


File diff suppressed because it is too large
+ 15 - 0
src/assets/icons/hazard/plan.svg


+ 21 - 0
src/assets/icons/hazard/submit.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title></title>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="隐患管理-隐患追踪" transform="translate(-199.000000, -410.000000)" fill-rule="nonzero">
+            <g id="编组-51" transform="translate(199.000000, 410.000000)">
+                <g id="Frame-172">
+                    <rect x="0" y="0" width="48" height="48"></rect>
+                    <circle id="Ellipse-265" fill-opacity="0.100000001" fill="currentColor" cx="24" cy="24" r="24"></circle>
+                    <g id="center" transform="translate(11.000000, 11.000000)">
+                        <rect x="0" y="0" width="26" height="26"></rect>
+                    </g>
+                </g>
+                <g id="PaperPlaneTilt" transform="translate(11.000000, 13.000000)">
+                    <line x1="1.77635684e-15" y1="1.77635684e-15" x2="0.1" y2="0.1" id="Vector"></line>
+                    <path d="M20.9752431,3.02471928 C20.7866931,2.83626902 20.5512681,2.70145728 20.2932682,2.63414868 C20.0353427,2.56684023 19.7640677,2.5694641 19.5074182,2.64175045 C19.5074182,2.64175045 2.03915298,7.56866616 2.03915298,7.56866616 C1.74524295,7.65155643 1.48363397,7.8221814 1.28929248,8.05773371 C1.09495098,8.29329389 0.977137601,8.58255368 0.95159867,8.88685852 C0.926059753,9.19117123 0.9940123,9.49602395 1.14637399,9.76067644 C1.29873573,10.0253289 1.52824697,10.2371812 1.80423045,10.3679064 C1.80423045,10.3679064 9.11054304,13.8287814 9.11054304,13.8287814 C9.11054304,13.8287814 14.1056924,8.83355623 14.1056924,8.83355623 C14.2463932,8.69289404 14.4371176,8.61387402 14.6360932,8.61387402 C14.8349929,8.61387402 15.0257931,8.69289404 15.1664181,8.83355623 C15.3071175,8.97421843 15.3860932,9.16499645 15.3860932,9.36391908 C15.3860932,9.56284887 15.3071175,9.75362617 15.1664181,9.89428121 C15.1664181,9.89428121 10.171268,14.8895064 10.171268,14.8895064 C10.171268,14.8895064 13.6321431,22.1957808 13.6321431,22.1957808 C13.7519927,22.4528065 13.9429431,22.6702314 14.1823435,22.8222561 C14.4218183,22.9742809 14.6997681,23.0546814 14.9834185,23.0538073 C15.0263925,23.0538073 15.0695925,23.051982 15.1131673,23.0483814 C15.4180436,23.0245319 15.7080684,22.9073816 15.9439426,22.7127562 C16.1798182,22.5181308 16.3499933,22.255632 16.4313679,21.9608064 C16.4313679,21.9608064 21.3582683,4.4925862 21.3582683,4.4925862 C21.4304934,4.23594385 21.4330426,3.96470627 21.3657685,3.70673624 C21.2984186,3.44876629 21.1636429,3.21336378 20.9752431,3.02471928 C20.9752431,3.02471928 20.9752431,3.02471928 20.9752431,3.02471928 C20.9752431,3.02471928 20.9752431,3.02471928 20.9752431,3.02471928 Z" id="Vector" fill="currentColor"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 1 - 0
src/assets/icons/left.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><path d="M15 6l-6 6l6 6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>

+ 16 - 0
src/types/hazard.d.ts

@@ -0,0 +1,16 @@
+declare namespace Hazard {
+  interface HazardDto {
+    id: number;
+    type: number;
+    category: number;
+    level: string;
+    name: string;
+    description: string;
+    status: number;
+    creator: string;
+    reportingTime: string;
+    responsible: number;
+    responsibleDepartments: number[];
+    images: string[];
+  }
+}

Some files were not shown because too many files changed in this diff