Selaa lähdekoodia

feat: hazard form

Signed-off-by: carlos <568187512@qq.com>
carlos 2 kuukautta sitten
vanhempi
commit
df974ccbd7

+ 212 - 0
src/app/pages/manager/hazard/hazard-tracking/hazard-form/hazard-form.component.html

@@ -0,0 +1,212 @@
+<form nz-form class="py-4" nzLayout="horizontal" [formGroup]="validateForm" (ngSubmit)="submitForm()">
+  <div nz-row>
+    <div nz-col [nzSpan]="10">
+      <nz-form-item>
+        <nz-form-label [nzSpan]="10" nzRequired>隐患级别 </nz-form-label>
+        <nz-form-control [nzSpan]="12" nzErrorTip="请选择隐患级别">
+          <nz-select class="precaution-select" nzPlaceHolder="请选择隐患级别" formControlName="level">
+            @for (level of levels; track level.value) {
+              <nz-option [nzValue]="level.value" [nzLabel]="level.label"></nz-option>
+            }
+          </nz-select>
+        </nz-form-control>
+      </nz-form-item>
+    </div>
+    <div nz-col [nzSpan]="10">
+      <nz-form-item>
+        <nz-form-label [nzSpan]="10" nzRequired>隐患类型 </nz-form-label>
+        <nz-form-control [nzSpan]="12" nzErrorTip="请选择隐患类型">
+          <nz-select class="precaution-select" nzPlaceHolder="请选择隐患类型" formControlName="type">
+            @for (level of types; track level.value) {
+              <nz-option [nzValue]="level.value" [nzLabel]="level.label"></nz-option>
+            }
+          </nz-select>
+        </nz-form-control>
+      </nz-form-item>
+    </div>
+  </div>
+  <div nz-row>
+    <div nz-col [nzSpan]="10">
+      <nz-form-item>
+        <nz-form-label nzRequired nzSpan="10">风险类别</nz-form-label>
+        <nz-form-control nzSpan="12" nzErrorTip="请选择风险类别">
+          <nz-select
+            class="precaution-select"
+            [nzOptions]="riskTypes"
+            formControlName="riskType"
+            nzPlaceHolder="请选择风险类别"
+            nzAllowClear
+            (ngModelChange)="changeRiskType($event)"
+          ></nz-select>
+        </nz-form-control>
+      </nz-form-item>
+    </div>
+
+    <div nz-col nzSpan="10">
+      <nz-form-item>
+        <nz-form-label nzRequired nzSpan="10">风险项</nz-form-label>
+        <nz-form-control nzSpan="12" nzErrorTip="请选择风险项">
+          <nz-select
+            class="precaution-select"
+            [nzOptions]="categories"
+            formControlName="riskCategory"
+            nzPlaceHolder="请选择风险项"
+            nzAllowClear
+            (ngModelChange)="changeRiskCategory($event)"
+          ></nz-select>
+        </nz-form-control>
+      </nz-form-item>
+    </div>
+  </div>
+  <nz-form-item>
+    <nz-form-label [nzSpan]="4" nzRequired>风险条目</nz-form-label>
+    <nz-form-control [nzSpan]="18" nzErrorTip="请选择风险条目">
+      <div class="pl-4">
+        <button
+          nz-button
+          class="precaution-button"
+          nzType="primary"
+          nzSize="small"
+          [disabled]="selectRiskItemDisabled"
+          (click)="selectRiskItem($event)"
+        >
+          {{ selectRiskItemDto ? '重新选择' : '选择' }}
+        </button>
+      </div>
+      @if (selectRiskItemDisabled) {
+        <div class="pl-4">
+          <span class="text-red-500">请先选择风险类别和风险项</span>
+        </div>
+      }
+      @if (selectRiskItemDto) {
+        <div class="py-1">
+          <div class="py-1 px-4 border border-solid border-primary rounded-md text-gray-700">
+            {{ selectRiskItemDto.title }}
+          </div>
+        </div>
+      }
+      <nz-select class="hidden" formControlName="riskItem"> </nz-select>
+    </nz-form-control>
+  </nz-form-item>
+  <div class="section-title">隐患详情信息</div>
+  <nz-form-item>
+    <nz-form-label [nzSpan]="4" nzRequired>隐患名称</nz-form-label>
+    <nz-form-control [nzSpan]="18" nzErrorTip="请输入隐患名称">
+      <input nz-input class="precaution-input" formControlName="name" placeHolder="请输入隐患名称" maxlength="100" />
+    </nz-form-control>
+  </nz-form-item>
+
+  <nz-form-item>
+    <nz-form-label [nzSpan]="4" nzRequired>问题描述</nz-form-label>
+    <nz-form-control [nzSpan]="18" nzErrorTip="请输入问题描述">
+      <textarea
+        nz-input
+        class="precaution-input"
+        formControlName="description"
+        rows="6"
+        placeHolder="请输入问题描述"
+        maxlength="1500"
+      ></textarea>
+    </nz-form-control>
+  </nz-form-item>
+  <nz-form-item>
+    <nz-form-label [nzSpan]="4" nzRequired>问题场所</nz-form-label>
+    <nz-form-control [nzSpan]="20" nzErrorTip="场所/操作/设备至少选择一项">
+      <nz-space class="overflow-visible">
+        <nz-select
+          class="precaution-select"
+          style="min-width: 164px"
+          nzDropdownClassName="precaution-select-dropdown"
+          nzAllowClear
+          formControlName="position"
+          nzPlaceHolder="请选择问题场所"
+          (ngModelChange)="onInvolvedChange()"
+        >
+          @for (po of positions; track po.id) {
+            <nz-option [nzValue]="po.id" [nzLabel]="po.name"></nz-option>
+          }
+        </nz-select>
+
+        <nz-select
+          class="precaution-select ml-2"
+          style="min-width: 164px"
+          nzDropdownClassName="precaution-select-dropdown"
+          nzAllowClear
+          formControlName="operation"
+          nzPlaceHolder="请选择问题操作"
+          (ngModelChange)="onInvolvedChange()"
+        >
+          @for (op of operations; track op.id) {
+            <nz-option [nzValue]="op.id" [nzLabel]="op.name"></nz-option>
+          }
+        </nz-select>
+
+        <nz-select
+          class="precaution-select ml-2"
+          style="min-width: 164px"
+          nzDropdownClassName="precaution-select-dropdown"
+          nzAllowClear
+          formControlName="equipment"
+          nzPlaceHolder="请选择问题设备"
+          (ngModelChange)="onInvolvedChange()"
+        >
+          @for (eq of equipments; track eq.id) {
+            <nz-option [nzValue]="eq.id" [nzLabel]="eq.name"></nz-option>
+          }
+        </nz-select>
+      </nz-space>
+      <!-- </div> -->
+    </nz-form-control>
+  </nz-form-item>
+  <nz-form-item>
+    <nz-form-label [nzSpan]="4">现场照片</nz-form-label>
+    <nz-form-control [nzSpan]="4">
+      <image-upload-group #imageUploadGroup [fileList]="fileList"></image-upload-group>
+    </nz-form-control>
+  </nz-form-item>
+  <nz-form-item>
+    <nz-form-label [nzSpan]="4" nzRequired>流转部门</nz-form-label>
+    <nz-form-control [nzSpan]="6" nzErrorTip="请选择流转部门">
+      <nz-select
+        class="precaution-select"
+        nzDropdownClassName="precaution-select-dropdown"
+        formControlName="department"
+        nzPlaceHolder="请选择流转部门"
+      >
+        @for (dept of departments; track dept.id) {
+          <nz-option [nzValue]="dept.id" [nzLabel]="dept.name"></nz-option>
+        }
+      </nz-select>
+    </nz-form-control>
+  </nz-form-item>
+
+  <nz-form-item>
+    <nz-form-label [nzSpan]="4">流转人</nz-form-label>
+    <nz-form-control [nzSpan]="6">
+      <nz-select
+        class="precaution-select"
+        nzDropdownClassName="precaution-select-dropdown"
+        formControlName="responsible"
+        nzPlaceHolder="请选择流转人"
+      >
+        @for (user of users; track user.id) {
+          <nz-option [nzValue]="user.id" [nzLabel]="user.name"></nz-option>
+        }
+      </nz-select>
+    </nz-form-control>
+  </nz-form-item>
+  <div class="flex justify-end pr-8 py-4">
+    <button nz-button nzType="primary" class="precaution-secondary" (click)="onCancel()">取消</button>
+    <button nz-button class="precaution-button ml-4" nzType="primary">提交</button>
+  </div>
+</form>
+
+<ng-template #detailTpl>
+  <risk-item-detail
+    [data]="currentFilteredRiskItems"
+    [isCompare]="false"
+    [isSelect]="true"
+    [selected]="selectRiskItemId"
+    (selectChange)="handleSelectRiskItem($event)"
+  ></risk-item-detail>
+</ng-template>

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

@@ -0,0 +1,28 @@
+.section-title {
+  font-weight: 500;
+  font-size: 16px;
+  color: #131523;
+  line-height: 22px;
+  padding-left: 24px;
+  padding-bottom: 24px;
+}
+
+.noop-modal {
+  ::ng-deep &.ant-modal {
+    padding-bottom: 0;
+    .ant-modal-body {
+      padding: 0;
+    }
+    .ant-modal-content {
+      border-radius: 19px;
+    }
+    &.ant-modal .ant-modal-close {
+      // color: var(--ant-primary-color);
+      // top: 8px;
+      // right: 8px;
+      &:hover {
+        color: var(--ant-primary-color);
+      }
+    }
+  }
+}

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

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

+ 190 - 0
src/app/pages/manager/hazard/hazard-tracking/hazard-form/hazard-form.component.ts

@@ -0,0 +1,190 @@
+import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core';
+import { AbstractControl, NonNullableFormBuilder, Validators } from '@angular/forms';
+import { NzMessageService } from 'ng-zorro-antd/message';
+import { NzModalService } from 'ng-zorro-antd/modal';
+import { NzUploadFile } from 'ng-zorro-antd/upload';
+import { CommonNzModule } from '../../../../../common.nz.module';
+import { ApiService } from '../../../../../services/api.service';
+import { BasicDataService } from '../../../../../services/basic.service';
+import { KnowledgeService } from '../../../../../services/knowledge.service';
+import { SettingService } from '../../../../../services/setting.service';
+import { ImageUploadGroupComponent } from '../../../../../shared/image-upload-group/image-upload-group.component';
+import { RiskItemDetailComponent } from '../../../risk/risk-bank/risk-item-detail/risk-item-detail.component';
+import { hazardLevelOptions, hazardTypeOptions } from '../../hazard.utils';
+
+export interface HazardFormComponentProps {
+  data?: Hazard.HazardDto;
+  visible: boolean;
+}
+
+@Component({
+  selector: 'hazard-form',
+  standalone: true,
+  imports: [CommonNzModule, ImageUploadGroupComponent, RiskItemDetailComponent],
+  templateUrl: './hazard-form.component.html',
+  styleUrl: './hazard-form.component.less',
+})
+export class HazardFormComponent {
+  @ViewChild('detailTpl') detailTpl!: TemplateRef<void>;
+  @ViewChild('imageUploadGroup') imageUploadGroup!: ImageUploadGroupComponent;
+  @Input() data?: Hazard.HazardDto;
+  @Output() onClose = new EventEmitter<void>();
+  validateForm = this.fb.group({
+    name: this.fb.control<string>('', [Validators.required, Validators.maxLength(100)]),
+    level: this.fb.control<string>('', [Validators.required]),
+    type: this.fb.control<string>('', [Validators.required]),
+    riskType: this.fb.control<number | null>(null, [Validators.required]),
+    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)]),
+    department: this.fb.control<number | null>(null, []),
+    responsible: this.fb.control<number | null>(null, []),
+  });
+  levels: Option[] = hazardLevelOptions.slice();
+  types: Option[] = hazardTypeOptions.slice();
+  categories: Option[] = [];
+  departments: BasicData.Department[] = [];
+  uploading = false;
+  fileList: NzUploadFile[] = [
+    {
+      uid: '15',
+      name: '现场照片',
+      status: 'done',
+      url: 'https://precaution-check2.stage.leadinvr.com/api/uploadFile/get?name=tZuzRmxRvku74ccb898b08bdcbe77a050b20863d1cc6.jpg',
+      preview:
+        'https://precaution-check2.stage.leadinvr.com/api/uploadFile/get?name=tZuzRmxRvku74ccb898b08bdcbe77a050b20863d1cc6.jpg',
+    },
+  ];
+  constructor(
+    private viewContainerRef: ViewContainerRef,
+    private modal: NzModalService,
+    private fb: NonNullableFormBuilder,
+    private message: NzMessageService,
+    private setting: SettingService,
+    private api: ApiService,
+    private knowledge: KnowledgeService,
+    private basic: BasicDataService
+  ) {
+    setting.getUsersOfCurrentCompany();
+  }
+  involvedValidator(control: AbstractControl) {
+    if (!this.validateForm) {
+      return null;
+    }
+    const position = this.validateForm.get('position')?.value;
+    const operation = this.validateForm.get('operation')?.value;
+    const equipment = this.validateForm.get('equipment')?.value;
+    if (!position && !operation && !equipment) {
+      return { involved: true, error: true, required: true };
+    }
+    return null;
+  }
+  reset() {
+    this.validateForm.reset();
+    this.validateForm.markAsUntouched();
+    this.validateForm.updateValueAndValidity();
+
+    this.fileList = [];
+    this.uploading = false;
+  }
+  onCancel() {
+    this.onClose.emit();
+  }
+  onInvolvedChange() {
+    this.validateForm.get('position')?.updateValueAndValidity();
+    this.validateForm.get('operation')?.updateValueAndValidity();
+    this.validateForm.get('equipment')?.updateValueAndValidity();
+  }
+  selectRiskItem(ev: Event) {
+    ev.preventDefault();
+    this.modal.create<TemplateRef<void>, {}>({
+      nzContent: this.detailTpl,
+      nzViewContainerRef: this.viewContainerRef,
+      nzClassName: 'noop-modal',
+      nzClosable: true,
+      nzData: {},
+      nzWidth: 'calc(100vw - 144px)',
+      nzFooter: null,
+    });
+  }
+  async submitForm() {
+    if (!this.validateForm.valid) {
+      Object.values(this.validateForm.controls).forEach(control => {
+        if (control.invalid) {
+          control.markAsDirty();
+          control.updateValueAndValidity({ onlySelf: true });
+        }
+      });
+    } else {
+      this.onSubmit();
+    }
+  }
+  async onSubmit() {
+    console.log(this.validateForm.value);
+    console.log(this.imageUrls);
+    this.onClose.emit();
+  }
+  changeRiskType(t: number) {
+    // const { riskType } = this.validateForm.value;
+    this.validateForm.get('riskCategory')?.reset();
+    this.validateForm.get('department')?.reset();
+    this.validateForm.get('responsible')?.reset();
+    if (t) {
+      this.categories = this.knowledge.getCategoryOptionsByType(t);
+    } else {
+      this.categories = [];
+    }
+  }
+  changeRiskCategory(category: number) {
+    this.validateForm.get('department')?.reset();
+    this.validateForm.get('responsible')?.reset();
+    this.departments = this.knowledge.getDepartmentOptionsByCategory(category);
+  }
+  handleSelectRiskItem(id: number) {
+    this.validateForm.get('riskItem')?.setValue(id);
+  }
+  get positions() {
+    return this.basic.positions;
+  }
+  get operations() {
+    return this.basic.operations;
+  }
+  get equipments() {
+    return this.basic.equipments;
+  }
+  get users() {
+    const { department } = this.validateForm.value;
+    if (!department) return [];
+    return this.setting.usersOfCurrentCompany.filter(u => u.department === department);
+  }
+  get imageUrls() {
+    return this.imageUploadGroup.fileList.map(file => file.url);
+  }
+  get riskTypes() {
+    return this.knowledge.types().map(t => ({ label: t.value, value: t.id }));
+  }
+  get allRiskItems() {
+    return this.knowledge.riskItems();
+  }
+  get selectRiskItemId() {
+    return this.validateForm.get('riskItem')?.value;
+  }
+  get currentFilteredRiskItems() {
+    const { riskType, riskCategory } = this.validateForm.value;
+    if (!riskType || !riskCategory) return [];
+    return this.allRiskItems.filter(r => {
+      return r.category === riskCategory && r.type === riskType;
+    });
+  }
+  get selectRiskItemDto() {
+    return this.allRiskItems.find(r => r.id === this.selectRiskItemId);
+  }
+  get selectRiskItemDisabled() {
+    const { riskType, riskCategory } = this.validateForm.value;
+    if (!riskType || !riskCategory) return true;
+    return false;
+  }
+}

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

@@ -3,7 +3,7 @@
     <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">
+      <button nz-button class="precaution-button add-button" nzType="primary" (click)="handleAddHazard()">
         <span nz-icon nzType="plus" class="text-xs -mr-1"></span>
         <span class="text-base leading-normal">新增隐患</span>
       </button>
@@ -25,3 +25,7 @@
     (onClose)="handleCloseDetail()"
   ></hazard-detail>
 }
+
+<custom-drawer [visible]="formConfig.visible" (onClose)="closeFormDrawer()" [width]="740" title="隐患基础信息">
+  <hazard-form #hazardForm [data]="formConfig.data" (onClose)="closeFormDrawer()"></hazard-form>
+</custom-drawer>

+ 14 - 1
src/app/pages/manager/hazard/hazard-tracking/hazard-tracking.component.ts

@@ -1,8 +1,10 @@
 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 { HazardCardComponent } from './card/card.component';
 import { HazardDetailComponent } from './detail/detail.component';
+import { HazardFormComponent, HazardFormComponentProps } from './hazard-form/hazard-form.component';
 
 export interface HazardDetailConfig {
   visible: boolean;
@@ -32,17 +34,22 @@ const getMockHazard = (index: number): Hazard.HazardDto => {
 @Component({
   selector: 'app-hazard-tracking',
   standalone: true,
-  imports: [CommonNzModule, HazardCardComponent, HazardDetailComponent],
+  imports: [CommonNzModule, HazardCardComponent, HazardDetailComponent, CustomDrawerComponent, HazardFormComponent],
   templateUrl: './hazard-tracking.component.html',
   styleUrl: './hazard-tracking.component.less',
   animations: [horizontalInOutReverse300ms],
 })
 export class HazardTrackingComponent {
   hazards: Hazard.HazardDto[] = [];
+
   detailConfig: HazardDetailConfig = {
     visible: false,
     data: {} as Hazard.HazardDto,
   };
+  formConfig: HazardFormComponentProps = {
+    visible: false,
+    data: undefined,
+  };
   ngOnInit() {
     this.hazards = Array.from({ length: 2 }, (_, i) => getMockHazard(i));
   }
@@ -53,4 +60,10 @@ export class HazardTrackingComponent {
   handleCloseDetail() {
     this.detailConfig.visible = false;
   }
+  handleAddHazard() {
+    this.formConfig.visible = true;
+  }
+  closeFormDrawer() {
+    this.formConfig.visible = false;
+  }
 }

+ 12 - 0
src/app/pages/manager/hazard/hazard.utils.ts

@@ -0,0 +1,12 @@
+export const hazardLevels = ['重大', 'A', 'B', 'C'];
+export const hazardLevelOptions: Option[] = [
+  { label: '重大', value: '重大' },
+  { label: 'A类', value: 'A' },
+  { label: 'B类', value: 'B' },
+  { label: 'C类', value: 'C' },
+];
+
+export const hazardTypeOptions: Option[] = [
+  { label: '生产现场类隐患', value: 'product' },
+  { label: '基础管理类隐患', value: 'base' },
+];

+ 1 - 1
src/app/pages/manager/workbench/problem-assessment/detail/form/form.component.html

@@ -156,7 +156,7 @@
           formControlName="responsible"
           nzPlaceHolder="请选择流转人"
         >
-          @for (user of users | async; track user.id) {
+          @for (user of users; track user.id) {
             <nz-option [nzValue]="user.id" [nzLabel]="user.name"></nz-option>
           }
         </nz-select>

+ 1 - 2
src/app/pages/manager/workbench/problem-assessment/detail/form/form.component.ts

@@ -86,7 +86,6 @@ export class AssessmentFormComponent {
     });
   }
   handleSelectRiskItem(id: number) {
-    console.log(id);
     this.validateForm.get('riskItem')?.setValue(id);
   }
   async submitForm() {
@@ -125,7 +124,7 @@ export class AssessmentFormComponent {
     return this.basic.departments;
   }
   get users() {
-    return this.setting.usersOfCurrentCompany$;
+    return this.setting.usersOfCurrentCompany;
   }
   get isRisk() {
     return this.validateForm.get('result')?.value === 0;

+ 13 - 0
src/app/services/knowledge.service.ts

@@ -2,6 +2,7 @@ import { inject, Injectable, signal } from '@angular/core';
 import { Subject } from 'rxjs';
 import { getLevelColor } from '../app.util';
 import { ApiService } from './api.service';
+import { BasicDataService } from './basic.service';
 import { SettingService } from './setting.service';
 
 const mockLevels: Knowledge.RiskLevelDtoWithColor[] = [
@@ -158,6 +159,7 @@ const mockCategories: Knowledge.RiskTypeDto[] = [
 })
 export class KnowledgeService {
   private setting = inject(SettingService);
+  private basic = inject(BasicDataService);
 
   // levels
   private _levelUpdateDate = signal<string>(new Date().toISOString());
@@ -178,6 +180,8 @@ export class KnowledgeService {
   }
   onTypesChange = new Subject<void>();
 
+  categories = signal<Knowledge.RiskCategoryDto[]>([]);
+
   private _docs = signal<Knowledge.Doc[]>([]);
   docs = this._docs.asReadonly();
   get docsKeys() {
@@ -203,6 +207,12 @@ export class KnowledgeService {
     }));
   }
 
+  getDepartmentOptionsByCategory(category: number) {
+    const cateDto = this.categories().find(c => c.id === category);
+    if (!cateDto) return [];
+    return this.basic.departments.filter(d => cateDto.departments.includes(d.id));
+  }
+
   getTypeName(typeId: number) {
     return this.types().find(t => t.id === typeId)?.value;
   }
@@ -238,6 +248,9 @@ export class KnowledgeService {
     this.api.knowledge.getRiskTypes().then(res => {
       this._types.set(res.list);
       this._typeUpdateDate.set(res.updateDate);
+      this.categories.set([
+        ...res.list.reduce((acc, t) => [...acc, ...t.categories], [] as Knowledge.RiskCategoryDto[]),
+      ]);
       this.onTypesChange.next();
     });
   }

+ 3 - 4
src/app/services/setting.service.ts

@@ -1,5 +1,4 @@
 import { Injectable } from '@angular/core';
-import { from, map, Observable, of } from 'rxjs';
 import { ApiService } from './api.service';
 import { AuthService } from './auth.service';
 import { StorageService } from './storage.service';
@@ -10,7 +9,7 @@ import { StorageService } from './storage.service';
 export class SettingService {
   user?: Auth.User;
 
-  usersOfCurrentCompany$: Observable<Auth.User[]> = of([]);
+  usersOfCurrentCompany: Auth.User[] = [];
 
   get userCompany() {
     return this.user?.company || '';
@@ -28,8 +27,8 @@ export class SettingService {
     this._initialized = false;
     this.user = undefined;
   }
-  getUsersOfCurrentCompany() {
-    this.usersOfCurrentCompany$ = from(this.api.user.getAllUserByCompany(this.userCompany)).pipe(map(res => res));
+  async getUsersOfCurrentCompany() {
+    this.usersOfCurrentCompany = await this.api.user.getAllUserByCompany(this.userCompany);
   }
   async init(): Promise<boolean> {
     if (this._initialized) {

+ 7 - 1
src/app/shared/custom-drawer/custom-drawer.component.html

@@ -5,7 +5,7 @@
   [nzWidth]="width"
   [nzVisible]="visible"
   nzPlacement="right"
-  [nzTitle]="title === null ? undefined : title"
+  [nzTitle]="title === null ? undefined : titleTpl"
   (nzOnClose)="close()"
 >
   <ng-container *nzDrawerContent>
@@ -15,3 +15,9 @@
     }
   </ng-container>
 </nz-drawer>
+
+<ng-template #titleTpl>
+  <div class="drawer-title">
+    {{ title }}
+  </div>
+</ng-template>

+ 7 - 0
src/app/shared/custom-drawer/custom-drawer.component.less

@@ -44,3 +44,10 @@
     }
   }
 }
+
+.drawer-title {
+  font-weight: 500;
+  font-size: 16px;
+  color: #131523;
+  line-height: 22px;
+}

+ 3 - 3
src/app/shared/issue-report-form/issue-report-form.component.html

@@ -120,7 +120,7 @@
         formControlName="responsible"
         nzPlaceHolder="请选择流转人"
       >
-        @for (user of users | async; track user.id) {
+        @for (user of users; track user.id) {
           <nz-option [nzValue]="user.id" [nzLabel]="user.name"></nz-option>
         }
       </nz-select>
@@ -128,7 +128,7 @@
   </nz-form-item>
 
   <div class="flex justify-end pr-8 py-4">
-    <button nz-button class="precaution-button" nzType="primary">提交</button>
-    <button nz-button nzType="primary" class="precaution-secondary ml-4" (click)="onCancel()">取消</button>
+    <button nz-button nzType="primary" class="precaution-secondary" (click)="onCancel()">取消</button>
+    <button nz-button class="precaution-button ml-4" nzType="primary">提交</button>
   </div>
 </form>

+ 1 - 1
src/app/shared/issue-report-form/issue-report-form.component.ts

@@ -131,7 +131,7 @@ export class IssueReportFormComponent {
     return this.basic.departments;
   }
   get users() {
-    return this.setting.usersOfCurrentCompany$;
+    return this.setting.usersOfCurrentCompany;
   }
   get imageUrls() {
     return this.imageUploadGroup.fileList.map(file => file.url);

+ 10 - 6
src/app/shared/scroll-images/scroll-images.component.html

@@ -9,11 +9,15 @@
     </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>
+    @if (leftVisible) {
+      <div class="left-arrow" (click)="handleScroll('left')">
+        <i nz-icon nzType="left" nzTheme="outline"></i>
+      </div>
+    }
+    @if (rightVisible) {
+      <div class="right-arrow" (click)="handleScroll('right')">
+        <i nz-icon nzType="right" nzTheme="outline"></i>
+      </div>
+    }
   }
 </div>

+ 11 - 1
src/app/shared/scroll-images/scroll-images.component.ts

@@ -16,6 +16,8 @@ export class ScrollImagesComponent {
   @Input() height = 88;
 
   directorVisible = false;
+  leftVisible = false;
+  rightVisible = false;
 
   constructor(private viewContainerRef: ViewContainerRef) {}
 
@@ -33,6 +35,7 @@ export class ScrollImagesComponent {
     setTimeout(() => {
       if (this.outer.scrollWidth > this.outer.offsetWidth) {
         this.directorVisible = true;
+        this.rightVisible = true;
       }
     });
   }
@@ -40,11 +43,18 @@ export class ScrollImagesComponent {
   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',
     });
+    setTimeout(() => {
+      this.checkSingleVisible();
+    }, 300);
+  }
+  checkSingleVisible() {
+    const { scrollWidth, scrollLeft, clientWidth } = this.outer;
+    this.rightVisible = clientWidth + scrollLeft < scrollWidth;
+    this.leftVisible = scrollLeft > 0;
   }
 
   get dom() {