风一更--软件开发--UI--Angular material-- DragDropList + table

目的: 记录 Angular material dragDrop list  + table 组合使用


 

场景:

 

<!-- Toolbar-->
<header>
  <nav>
    <mat-toolbar color="primary">
      <button mat-icon-button class="app-icon" aria-label="app icon-button with menu  icon">
        <mat-icon>menu</mat-icon>
      </button>
      <span>Data process & analysis platform</span>
      <span class="app-spacer"></span>
      <button mat-icon-button class="app-icon home-icon" aria-label="app icon-button with  home icon"  routerLink="">
        <mat-icon aria-hidden="false" aria-label="app home icon" fontIcon="home">Home</mat-icon>
      </button>
      <button mat-icon-button class="app-icon" aria-label="app icon-button with share  icon">
        <mat-icon>share</mat-icon>
      </button>
    </mat-toolbar>
  </nav>
</header>

<ng-template cdk-portal #overlayConfig="cdkPortal">
  <mat-spinner color="accent"></mat-spinner>
</ng-template>

<!-- main -->
<main class="wrapper">
  <mat-toolbar>
    <mat-slide-toggle [(ngModel)]="configDefault">
      configuration default: {{configDefault}}
    </mat-slide-toggle>
    <span class="app-spacer"></span>
    <button class="btn_config_op" mat-stroked-button color="warn" (click)="reset()">
      Reset
    </button>
    <button class="btn_config_op" mat-stroked-button color="primary" (click)="submitConfig()" [disabled]="overlayRefConfig">
      Submit
    </button>
  </mat-toolbar>
  <section class="subcategory">
    <mat-sidenav-container [hasBackdrop]="true">
      <mat-sidenav disableClose [(opened)]="configDefault"></mat-sidenav>
      <mat-sidenav-content>
        <div class="grid-config">

          <article class="grid-config-base">
            <header><h3>base</h3></header>
            <mat-table #tb_bases [dataSource]="configInit.bases" class="mat-elevation-z8"
                       cdkDropList id="tb_id_bases" [cdkDropListData]="configInit.bases"
                       [cdkDropListConnectedTo]="['tb_id_conditions','tb_id_comparisons']"
                       (cdkDropListDropped)="dragDropEventHandler($event)">

              <ng-container matColumnDef="index">
                <mat-header-cell *matHeaderCellDef>no.</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{index + 1}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="partition">
                <mat-header-cell *matHeaderCellDef>partition</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{cell.charEntryMeta.partition}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="segment">
                <mat-header-cell *matHeaderCellDef>segment</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{cell.charEntryMeta.segment}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="charName">
                <mat-header-cell *matHeaderCellDef>char name</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{cell.charEntryMeta.charName}}</mat-cell>
              </ng-container>

              <mat-header-row *matHeaderRowDef="metaColumns; sticky: true"></mat-header-row>
              <mat-row *matRowDef="let row; columns: metaColumns; let index = index"
                       [ngStyle]="{'background': highlight(row)}"
                       cdkDrag [cdkDragData]="row.data"
                       [cdkDragDisabled]="row.assignedCondition && row.assignedCompared">
              </mat-row>
            </mat-table>
          </article>

          <article class="grid-config-condition">
            <header><h3>condition</h3></header>
            <mat-table #tb_conditions [dataSource]="configInit.conditions" class="mat-elevation-z8"
                       cdkDropList id="tb_id_conditions" [cdkDropListData]="configInit.conditions"
                       [cdkDropListConnectedTo]="['tb_id_bases']"
                       (cdkDropListDropped)="dragDropEventHandler($event)">
              <ng-container matColumnDef="index">
                <mat-header-cell *matHeaderCellDef>no.</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{index + 1}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="partition">
                <mat-header-cell *matHeaderCellDef>partition</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{cell.charEntryMeta.partition}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="segment">
                <mat-header-cell *matHeaderCellDef>segment</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{cell.charEntryMeta.segment}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="charName">
                <mat-header-cell *matHeaderCellDef>charName</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{cell.charEntryMeta.charName}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="valueType">
                <mat-header-cell *matHeaderCellDef>value type</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{cell.valueType}}</mat-cell>
              </ng-container>

              <mat-header-row *matHeaderRowDef="configColumns; sticky: true"></mat-header-row>
              <mat-row *matRowDef="let row; columns: configColumns;let index = index;"
                       cdkDrag [cdkDragData]="row.data"
                       [cdkDragDisabled]="row.constraint=='MANDATORY'">
              </mat-row>

            </mat-table>
          </article>

          <article class="grid-config-comparison">
            <header><h3>comparison</h3></header>
            <mat-table #tb_comparisons [dataSource]="configInit.comparisons" class="mat-elevation-z8"
                       cdkDropList id="tb_id_comparisons" [cdkDropListData]="configInit.comparisons"
                       [cdkDropListConnectedTo]="['tb_id_bases']"
                        (cdkDropListDropped)="dragDropEventHandler($event)">
              <ng-container matColumnDef="index">
                <mat-header-cell *matHeaderCellDef>no.</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{index + 1}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="partition">
                <mat-header-cell *matHeaderCellDef>partition</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{cell.charEntryMeta.partition}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="segment">
                <mat-header-cell *matHeaderCellDef>segment</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{cell.charEntryMeta.segment}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="charName">
                <mat-header-cell *matHeaderCellDef>char name</mat-header-cell>
                <mat-cell *matCellDef="let cell;let index = index">{{cell.charEntryMeta.charName}}</mat-cell>
              </ng-container>

              <ng-container matColumnDef="valueType">
                <mat-header-cell *matHeaderCellDef>value type</mat-header-cell>
                <mat-cell *matCellDef="let cell">
                  <mat-select [(ngModel)]="cell.valueType" name="valueType">
                    <mat-option class="opt_valueType"  *ngFor="let val of valueOptions" [value]="val.value">
                      {{val.viewValue}}
                    </mat-option>
                  </mat-select>
                </mat-cell>
              </ng-container>

              <mat-header-row *matHeaderRowDef="configColumns; sticky: true"></mat-header-row>
              <mat-row *matRowDef="let row; columns: configColumns; let index = index;" cdkDrag [cdkDragData]="row.data"></mat-row>
            </mat-table>
          </article>
        </div>
      </mat-sidenav-content>
    </mat-sidenav-container>
  </section>
</main>
html
css
h2,h3 {
  margin: 1px 0 1px;
  border-bottom: 1px solid #8e3339;
}
.wrapper {
  width: 95%;
  max-width: 100em;
  margin: 0 auto;
}
.btn_config_op{
  margin: 0 0 0 1.5em;
}
.subcategory {
  margin-top: 1.5em;
  border-bottom: 1px solid #8E3339;
}
.grid-config {
  display: -webkit-grid;
  display: grid;
  -webkit-grid-template-rows : auto auto auto;
  grid-template-rows: auto auto auto;
  -webkit-grid-template-columns: 45% 1fr;
  grid-template-columns: 45% 1fr;
  grid-column-gap: .5em;
  grid-row-gap: .5em;
  margin: 0 -.375em;
}
.grid-config-base {
  -webkit-grid-row: 1/-1;
  grid-row: 1/-1;
  -webkit-grid-column: 1;
  grid-column: 1;
  padding: .175em;
  margin: 0 .375em .375em;
  background-color:skyblue;
}
.grid-config-condition {
  -webkit-grid-row: 1;
  grid-row: 1;
  -webkit-grid-column: 2;
  grid-column: 2;
  padding: .175em;
  margin: 0 .375em .375em;
  background-color: red;
}
.grid-config-comparison{
  -webkit-grid-row: 2;
  grid-row: 2;
  -webkit-grid-column:2;
  grid-column: 2;
  padding: .175em;
  margin: 0 .375em .375em;
  background-color: #FFC66D;
}
/********************************/
.mat-mdc-table{
  font-family: "Lucida Sans", Verdana, Arial, sans-serif;
  border-collapse: collapse;
  overflow: auto;
  max-height: 900px;
  width: 100%;
}
/*#tb_id_conditions{
  font-family: "Lucida Sans", Verdana, Arial, sans-serif;
  border-collapse: collapse;
  overflow: auto;
  height: 900px;
  width: 100%;
}*/
.mat-mdc-table .mat-mdc-row:nth-child(even){
  background-color: lightblue;
}
.mat-mdc-cell {
  word-break: break-word !important;
  font-size: xx-small;
}
.mat-column-index{
  border-right: 1px solid currentColor;
}
/*.mat-column-index{
  width: 35px;
  border-right: 1px solid currentColor;
  text-align: left;
}
.mat-column-partition{
  width:110px;
  text-align:left;
}
.mat-column-segment{
  width:150px;
  text-align:left;
}
.mat-column-valueType{
  width:120px;
}
.mat-mdc-table .mat-column-charName{
  max-width:100%
}*/
.mat-mdc-option{
  font-size: medium;
}
.grid-config article header {
  text-align: center;
  background-color: wheat;
}
.grid-config article header h3{
  font-style:  italic;
}


import {Component, OnInit, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
import {
  ConfigAquaEdgeRegPerform,
  ConfigEntryAquaEdgeRegPerform, ConfigEntryMetaAquaEdgeRegPerform
} from "../../model/config_aqua_edge_reg_perform/config_aqua_edge_reg_perform.model";
import {MatTable} from "@angular/material/table";
import {ActivatedRoute, Router} from "@angular/router";
import {ConfigAquaEdgeRegPerformRepository} from "../../model/config_aqua_edge_reg_perform/config_aqua_edge_reg_perform.repository";
import {ConfigAquaEdgeRegPerformRest} from "../../model/config_aqua_edge_reg_perform/config_aqua_edge_reg_perform.rest";
import {CdkOverlayOrigin, Overlay, OverlayConfig, OverlayRef} from "@angular/cdk/overlay";
import {CdkDrag, CdkDragDrop, moveItemInArray, transferArrayItem} from "@angular/cdk/drag-drop";
import {TemplatePortal} from "@angular/cdk/portal";
import {ConfigAquaEdgeRegPerformService} from "../../model/config_aqua_edge_reg_perform/config-aqua-edge-reg-perform.service";

interface valueEnum {
  value: string;
  viewValue: string;
}

@Component({
  selector: 'app-config-aqua-edge-reg-perform',
  templateUrl: './config-aqua-edge-reg-perform.component.html',
  styleUrls: ['./config-aqua-edge-reg-perform.component.css']
})
export class ConfigAquaEdgeRegPerformComponent implements OnInit {

  configDefault: boolean = true;

  // @ts-ignore
  configInit: ConfigAquaEdgeRegPerform;

  // overlay
  // @ts-ignore
  overlayRefConfig: OverlayRef = null;
  // @ts-ignore
  @ViewChild("overlayConfig", {static: false}) templatePortal: TemplatePortal;

  // table dropList config
  metaColumns: string[] = ['index', 'partition', 'segment', 'charName'];
  configColumns: string[] = ['index', 'partition', 'segment', 'charName', 'valueType'];

  // @ts-ignore
  @ViewChild('tb_bases') tb_bases: MatTable<ConfigEntryAquaEdgeRegPerform>;
  // @ts-ignore
  @ViewChild('tb_conditions') tb_conditions: MatTable<ConfigEntryAquaEdgeRegPerform>;
  // @ts-ignore
  @ViewChild('tb_comparisons') tb_comparisons: MatTable<ConfigEntryAquaEdgeRegPerform>;

  valueOptions: valueEnum[] = [
    {value: "STRING", viewValue: "String"},
    {value: "NUMBER", viewValue: "Number"}
  ];

  constructor(activatedRoute: ActivatedRoute,
              private configService: ConfigAquaEdgeRegPerformService,
              private configRest: ConfigAquaEdgeRegPerformRest,
              private router: Router,
              private overlay: Overlay) {
  }

  ngOnInit(): void {
    this.configInit = this.configService.getInitData();
  }

  /* init table */
  highlight(rowData: ConfigEntryAquaEdgeRegPerform): any {

    let conditionAssigned: boolean = rowData.assignedCondition;
    let comparisonAssigned: boolean = rowData.assignedCompared;

    if (!conditionAssigned && !comparisonAssigned) {
      return '';
    }

    if (conditionAssigned && !comparisonAssigned) {
      return 'red';
    }

    if (!conditionAssigned && comparisonAssigned) {
      return '#FFC66D';
    }

    /* condition and comparison */
    return 'lightslategray';
  }

  /*------------*/
  dragDropEventHandler(event: CdkDragDrop<any[]>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(
        event.container.data,
        event.previousIndex,
        event.currentIndex);
    } else {
      this.transferHandler(event);
    }
    this.tb_bases.renderRows();
    this.tb_conditions.renderRows();
    this.tb_comparisons.renderRows();
  }

  transferHandler(event: CdkDragDrop<any[]>){
    if (event.previousContainer.id === "tb_id_bases") {
      this.transferFromBaseToOther(event);
    }

    if (event.previousContainer.id === "tb_id_conditions") {
      this.transferFromOtherToBase(event,true);
    }

    if (event.previousContainer.id === "tb_id_comparisons") {
      this.transferFromOtherToBase(event,false);
    }
  }

  transferFromBaseToOther(event: CdkDragDrop<any[]>) {
    let item = event.previousContainer.data[event.previousIndex];
    let copiedItem = this.copyMovedItem(item);

    if(event.container.id === "tb_id_conditions"){
      if(item.assignedCondition){
        return;
      }
      copiedItem.assignedCondition = true;
    }
    if(event.container.id === "tb_id_comparisons"){
      if(item.assignedCompared){
        return;
      }
      copiedItem.assignedCompared = true;
    }

    transferArrayItem(
      event.previousContainer.data,
      event.container.data,
      event.previousIndex,
      event.currentIndex);

    event.previousContainer.data.splice(
      event.previousIndex,0,copiedItem);
  }

  transferFromOtherToBase(event: CdkDragDrop<any[]>,isFromCondition:boolean) {
    let item = event.previousContainer.data[event.previousIndex];

    for (let e of event.container.data) {
      if(this.isCharEntryMetaEqual(item.charEntryMeta,e.charEntryMeta)){
        if(isFromCondition){
          e.assignedCondition = false;
        }else{
          e.assignedCompared = false;
        }
        break;
      }
    }
    event.previousContainer.data.splice(event.previousIndex, 1);
  }

  isCharEntryMetaEqual(left: ConfigEntryMetaAquaEdgeRegPerform,
                       right: ConfigEntryMetaAquaEdgeRegPerform) {
    return left.charName == right.charName &&
      left.segment == right.segment &&
      left.partition == right.partition;
  }

  dropEntryDuplicatePredicate(item: CdkDrag<any>) {
    return false;
  }

  copyMovedItem(item:any) : ConfigEntryAquaEdgeRegPerform{
    return JSON.parse(JSON.stringify(item));
  }

  //----------------------------------------------------------------------
  reset() {
  }

  submitConfig() {
    this.openOverlay();
    this.applyConfig();
  }

  applyConfig() {
    let config: ConfigAquaEdgeRegPerform;
    if (this.configDefault) {
      config = this.buildDefaultConfig();
    } else {
      config = this.buildCustomizeConfig();
    }

    // @ts-ignore
    this.configRest.postConfig(config).subscribe(res => {
      this.handleConfigAppliedRes(res);
    });
  }

  buildDefaultConfig() {
    return new ConfigAquaEdgeRegPerform("default", [], [], []);
  }

  buildCustomizeConfig() {
    return new ConfigAquaEdgeRegPerform("customize",
      this.configInit.conditions,
      [],
      this.configInit.comparisons);
  }

  handleConfigAppliedRes(response: any) {
    if (response.statusCode === "NORMAL") {
      this.transferToReport(response);
      return;
    } else {
      alert(response.statusCode);
    }
  }

  private transferToReport(response: any) {
    const data = {
      data: JSON.stringify(response.data)
    };
    this.router.navigate(["/aqua-edge-regression-performance-report", data]);
  }

  // overlay
  openOverlay() {
    const config = new OverlayConfig();
    config.positionStrategy = this.overlay.position()
      .global().centerVertically().centerHorizontally();
    config.disposeOnNavigation = true;
    config.hasBackdrop = true;

    this.overlayRefConfig = this.overlay.create(config);
    this.overlayRefConfig.attach(this.templatePortal);
  }
}
typescript
export class ConfigEntryAquaEdgeRegPerform{
  constructor(public charEntryMeta:ConfigEntryMetaAquaEdgeRegPerform,
              public sequence:number,
              public role:string="BASE",
              public valueType:string = "STRING",
              public constraint:string = "FREE",
              public assignedCondition:boolean = false,
              public assignedCompared : boolean = false) {
  }
}

export class ConfigEntryMetaAquaEdgeRegPerform{
  constructor(public charName:string,
              public segment:string,
              public partition:string){}
}
entity model

 

 

 


 

posted @ 2023-07-03 20:45  君子之行  阅读(14)  评论(0)    收藏  举报