浏览代码

Merge branch 'master' of http://139.196.39.194:9021/chenc/cd-project-management-web

liangyan0105 3 年之前
父节点
当前提交
49a8d867ca

+ 2 - 1
src/app/entity/contract-management/contract-file-product.ts

@@ -40,7 +40,8 @@ export class ContractFileProduct extends Page {
 	sort?:number;
 	/**主表id*/
 	contractFileId?:string;
-
+	/**折扣率 */
+	discountRate?:number;
 	/**合同产品模块子表*/
 	contractFileModularList?:ContractFileModular[];
 }

+ 2 - 0
src/app/entity/contract-management/contract-file.ts

@@ -65,6 +65,8 @@ export class ContractFile extends Page {
 	salesmanId?:string;
 	/**销售人员名称*/
 	salesmanName?:string;
+	/**汇款金额 */
+	received?:number;
 
 	 //显示
 	 /**客户名称 */

+ 9 - 3
src/app/routes/contract-management/contract-file/add/add.component.html

@@ -1,7 +1,13 @@
 <nz-tabset nzTabPosition="left">
   <nz-tab nzTitle="合同基本信息">
-    <app-contract-management-contract-file-add-essential-information #essentialInformation></app-contract-management-contract-file-add-essential-information>
+    <app-contract-management-contract-file-add-essential-information #essentialInformation (milestone)="milestone($event)"></app-contract-management-contract-file-add-essential-information>
+  </nz-tab>
+  <nz-tab nzTitle="产品与模块">
+    <app-contract-management-contract-file-add-product-module #productModule (contractFileObject)="contractFileObject($event)"></app-contract-management-contract-file-add-product-module>
+  </nz-tab>
+  <nz-tab nzTitle="收款计划">
+    <app-contract-management-contract-file-add-collection-plan #collectionPlan>
+
+    </app-contract-management-contract-file-add-collection-plan>
   </nz-tab>
-  <nz-tab nzTitle="产品与模块">Content of Tab Pane 2</nz-tab>
-  <nz-tab nzTitle="收款计划">Content of Tab Pane 3</nz-tab>
 </nz-tabset>

+ 80 - 6
src/app/routes/contract-management/contract-file/add/add.component.ts

@@ -7,6 +7,13 @@ import { BaseArchivesProjectApproval } from 'app/entity/basedata/base-archives-p
 import { ContractManagementContractFileAddEssentialInformationComponent } from './essential-information/essential-information.component';
 import { PersonnelService } from 'app/services/basedata/personnel.service';
 import { recursiveQuery } from '@shared';
+import { ContractManagementContractFileAddProductModuleComponent } from './product-module/product-module.component';
+import { ContractFile } from 'app/entity/contract-management/contract-file';
+import { ContractManagementContractFileAddCollectionPlanComponent } from './collection-plan/collection-plan.component';
+import { BaseArchivesCollectionLine } from 'app/entity/basedata/base-archives-collection-line';
+import { BaseArchivesCollectionLineService } from 'app/services/basedata/base-archives-collection-line.service';
+import { BaseArchivesMilestone } from 'app/entity/basedata/base-archives-milestone';
+import { BaseArchivesMilestoneService } from 'app/services/basedata/base-archives-milestone.service';
 
 @Component({
   selector: 'app-contract-management-contract-file-add',
@@ -17,18 +24,35 @@ export class ContractManagementContractFileAddComponent implements OnInit {
     private baseArchivesProjectApprovalService: BaseArchivesProjectApprovalService,
     private dictService: DictService,
     private personnelService: PersonnelService,
+    private baseArchivesCollectionLineService: BaseArchivesCollectionLineService,
+    private baseArchivesMilestoneService:BaseArchivesMilestoneService
   ) {}
 
   ngOnInit(): void {
-    this.getDictList().then(() => {
-      return this.getProList();
-    }).then(()=>{
-      return this.getSalesStaffTree();
-    });
+    this.getDictList()
+      .then(() => {
+        return this.getProList();
+      })
+      .then(() => {
+        return this.getSalesStaffTree();
+      })
+      .then(() => {
+        //计划类型
+        return this.getPlanType();
+      })
+      .then(() => {
+        //里程碑
+      });
   }
   //基本信息组件
   @ViewChild('essentialInformation')
   essentialInformation: ContractManagementContractFileAddEssentialInformationComponent;
+  //产品与模块组件
+  @ViewChild('productModule')
+  productModule: ContractManagementContractFileAddProductModuleComponent;
+  //收款计划
+  @ViewChild('collectionPlan')
+  collectionPlan: ContractManagementContractFileAddCollectionPlanComponent;
 
   /**
    * 查询项目立项
@@ -51,7 +75,11 @@ export class ContractManagementContractFileAddComponent implements OnInit {
     return new Promise(resolve => {
       this.dictService.getByDictCode('business_type').then(response => {
         this.essentialInformation.businessTypeDictList = JSON.parse(JSON.stringify(response.result));
-        resolve();
+        //里程碑类型
+        this.dictService.getByDictCode('base_archives_milestone_type').then(response => {
+          this.essentialInformation.milestoneList = response.result;
+          resolve();
+        });
       });
     });
   }
@@ -69,5 +97,51 @@ export class ContractManagementContractFileAddComponent implements OnInit {
     });
   }
 
+  /**
+   * 产品与模块改变数据传值到其他页面
+   */
+  contractFileObject(contractFile: ContractFile) {
+    console.log(contractFile);
+    //产品中计算的标准报价总和传入基本信息
+    this.essentialInformation.contractFile.standardQuotation = contractFile.standardQuotation;
+    //计算折扣率
+    this.essentialInformation.calculation()
+  }
+
+  /**
+   * 计划类型查询
+   */
+  getPlanType() {
+    return new Promise(resolve => {
+      let baseArchivesCollectionLine = new BaseArchivesCollectionLine();
+      baseArchivesCollectionLine.pageSize = 20000;
+      baseArchivesCollectionLine.pkOrg = sessionStorage.getItem('pkOrg');
+      this.baseArchivesCollectionLineService.getList(baseArchivesCollectionLine).then(response => {
+        if (response.success) {
+          //计划类型
+          this.collectionPlan.planList = response.result.records;
+        }
+        resolve();
+      });
+    });
+  }
+
+  /**
+   * 根据里程碑类型查询收款计划的里程碑下拉数据
+   */
+  milestone(event: ContractFile) {
+    //商务里程碑下拉数据
+    //查询条件
+    let baseArchivesMilestone = new BaseArchivesMilestone();
+    baseArchivesMilestone.parentId = '0';
+    baseArchivesMilestone.typeId = event.milestoneId; //里程碑档案类型
+    //查询数据
+    this.baseArchivesMilestoneService.getAllParent(baseArchivesMilestone).then(response => {
+      if (response.result) {
+        this.collectionPlan.milestoneList = JSON.parse(JSON.stringify(response.result));
+      }
+    });
+  }
+
   close() {}
 }

+ 219 - 0
src/app/routes/contract-management/contract-file/add/collection-plan/collection-plan.component.html

@@ -0,0 +1,219 @@
+<!-- 收款计划 -->
+<nz-card nzTitle="收款计划" nzSize="small">
+    <div nz-row [nzGutter]="24">
+      <div nz-col [nzSpan]="24">
+        <!-- 新增按钮 -->
+        <button (click)="collectionPlanAdd()" nz-button nzType="primary">{{ 'button.add' | translate }}</button>
+      </div>
+    </div>
+    <div nz-row [nzGutter]="24">
+      <div nz-col [nzSpan]="24">
+        <nz-table style="padding-top: 5px;" nzSize="small" class="tableTdPadding" #basicTable
+          [nzData]="collectionPlanList" [nzFrontPagination]="false" [nzShowPagination]="false" [nzScroll]="{x:'123vw'}">
+          <!-- <thead>
+            <tr>
+              <th>{{ 'customer.contacts.name' | translate }}</th>
+              <th>{{ 'customer.contacts.telephone' | translate }}</th>
+              <th>{{ 'customer.contacts.mail' | translate }}</th>
+              <th>{{ 'customer.is.it.default' | translate }}</th>
+              <th>{{ 'customer.contacts.client.address' | translate }}</th>
+              <th>{{ 'table.operation' | translate }}</th>
+            </tr>
+          </thead> -->
+          <tbody>
+            <tr *ngFor="let data of basicTable.data">
+              <td style="width: 7%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.planId" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="planIdChange(data,'1')">
+                  <nz-option *ngFor="let i of planList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 4%;">第一笔</td>
+              <td style="width: 4%;">
+                <nz-input-number [(ngModel)]="data.price1" [nzMin]="0" [nzStep]="1" [nzFormatter]="formatterDollar"
+                  [nzParser]="parserDollar" [nzMax]=1000000000 (nzBlur)="priceBlur()"></nz-input-number>
+              </td>
+              <td style="width: 5%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.mileId1" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="mileIdChange(data)">
+                  <nz-option *ngFor="let i of milestoneList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 5%;">
+                <input [(ngModel)]="data.desc1" nz-input maxlength=140 (blur)="descChange()"/>
+              </td>
+              <td style="width: 4%;">第二笔</td>
+              <td style="width: 4%;">
+                <nz-input-number [(ngModel)]="data.price2" [nzMin]="0" [nzStep]="1" [nzFormatter]="formatterDollar"
+                  [nzParser]="parserDollar" [nzMax]=1000000000 (nzBlur)="priceBlur()"></nz-input-number>
+              </td>
+              <td style="width: 5%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.mileId2" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="mileIdChange(data)">
+                  <nz-option *ngFor="let i of milestoneList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 5%;">
+                <input [(ngModel)]="data.desc2" nz-input maxlength=140 (blur)="descChange()"/>
+              </td>
+              <td style="width: 4%;">第三笔</td>
+              <td style="width: 4%;">
+                <nz-input-number [(ngModel)]="data.price3" [nzMin]="0" [nzStep]="1" [nzFormatter]="formatterDollar"
+                  [nzParser]="parserDollar" [nzMax]=1000000000 (nzBlur)="priceBlur()"></nz-input-number>
+              </td>
+              <td style="width: 5%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.mileId3" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="mileIdChange(data)">
+                  <nz-option *ngFor="let i of milestoneList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 5%;">
+                <input [(ngModel)]="data.desc3" nz-input maxlength=140 (blur)="descChange()"/>
+              </td>
+              <td style="width: 4%;">第四笔</td>
+              <td style="width: 4%;">
+                <nz-input-number [(ngModel)]="data.price4" [nzMin]="0" [nzStep]="1" [nzFormatter]="formatterDollar"
+                  [nzParser]="parserDollar" [nzMax]=1000000000 (nzBlur)="priceBlur()"></nz-input-number>
+              </td>
+              <td style="width: 5%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.mileId4" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="mileIdChange(data)">
+                  <nz-option *ngFor="let i of milestoneList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 5%;">
+                <input [(ngModel)]="data.desc4" nz-input maxlength=140 (blur)="descChange()"/>
+              </td>
+              <td style="width: 4%;">第五笔</td>
+              <td style="width: 4%;">
+                <nz-input-number [(ngModel)]="data.price5" [nzMin]="0" [nzStep]="1" [nzFormatter]="formatterDollar"
+                  [nzParser]="parserDollar" [nzMax]=1000000000 (nzBlur)="priceBlur()"></nz-input-number>
+              </td>
+              <td style="width: 5%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.mileId5" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="mileIdChange(data)">
+                  <nz-option *ngFor="let i of milestoneList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 5%;">
+                <input [(ngModel)]="data.desc5" nz-input maxlength=140 (blur)="descChange()"/>
+              </td>
+              <td nzRight="0"><a nz-popconfirm nzTitle="是否删除?"
+                  (nzOnConfirm)="collectionPlanDelete(data.sort)">{{'table.delete'|translate}}</a></td>
+            </tr>
+          </tbody>
+        </nz-table>
+      </div>
+    </div>
+  </nz-card>
+  
+  
+  <!-- 回款情况 -->
+  <nz-card nzTitle="回款情况" nzSize="small">
+    <div nz-row [nzGutter]="24">
+      <div nz-col [nzSpan]="24">
+        <!-- 新增按钮 -->
+        <!-- <button (click)="paymentCollectionAdd()" nz-button nzType="primary">{{ 'button.add' | translate }}</button> -->
+      </div>
+    </div>
+    <div nz-row [nzGutter]="24">
+      <div nz-col [nzSpan]="24">
+        <nz-table style="padding-top: 5px;" nzSize="small" class="tableTdPadding" #basicTable2
+          [nzData]="paymentCollectionList" [nzFrontPagination]="false" [nzShowPagination]="false"
+          [nzScroll]="{x:'123vw'}">
+          <!-- <thead>
+            <tr>
+              <th>{{ 'customer.contacts.name' | translate }}</th>
+              <th>{{ 'customer.contacts.telephone' | translate }}</th>
+              <th>{{ 'customer.contacts.mail' | translate }}</th>
+              <th>{{ 'customer.is.it.default' | translate }}</th>
+              <th>{{ 'customer.contacts.client.address' | translate }}</th>
+              <th>{{ 'table.operation' | translate }}</th>
+            </tr>
+          </thead> -->
+          <tbody>
+            <tr *ngFor="let data of basicTable2.data">
+              <td style="width: 7%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.planId" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="planIdChange(data,'2')" nzDisabled>
+                  <nz-option *ngFor="let i of planList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 4%;">第一笔</td>
+              <td style="width: 4%;">
+                <nz-input-number [(ngModel)]="data.price1" [nzMin]="0" [nzStep]="1" [nzFormatter]="formatterDollar"
+                  [nzParser]="parserDollar"  [nzMax]=1000000000 [disabled]="true"></nz-input-number>
+              </td>
+              <td style="width: 5%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.mileId1" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="mileChange(data)" nzDisabled>
+                  <nz-option *ngFor="let i of milestoneList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 5%;">
+                <input [(ngModel)]="data.desc1" nz-input maxlength=140 [disabled]="true"/>
+              </td>
+              <td style="width: 4%;">第二笔</td>
+              <td style="width: 4%;">
+                <nz-input-number [(ngModel)]="data.price2" [nzMin]="0" [nzStep]="1" [nzFormatter]="formatterDollar"
+                  [nzParser]="parserDollar" [nzMax]=1000000000 [disabled]="true"></nz-input-number>
+              </td>
+              <td style="width: 5%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.mileId2" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="mileChange(data)" nzDisabled>
+                  <nz-option *ngFor="let i of milestoneList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 5%;">
+                <input [(ngModel)]="data.desc2" nz-input maxlength=140 [disabled]="true"/>
+              </td>
+              <td style="width: 4%;">第三笔</td>
+              <td style="width: 4%;">
+                <nz-input-number [(ngModel)]="data.price3" [nzMin]="0" [nzStep]="1" [nzFormatter]="formatterDollar"
+                  [nzParser]="parserDollar" [nzMax]=1000000000 [disabled]="true"></nz-input-number>
+              </td>
+              <td style="width: 5%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.mileId3" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="mileChange(data)" nzDisabled>
+                  <nz-option *ngFor="let i of milestoneList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 5%;">
+                <input [(ngModel)]="data.desc3" nz-input maxlength=140 [disabled]="true"/>
+              </td>
+              <td style="width: 4%;">第四笔</td>
+              <td style="width: 4%;">
+                <nz-input-number [(ngModel)]="data.price4" [nzMin]="0" [nzStep]="1" [nzFormatter]="formatterDollar"
+                  [nzParser]="parserDollar" [nzMax]=1000000000 [disabled]="true"></nz-input-number>
+              </td>
+              <td style="width: 5%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.mileId4" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="mileChange(data)" nzDisabled>
+                  <nz-option *ngFor="let i of milestoneList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 5%;">
+                <input [(ngModel)]="data.desc4" nz-input maxlength=140 [disabled]="true"/>
+              </td>
+              <td style="width: 4%;">第五笔</td>
+              <td style="width: 4%;">
+                <nz-input-number [(ngModel)]="data.price5" [nzMin]="0" [nzStep]="1" [nzFormatter]="formatterDollar"
+                  [nzParser]="parserDollar" [nzMax]=1000000000 [disabled]="true"></nz-input-number>
+              </td>
+              <td style="width: 5%;">
+                <nz-select style="width: 100%;" nzShowSearch nzAllowClear [(ngModel)]="data.mileId5" nzPlaceHolder="请选择"
+                  [nzDropdownMatchSelectWidth]="false" (ngModelChange)="mileChange(data)" nzDisabled>
+                  <nz-option *ngFor="let i of milestoneList" [nzValue]="i.id" [nzLabel]="i.name"></nz-option>
+                </nz-select>
+              </td>
+              <td style="width: 5%;">
+                <input [(ngModel)]="data.desc5" nz-input maxlength=140 [disabled]="true"/>
+              </td>
+              <td nzRight="0"><a nz-popconfirm nzTitle="是否删除?"
+                (nzOnConfirm)="paymentCollectionDelete(data.sort)">{{'table.delete'|translate}}</a></td>
+            </tr>
+          </tbody>
+        </nz-table>
+      </div>
+    </div>
+  </nz-card>

+ 24 - 0
src/app/routes/contract-management/contract-file/add/collection-plan/collection-plan.component.spec.ts

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

+ 230 - 0
src/app/routes/contract-management/contract-file/add/collection-plan/collection-plan.component.ts

@@ -0,0 +1,230 @@
+import { Component, OnInit } from '@angular/core';
+import { NzModalRef, NzMessageService } from 'ng-zorro-antd';
+import { _HttpClient } from '@delon/theme';
+
+@Component({
+  selector: 'app-contract-management-contract-file-add-collection-plan',
+  templateUrl: './collection-plan.component.html',
+})
+export class ContractManagementContractFileAddCollectionPlanComponent implements OnInit {
+  constructor() {}
+
+  collectionPlanList = []; //收款计划数据
+  paymentCollectionList = []; //汇款计划数据
+  planList: any = []; //计划下来集合数据
+  milestoneList: any = []; //里程碑集合数据
+  formatterDollar = (value: number) => {
+    if (value) {
+      return `¥ ${value}`;
+    } else {
+      return `¥ `;
+    }
+  };
+  parserDollar = (value: string) => value.replace('¥ ', '');
+
+  /**
+   * 
+   收款计划新增
+   */
+  collectionPlanSort = 0; //收款计划排序
+  collectionPlanAdd() {
+    this.collectionPlanList = [
+      ...this.collectionPlanList,
+      {
+        price1: 0,
+        price2: 0,
+        price3: 0,
+        price4: 0,
+        price5: 0,
+        sort: this.collectionPlanSort,
+        planType: '1',
+      },
+    ];
+    this.collectionPlanSort++;
+    //回款计划新增
+    this.paymentCollectionAdd();
+  }
+
+  /**
+   * 回款计划新增
+   */
+  paymentCollectionSort = 0; //回款情况排序
+  paymentCollectionAdd() {
+    this.paymentCollectionList = [
+      ...this.paymentCollectionList,
+      {
+        price1: 0,
+        price2: 0,
+        price3: 0,
+        price4: 0,
+        price5: 0,
+        sort: this.paymentCollectionSort,
+        planType: '2',
+      },
+    ];
+    this.paymentCollectionSort++;
+  }
+
+  /**
+   * 类型触发事件
+   */
+  planIdChange(data, type) {
+    //获取类型名称
+    this.getPlanName(data);
+    //同步其他三个表格数据
+    this.getData();
+  }
+
+  /**
+   * 获取类型名称
+   * @param data 计划对象
+   */
+  getPlanName(data) {
+    this.planList.forEach(element => {
+      if (element.id === data.planId) {
+        data.planName = element.name;
+      }
+    });
+  }
+
+  //根据计划收款信息表格数据复制到其他三个表格数据
+  getData() {
+    if (this.collectionPlanList) {
+      this.getAddData(this.collectionPlanList);
+    }
+  }
+
+  /**
+   * 根据计划收款信息表格数据复制到其他三个表格数据
+   *
+   */
+  getAddData(dataList) {
+    this.paymentCollectionList = [];
+    this.paymentCollectionList = [];
+    //循环收款情况
+    dataList.forEach(element => {
+      //回款表格数据
+      let data = {
+        planId: element.planId, //计划条线id
+        planName: element.planName, //计划条线名称
+        price1: element.price1, //金额
+        mileId1: element.mileId1, //里程碑id
+        milestone1: element.milestone1, //里程碑名称
+        price2: element.price2, //金额
+        mileId2: element.mileId2, //里程碑id
+        milestone2: element.milestone2, //里程碑名称
+        price3: element.price3, //金额
+        mileId3: element.mileId3, //里程碑id
+        milestone3: element.milestone3, //里程碑名称
+        price4: element.price4, //金额
+        mileId4: element.mileId4, //里程碑id
+        milestone4: element.milestone4, //里程碑名称
+        price5: element.price5, //金额
+        mileId5: element.mileId5, //里程碑id
+        milestone5: element.milestone5, //里程碑名称
+        sort: element.sort,
+        planType: '2',
+      };
+      //回款情况
+      let data2 = JSON.parse(JSON.stringify(data));
+      data2.planType = '2';
+      this.paymentCollectionList.push(data2);
+    });
+  }
+
+  /**
+   * 金额鼠标离开事件
+   */
+  priceBlur() {
+    this.getData();
+  }
+
+  /**
+   * 获取里程碑名称
+   */
+  getMileName(element) {
+    //判断是否选择里程碑
+    if (element.mileId1) {
+      //循环商务页签中计划表中里程碑下拉数据获取名称
+      this.milestoneList.forEach(mile => {
+        if (mile.id === element.mileId1) {
+          element.milestone1 = mile.name;
+        }
+      });
+    } else {
+      element.milestone1 = '';
+    }
+    //判断是否选择里程碑
+    if (element.mileId2) {
+      //循环商务页签中计划表中里程碑下拉数据获取名称
+      this.milestoneList.forEach(mile => {
+        if (mile.id === element.mileId2) {
+          element.milestone2 = mile.name;
+        }
+      });
+    } else {
+      element.milestone2 = '';
+    }
+    //判断是否选择里程碑
+    if (element.mileId3) {
+      //循环商务页签中计划表中里程碑下拉数据获取名称
+      this.milestoneList.forEach(mile => {
+        if (mile.id === element.mileId3) {
+          element.milestone3 = mile.name;
+        }
+      });
+    } else {
+      element.milestone3 = '';
+    }
+    //判断是否选择里程碑
+    if (element.mileId4) {
+      //循环商务页签中计划表中里程碑下拉数据获取名称
+      this.milestoneList.forEach(mile => {
+        if (mile.id === element.mileId4) {
+          element.milestone4 = mile.name;
+        }
+      });
+    } else {
+      element.milestone4 = '';
+    }
+    //判断是否选择里程碑
+    if (element.mileId5) {
+      //循环商务页签中计划表中里程碑下拉数据获取名称
+      this.milestoneList.forEach(mile => {
+        if (mile.id === element.mileId5) {
+          element.milestone5 = mile.name;
+        }
+      });
+    } else {
+      element.milestone5 = '';
+    }
+  }
+
+  /**
+   * 里程碑选择事件
+   */
+  mileIdChange(data) {
+    //获取里程碑名称
+    this.getMileName(data);
+    //同步其他表格数据
+    this.getData();
+  }
+
+  /**
+   * 描述输入改变事件
+   */
+  descChange() {
+    this.getData();
+  }
+
+  /**
+   * 收款删除按钮
+   */
+  collectionPlanDelete(sort) {
+    this.collectionPlanList = this.collectionPlanList.filter(d => d.sort !== sort);
+  }
+
+  ngOnInit(): void {}
+
+  close() {}
+}

+ 41 - 14
src/app/routes/contract-management/contract-file/add/essential-information/essential-information.component.html

@@ -56,8 +56,24 @@
       </div>
     </div>
     <div nz-row [nzGutter]="24">
+      <!-- 里程碑类型 -->
+      <div nz-col [nzSpan]="6">
+        <nz-form-item>
+          <nz-form-label [nzSpan]="'vertical' ? 24 : null" [nzXs]="24" nzRequired>里程碑类型</nz-form-label>
+          <nz-form-control [nzSm]="24" [nzXs]="24">
+            <nz-select style="widows: 100%;" nzShowSearch nzAllowClear formControlName="milestoneId" id="milestoneId"
+              [(ngModel)]="contractFile.milestoneId" nzPlaceHolder="请选择"
+              (ngModelChange)="milestoneChange($event)">
+              <nz-option *ngFor="let i of milestoneList" [nzValue]="i.value" [nzLabel]="i.text"></nz-option>
+            </nz-select>
+            <nz-form-explain *ngIf="validateForm.get('milestoneId')?.dirty && validateForm.get('milestoneId')?.errors">
+              请选择里程碑
+            </nz-form-explain>
+          </nz-form-control>
+        </nz-form-item>
+      </div>
       <!-- 合同概述 -->
-      <div nz-col [nzSpan]="24">
+      <div nz-col [nzSpan]="18">
         <nz-form-item>
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">合同概述</nz-form-label>
           <nz-form-control [nzSm]="24" [nzXs]="24">
@@ -170,7 +186,7 @@
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">标准报价</nz-form-label>
           <nz-form-explain>
             <nz-input-number [(ngModel)]="contractFile.standardQuotation" formControlName="standardQuotation"
-              id="standardQuotation" [nzMin]="0" [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
+              id="standardQuotation" [nzDisabled]="true" [nzMin]="0" [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
             </nz-input-number>
           </nz-form-explain>
         </nz-form-item>
@@ -180,7 +196,7 @@
         <nz-form-item>
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">成交金额</nz-form-label>
           <nz-form-explain>
-            <nz-input-number [(ngModel)]="contractFile.transactionAmount" formControlName="transactionAmount"
+            <nz-input-number [(ngModel)]="contractFile.transactionAmount" (nzBlur)="transactionAmountBlur()" formControlName="transactionAmount"
               id="transactionAmount" [nzMin]="0" [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
             </nz-input-number>
           </nz-form-explain>
@@ -192,7 +208,7 @@
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">折扣率</nz-form-label>
           <nz-form-explain>
             <nz-input-number [(ngModel)]="contractFile.discountRate" formControlName="discountRate" id="discountRate"
-              [nzMin]="0" [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
+              [nzMin]="0" [nzFormatter]="formatterDollar2" [nzParser]="parserDollar2" [nzDisabled]="true">
             </nz-input-number>
           </nz-form-explain>
         </nz-form-item>
@@ -214,7 +230,7 @@
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">成本</nz-form-label>
           <nz-form-explain>
             <nz-input-number [(ngModel)]="contractFile.cost" formControlName="cost" id="cost" [nzMin]="0"
-              [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
+              [nzFormatter]="formatterDollar" [nzParser]="parserDollar" (nzBlur)="costBlur()">
             </nz-input-number>
           </nz-form-explain>
         </nz-form-item>
@@ -225,7 +241,7 @@
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">合同费用</nz-form-label>
           <nz-form-explain>
             <nz-input-number [(ngModel)]="contractFile.contractCost" formControlName="contractCost" id="contractCost"
-              [nzMin]="0" [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
+              [nzMin]="0" [nzFormatter]="formatterDollar" [nzParser]="parserDollar" (nzBlur)="contractCostBlur()">
             </nz-input-number>
           </nz-form-explain>
         </nz-form-item>
@@ -238,7 +254,18 @@
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">合同利润</nz-form-label>
           <nz-form-explain>
             <nz-input-number [(ngModel)]="contractFile.contractProfit" formControlName="contractProfit"
-              id="contractProfit" [nzMin]="0" [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
+              id="contractProfit" [nzMin]="0" [nzDisabled]="true" [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
+            </nz-input-number>
+          </nz-form-explain>
+        </nz-form-item>
+      </div>
+      <!-- 回款金额 -->
+      <div nz-col [nzSpan]="4">
+        <nz-form-item>
+          <nz-form-label [nzSpan]="'vertical' ? 24 : null">回款金额</nz-form-label>
+          <nz-form-explain>
+            <nz-input-number [nzDisabled]="true" [(ngModel)]="contractFile.received" formControlName="received"
+              id="received" [nzMin]="0" [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
             </nz-input-number>
           </nz-form-explain>
         </nz-form-item>
@@ -248,7 +275,7 @@
         <nz-form-item>
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">应收款账</nz-form-label>
           <nz-form-explain>
-            <nz-input-number [(ngModel)]="contractFile.accountsReceivable" formControlName="accountsReceivable"
+            <nz-input-number [nzDisabled]="true" [(ngModel)]="contractFile.accountsReceivable" formControlName="accountsReceivable"
               id="accountsReceivable" [nzMin]="0" [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
             </nz-input-number>
           </nz-form-explain>
@@ -261,7 +288,7 @@
           <nz-form-explain>
             <nz-input-number [(ngModel)]="contractFile.performanceCalculationRate"
               formControlName="performanceCalculationRate" id="performanceCalculationRate" [nzMin]="0"
-              [nzFormatter]="formatterDollar" [nzParser]="parserDollar">
+              [nzFormatter]="formatterDollar2" [nzParser]="parserDollar2">
             </nz-input-number>
           </nz-form-explain>
         </nz-form-item>
@@ -275,16 +302,16 @@
         <nz-form-item>
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">制单人</nz-form-label>
           <nz-form-explain>
-            xxx
+            {{contractFile.createBy}}
           </nz-form-explain>
         </nz-form-item>
       </div>
       <!-- 制单时间 -->
       <div nz-col [nzSpan]="12">
         <nz-form-item>
-          <nz-form-label [nzSpan]="'vertical' ? 24 : null">制单</nz-form-label>
+          <nz-form-label [nzSpan]="'vertical' ? 24 : null">制单时间</nz-form-label>
           <nz-form-explain>
-            xxx
+            {{contractFile.createTime}}
           </nz-form-explain>
         </nz-form-item>
       </div>
@@ -295,7 +322,7 @@
         <nz-form-item>
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">修改人</nz-form-label>
           <nz-form-explain>
-            xxx
+            {{contractFile.updateBy}}
           </nz-form-explain>
         </nz-form-item>
       </div>
@@ -304,7 +331,7 @@
         <nz-form-item>
           <nz-form-label [nzSpan]="'vertical' ? 24 : null">修改时间</nz-form-label>
           <nz-form-explain>
-            xxx
+            {{contractFile.updateTime}}
           </nz-form-explain>
         </nz-form-item>
       </div>

+ 185 - 65
src/app/routes/contract-management/contract-file/add/essential-information/essential-information.component.ts

@@ -1,6 +1,6 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, Output, EventEmitter } from '@angular/core';
 import { NzModalRef, NzMessageService, NzModalService } from 'ng-zorro-antd';
-import { _HttpClient } from '@delon/theme';
+import { _HttpClient, SettingsService } from '@delon/theme';
 import { FormGroup, Validators, FormBuilder } from '@angular/forms';
 import { ContractFile } from 'app/entity/contract-management/contract-file';
 import { BaseArchivesProjectApproval } from 'app/entity/basedata/base-archives-project-approval';
@@ -8,14 +8,20 @@ import { BaseArchivesProjectApprovalService } from 'app/services/basedata/base-a
 import { DictService } from 'app/services/dict.service';
 import { BasedataCustomerModalTableComponent } from 'app/routes/basedata/customer/modal-table/modal-table.component';
 import { CustomerService } from 'app/services/basedata/customer.service';
+import { DatePipe } from '@angular/common';
 
 @Component({
   selector: 'app-contract-management-contract-file-add-essential-information',
   templateUrl: './essential-information.component.html',
 })
 export class ContractManagementContractFileAddEssentialInformationComponent implements OnInit {
-  constructor(private fb: FormBuilder, private modalService: NzModalService,
-    private customerService:CustomerService) {}
+  constructor(
+    private fb: FormBuilder,
+    private modalService: NzModalService,
+    private customerService: CustomerService,
+    private settingsService: SettingsService,
+    private datePipe:DatePipe
+  ) {}
 
   validateForm!: FormGroup;
   ngOnInit(): void {
@@ -24,41 +30,49 @@ export class ContractManagementContractFileAddEssentialInformationComponent impl
       proId: [null, [Validators.required]],
       overview: [],
       businessTypeDictId: [],
-      fdCustomerName:[null, [Validators.required]],
-      fdCustomerPersonnelId:[null, [Validators.required]],
-      fdCustomeraddress:[],
-      fdCustomerPersonnelTel:[],
-      salesmanId:[null, [Validators.required]],
-      freeAfterSalesStart:[null, [Validators.required]],
-      freeAfterSalesEnd:[null, [Validators.required]],
-      standardQuotation:[],
-      transactionAmount:[],
-      discountRate:[],
-      giftAmount:[],
-      cost:[],
-      contractCost:[],
-      contractProfit:[],
-      accountsReceivable:[],
-      performanceCalculationRate:[]
+      fdCustomerName: [null, [Validators.required]],
+      fdCustomerPersonnelId: [null, [Validators.required]],
+      fdCustomeraddress: [],
+      fdCustomerPersonnelTel: [],
+      salesmanId: [null, [Validators.required]],
+      freeAfterSalesStart: [null, [Validators.required]],
+      freeAfterSalesEnd: [null, [Validators.required]],
+      standardQuotation: [],
+      transactionAmount: [],
+      discountRate: [],
+      giftAmount: [],
+      cost: [],
+      contractCost: [],
+      contractProfit: [],
+      accountsReceivable: [],
+      performanceCalculationRate: [],
+      milestoneId: [],
+      received: [],
     });
   }
-
+  user=this.settingsService.user//当前登录用户信息
   contractFile: ContractFile = {
-    standardQuotation:0,
-    transactionAmount:0,
-    discountRate:0,
-    giftAmount:0,
-    cost:0,
-    contractCost:0,
-    contractProfit:0,
-    accountsReceivable:0,
-    performanceCalculationRate:0
+    standardQuotation: 0,
+    transactionAmount: 0,
+    discountRate: 0,
+    giftAmount: 0,
+    cost: 0,
+    contractCost: 0,
+    contractProfit: 0,
+    accountsReceivable: 0,
+    performanceCalculationRate: 0,
+    received: 0,
+    createBy:this.user.realname,
+    createTime:this.datePipe.transform(new Date(), 'yyyy-MM-dd HH:mm:ss')
   }; //合同对象
   proList: any = []; //项目下拉集合
   businessTypeDictList: any = []; //业务类型集合
-  salesStaffList:any=[];//销售人员tree下拉数据
-  formatterDollar = (value: number): string => `$ ${value}`;
-  parserDollar = (value: string): string => value.replace('$ ', '');
+  salesStaffList: any = []; //销售人员tree下拉数据
+  formatterDollar = (value: number): string => `¥ ${value}`;
+  parserDollar = (value: string): string => value.replace('¥ ', '');
+  milestoneList: any = []; //里程碑类型
+  formatterDollar2 = (value: number): string => `${value}%`;
+  parserDollar2 = (value: string): string => value.replace('%', '');
 
   /**
    * 项目名称下拉款触发事件
@@ -73,7 +87,7 @@ export class ContractManagementContractFileAddEssentialInformationComponent impl
    * 客户选择
    */
   modalTable() {
-    this.getLdCustomerPersonnelList("");
+    this.getLdCustomerPersonnelList('');
     const modalRef = this.modalService.create({
       nzTitle: '选择客户',
       nzContent: BasedataCustomerModalTableComponent,
@@ -103,59 +117,59 @@ export class ContractManagementContractFileAddEssentialInformationComponent impl
   /**
    * 根据客户获取客户人员数据
    */
-  fdCustomerPersonnelList:any=[]
-  getLdCustomerPersonnelList(customerId){
-    return new Promise((resolve)=>{
-      this.customerService.getContactsByMainId("4d0df0dc441aff4655d8aa4380b194dc").then((response)=>{
-        this.fdCustomerPersonnelList=response.result
+  fdCustomerPersonnelList: any = [];
+  getLdCustomerPersonnelList(customerId) {
+    return new Promise(resolve => {
+      this.customerService.getContactsByMainId('4d0df0dc441aff4655d8aa4380b194dc').then(response => {
+        this.fdCustomerPersonnelList = response.result;
         resolve();
-      })
-    })
+      });
+    });
   }
 
   /**
    * 客户人员选择事件
    */
-  fdCustomerPersonnelChange(event){
-    if(event){
+  fdCustomerPersonnelChange(event) {
+    if (event) {
       this.fdCustomerPersonnelList.forEach(element => {
-        if(element.id==event){
-          this.contractFile.fdCustomerPersonnelTel=element.contectTel
+        if (element.id == event) {
+          this.contractFile.fdCustomerPersonnelTel = element.contectTel;
         }
       });
-    }else{
-      this.contractFile.fdCustomerPersonnelId=""
-      this.contractFile.fdCustomerPersonnelTel=""
+    } else {
+      this.contractFile.fdCustomerPersonnelId = '';
+      this.contractFile.fdCustomerPersonnelTel = '';
     }
   }
 
   /**
    * 业务类型选择事件
    */
-  businessTypeDictIdChange(event){
-    if(!event){
-      this.contractFile.businessTypeDictId="";
+  businessTypeDictIdChange(event) {
+    if (!event) {
+      this.contractFile.businessTypeDictId = '';
     }
   }
 
   /**
    * 销售人员选择时间
-   * @param event 
+   * @param event
    */
-  onChangeSalesmanId(event){
-    if(event){
+  onChangeSalesmanId(event) {
+    if (event) {
       //获取人员名称
       this.salesStaffList.forEach(pkOrg => {
         pkOrg.children.forEach(depart => {
           depart.children.forEach(personnel => {
             if (personnel.key === event) {
-              this.contractFile.salesmanName=personnel.name;
+              this.contractFile.salesmanName = personnel.name;
             }
           });
         });
       });
-    }else{
-      this.contractFile.salesmanName=""
+    } else {
+      this.contractFile.salesmanName = '';
     }
   }
 
@@ -164,11 +178,11 @@ export class ContractManagementContractFileAddEssentialInformationComponent impl
    */
   disabledDate = (current: Date): boolean => {
     // Can not select days before today and today
-    if(this.contractFile.freeAfterSalesEnd){
-      let end=new Date(this.contractFile.freeAfterSalesEnd);//结束时间
+    if (this.contractFile.freeAfterSalesEnd) {
+      let end = new Date(this.contractFile.freeAfterSalesEnd); //结束时间
       //开始时间大于结束时间的禁用
-      return current.getTime()>end.getTime();
-    }else{
+      return current.getTime() > end.getTime();
+    } else {
       return false;
     }
   };
@@ -178,15 +192,121 @@ export class ContractManagementContractFileAddEssentialInformationComponent impl
    */
   disabledDate2 = (current: Date): boolean => {
     // Can not select days before today and today
-    if(this.contractFile.freeAfterSalesStart){
-      let start=new Date(this.contractFile.freeAfterSalesStart);//开始时间
+    if (this.contractFile.freeAfterSalesStart) {
+      let start = new Date(this.contractFile.freeAfterSalesStart); //开始时间
       //结束时间小于开始时间禁用
-      return current.getTime()<start.getTime();
-    }else{
+      return current.getTime() < start.getTime();
+    } else {
       return false;
     }
   };
 
+  /**
+   * 里程碑类型选择事件
+   */
+  //回写里程碑类型到其他页签查询明细
+  @Output() milestone = new EventEmitter<{}>();
+  milestoneChange(event) {
+    if (event) {
+      // this.milestoneList.forEach(element => {
+      //   if (element.value === event) {
+      //     this.projectManageArchivesa.milestoneType = element.text;
+      //   }
+      // });
+      this.milestone.emit(this.contractFile);
+    }
+  }
+
+  /**
+   * 成交金额改变事件
+   */
+  transactionAmountBlur() {
+    //计算折扣率
+    this.calculation();
+    //计算应收金额
+    this.calculationAccountsReceivable();
+  }
+
+  /**
+   * 计算折扣率
+   */
+  calculation() {
+    /**计算折扣率*/
+    //标准报价
+    let standardQuotation = this.getIsNaN('standardQuotation');
+    //成交金额
+    let transactionAmount = this.getIsNaN('transactionAmount');
+    //折扣率
+    if (transactionAmount > 0.0 && standardQuotation > 0.0) {
+      //(成交金额/标准报价)100
+      let discountRate = Number(((transactionAmount / standardQuotation) * 100).toFixed(2));
+      this.contractFile.discountRate = discountRate;
+    }
+    //计算合同利润
+    this.calculationContractProfit();
+  }
+
+  /**
+   * 成本改变事件
+   */
+  costBlur() {
+    //计算合同利润
+    this.calculationContractProfit();
+  }
+
+  /**
+   * 合同费用改变事件
+   */
+  contractCostBlur() {
+    //计算合同利润
+    this.calculationContractProfit();
+  }
+
+  /**
+   * 计算合同利润
+   */
+  calculationContractProfit() {
+    //成交金额
+    let transactionAmount = this.getIsNaN('transactionAmount');
+    //成本
+    let cost = this.getIsNaN('cost');
+    //合同费用
+    let contractCost = this.getIsNaN('contractCost');
+    //合同利润
+    if (transactionAmount > 0.0) {
+      //成交金额-成本-合同费用
+      let contractProfit = transactionAmount - cost - contractCost;
+      this.contractFile.contractProfit = contractProfit;
+    }
+  }
+
+  /**
+   * 计算应收款账
+   */
+  calculationAccountsReceivable(){
+    //成交金额
+    let transactionAmount = this.getIsNaN('transactionAmount');
+    //回款金额
+    let received=this.getIsNaN("received");
+    //计算应收金额
+    if(transactionAmount>0.0){
+      //成交金额-回款金额
+      this.contractFile.accountsReceivable=transactionAmount-received;
+    }
+  }
+
+
+  /**
+   * 验证是否数字
+   */
+  getIsNaN(name) {
+    if (!isNaN(Number(this.contractFile[name]))) {
+      return Number(this.contractFile[name]);
+    } else {
+      return 0.0;
+    }
+  }
+
   submitForm() {}
 
   close() {}

+ 121 - 0
src/app/routes/contract-management/contract-file/add/product-module/product-module.component.html

@@ -0,0 +1,121 @@
+<!-- 产品 -->
+<nz-card nzTitle="产品">
+  <!-- 操作按钮 -->
+  <div nz-row [nzGutter]="24">
+    <div nz-col [nzSpan]="24">
+      <button nz-button (click)="productAddRow()" nzType="primary">新增</button>
+      <br />
+      <br />
+    </div>
+  </div>
+  <!-- 表格 -->
+  <div nz-row [nzGutter]="24">
+    <div nz-col [nzSpan]="24">
+      <nz-table #productRowTable nzSize="small" class="tableTdPadding" nzBordered [nzData]="productList">
+        <thead>
+          <tr>
+            <th nzWidth="10%">产品编码</th>
+            <th nzWidth="17%">产品名称</th>
+            <th nzWidth="9%">折扣率</th>
+            <th nzWidth="9%">标准报价</th>
+            <th nzWidth="9%">折扣后单价</th>
+            <th nzWidth="9%">标准金额</th>
+            <th nzWidth="9%">折扣后金额</th>
+            <th nzWidth="9%">成本单价</th>
+            <th nzWidth="9%">成本金额</th>
+            <th nzWidth="10%">操作</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr *ngFor="let data of productRowTable.data" class="editable-row">
+            <td><input type="text" nz-input [(ngModel)]="data.code" /></td>
+            <td><input type="text" nz-input [(ngModel)]="data.name" /></td>
+            <td>
+              <nz-input-number [nzMin]="0" [(ngModel)]="data.discountRate"></nz-input-number>
+            </td>
+            <td>
+              <nz-input-number [nzMin]="0" [nzDisabled]="true" [(ngModel)]="data.standardQuotation" [nzFormatter]="formatterDollar"
+                [nzParser]="parserDollar"></nz-input-number>
+            </td>
+            <td>
+              <nz-input-number [nzMin]="0" [(ngModel)]="data.unitPriceAfterDiscount" [nzFormatter]="formatterDollar"
+                [nzParser]="parserDollar"></nz-input-number>
+            </td>
+            <td>
+              <nz-input-number [nzMin]="0" [(ngModel)]="data.standardAmount" [nzFormatter]="formatterDollar"
+                [nzParser]="parserDollar"></nz-input-number>
+            </td>
+            <td>
+              <nz-input-number [nzMin]="0" [(ngModel)]="data.amountAfterDiscount" [nzFormatter]="formatterDollar"
+                [nzParser]="parserDollar"></nz-input-number>
+            </td>
+            <td>
+              <nz-input-number [nzMin]="0" [(ngModel)]="data.costUnitPrice" [nzFormatter]="formatterDollar"
+                [nzParser]="parserDollar"></nz-input-number>
+            </td>
+            <td>
+              <nz-input-number [nzMin]="0" [(ngModel)]="data.costAmount" [nzFormatter]="formatterDollar"
+                [nzParser]="parserDollar"></nz-input-number>
+            </td>
+            <td>
+              <a nz-popconfirm nzTitle="{{ 'confirm.to.delete' | translate }}?" nzOkText="{{ 'yes' | translate }}"
+                nzCancelText="{{ 'no' | translate }}"
+                (nzOnConfirm)="productDeleteRow(data.id)">{{ 'table.delete' | translate }}</a>
+            </td>
+          </tr>
+        </tbody>
+      </nz-table>
+    </div>
+  </div>
+</nz-card>
+
+<nz-card nzTitle="模块">
+  <nz-tabset>
+    <nz-tab *ngFor="let product of productList" [nzTitle]="product.name">
+      <!-- 操作按钮 -->
+      <div nz-row [nzGutter]="24">
+        <div nz-col [nzSpan]="24">
+          <button nz-button (click)="moduleAddRow(product)" nzType="primary">新增</button>
+          <br />
+          <br />
+        </div>
+      </div>
+      <!-- 表格 -->
+      <div nz-row [nzGutter]="24">
+        <div nz-col [nzSpan]="24">
+          <nz-table #moduleRowTable nzSize="small" class="tableTdPadding" nzBordered
+            [nzData]="product.contractFileModularList">
+            <thead>
+              <tr>
+                <th nzWidth="30%">产品模块编码</th>
+                <th nzWidth="30%">产品模块名称</th>
+                <th nzWidth="10%">标准报价</th>
+                <th nzWidth="10%">采购价格</th>
+                <th nzWidth="20%">操作</th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr *ngFor="let data of moduleRowTable.data" class="editable-row">
+                <td><input type="text" nz-input [(ngModel)]="data.code" /></td>
+                <td><input type="text" nz-input [(ngModel)]="data.name" /></td>
+                <td>
+                  <nz-input-number [nzMin]="0" [(ngModel)]="data.standardQuotation" [nzFormatter]="formatterDollar"
+                    [nzParser]="parserDollar" (ngModelChange)="standardQuotationModuleKeyUp(product)"></nz-input-number>
+                </td>
+                <td>
+                  <nz-input-number [nzMin]="0" [(ngModel)]="data.purchasePrice" [nzFormatter]="formatterDollar"
+                    [nzParser]="parserDollar"></nz-input-number>
+                </td>
+                <td>
+                  <a nz-popconfirm nzTitle="{{ 'confirm.to.delete' | translate }}?" nzOkText="{{ 'yes' | translate }}"
+                    nzCancelText="{{ 'no' | translate }}"
+                    (nzOnConfirm)="moduleDeleteRow(product,data.id)">{{ 'table.delete' | translate }}</a>
+                </td>
+              </tr>
+            </tbody>
+          </nz-table>
+        </div>
+      </div>
+    </nz-tab>
+  </nz-tabset>
+</nz-card>

+ 24 - 0
src/app/routes/contract-management/contract-file/add/product-module/product-module.component.spec.ts

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

+ 130 - 0
src/app/routes/contract-management/contract-file/add/product-module/product-module.component.ts

@@ -0,0 +1,130 @@
+import { Component, OnInit, Output, EventEmitter } from '@angular/core';
+import { NzModalRef, NzMessageService } from 'ng-zorro-antd';
+import { _HttpClient } from '@delon/theme';
+import { ContractFileProduct } from 'app/entity/contract-management/contract-file-product';
+import { ContractFileModular } from 'app/entity/contract-management/contract-file-modular';
+import { ContractFile } from 'app/entity/contract-management/contract-file';
+
+@Component({
+  selector: 'app-contract-management-contract-file-add-product-module',
+  templateUrl: './product-module.component.html',
+})
+export class ContractManagementContractFileAddProductModuleComponent implements OnInit {
+  constructor() {}
+  ngOnInit(): void {}
+
+  productList: ContractFileProduct[] = []; //产品数据
+  // moduleList:ContractFileModular[]=[];//模块数据
+  formatterDollar = (value: number): string => `¥ ${value}`;
+  parserDollar = (value: string): string => value.replace('¥ ', '');
+
+  /**
+   * 产品新增行
+   */
+  prouctI = 0;
+  productAddRow() {
+    this.productList = [
+      ...this.productList,
+      {
+        id: `${this.prouctI}`,
+        code: '',
+        name: '',
+        discountRate: 0,
+        standardQuotation: 0,
+        unitPriceAfterDiscount: 0,
+        standardAmount: 0,
+        amountAfterDiscount: 0,
+        costUnitPrice: 0,
+        costAmount: 0,
+        contractFileModularList: [],
+      },
+    ];
+    this.prouctI++;
+  }
+
+  /**
+   * 产品删除行
+   */
+  productDeleteRow(id) {
+    this.productList = this.productList.filter(d => d.id !== id);
+  }
+
+  /**
+   * 模块新增行
+   */
+  moduleI = 0;
+  moduleAddRow(product) {
+    product.contractFileModularList = [
+      ...product.contractFileModularList,
+      {
+        id: `${this.moduleI}`,
+        code: '',
+        name: '',
+        standardQuotation: 0,
+        purchasePrice: 0,
+      },
+    ];
+    this.moduleI++;
+  }
+
+  /**
+   * 模块删除行
+   */
+  moduleDeleteRow(product, id) {
+    product.contractFileModularList = product.contractFileModularList.filter(d => d.id !== id);
+  }
+
+  /**
+   * 模块标准报价键盘弹起事件
+   * @param product 产品对象
+   */
+  standardQuotationModuleKeyUp(product){
+    //判断当前产品下是否存在模块
+    if(product&&product.contractFileModularList){
+      let standardQuotationTotal=0.0;//定义模块中标准报价总计
+      //循环模块
+      product.contractFileModularList.forEach(element => {
+        //判断标准报价是否为数字
+        if(!isNaN(Number(element.standardQuotation))){
+          //累加
+          standardQuotationTotal=standardQuotationTotal+Number(element.standardQuotation);
+        }
+      });
+      //把模块标准报价总和赋值给产品的标准报价
+      product.standardQuotation=standardQuotationTotal;
+      //获取品总标准报价并传入基本信息
+      this.getContractFileStandardQuotationTotal();
+    }
+  }
+
+  /**
+   * 获取合同总共的标准报价
+   */
+  
+  getContractFileStandardQuotationTotal(){
+    if(this.productList){
+      let standardQuotationProductTotal=0.0;//定义产品中标准报价总计
+      this.productList.forEach(element => {
+        //判断标准报价是否为数字
+        if(!isNaN(Number(element.standardQuotation))){
+          //累加
+          standardQuotationProductTotal=standardQuotationProductTotal+Number(element.standardQuotation);
+        }
+      });
+      //把产品的总标准报价给合同标准报价
+      this.contractFile.standardQuotation=standardQuotationProductTotal;
+      //传给基本信息
+      this.outContractFileObject();
+    }
+  }
+
+  contractFile:ContractFile={}//合同的对象
+  /**
+   * 获取当前页面的相关信息传个父级页面
+   */
+  @Output() contractFileObject = new EventEmitter<{}>();
+  outContractFileObject(){
+    this.contractFileObject.emit(this.contractFile);
+  }
+  close() {}
+}

+ 5 - 1
src/app/routes/contract-management/contract-management.module.ts

@@ -4,12 +4,16 @@ import { ContractManagementRoutingModule } from './contract-management-routing.m
 import { ContractManagementContractFileComponent } from './contract-file/contract-file.component';
 import { ContractManagementContractFileAddComponent } from './contract-file/add/add.component';
 import { ContractManagementContractFileAddEssentialInformationComponent } from './contract-file/add/essential-information/essential-information.component';
+import { ContractManagementContractFileAddProductModuleComponent } from './contract-file/add/product-module/product-module.component';
+import { ContractManagementContractFileAddCollectionPlanComponent } from './contract-file/add/collection-plan/collection-plan.component';
 
 const COMPONENTS = [
   ContractManagementContractFileComponent];
 const COMPONENTS_NOROUNT = [
   ContractManagementContractFileAddComponent,
-  ContractManagementContractFileAddEssentialInformationComponent];
+  ContractManagementContractFileAddEssentialInformationComponent,
+  ContractManagementContractFileAddProductModuleComponent,
+  ContractManagementContractFileAddCollectionPlanComponent];
 
 @NgModule({
   imports: [