标签 Signature Pad 下的文章

准备工作

当然,首先必须要有 ThingsBoard 二次开发基础,可以在本博客中搜索“二次开发”,你会得到很多提升。

增加签名用的 signature pad 包

https://www.npmjs.com/package/angular2-signaturepad,根据提示,加入到 package.json 中,确保模块运行正常。

新增用于签名的组建

手工创建或通过命令行创建,sign.component.ts, sign.component.html, sign.component.scss,并且加入到 module.ts

新增配套 service 处理

页面点击触发签名框弹出,保存签名图片到数据库的请求。

后端图片处理

后端需要将前端提交的数据保存到数据库,还需要将数据展现在前端页面。

代码示例

TS 文件

import { Component, Inject, OnInit, SkipSelf, ViewChild } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { ReportService } from '@core/http/report.service';
import { Report, ReportFill } from '@shared/models/report.models';
import { DialogComponent } from '@shared/components/dialog.component';
import { Router } from '@angular/router';

import { SignaturePad } from 'angular2-signaturepad';

export interface ReportSignObservationDialogData {
  report: Report;
}

@Component({
  selector: 'tb-report-sign-observation-dialog',
  templateUrl: './sign-report-observation-dialog.component.html',
  styleUrls: ['./sign-report-observation-dialog.component.scss'],
  providers: [{provide: ErrorStateMatcher, useExisting: ReportSignObservationDialogComponent}]
})
export class ReportSignObservationDialogComponent extends
  DialogComponent<ReportSignObservationDialogComponent, Report> implements OnInit, ErrorStateMatcher {
    @ViewChild(SignaturePad) signaturePad: SignaturePad;
    private signaturePadOptions: Object = { // passed through to szimek/signature_pad constructor
      'minWidth': 2,
      'canvasWidth': 400, // 弹出的窗口宽度
      'canvasHeight': 200, // 弹出的窗口高度
      'backgroundColor': 'rgb(240,240,240)' // 背景颜色
    };

  reportSignFormGroup: FormGroup;
  isReadOnly: boolean;
  reportSign: String;
  report: Report;
  submitted = false;

  constructor(protected store: Store<AppState>,
              protected router: Router,
              @Inject(MAT_DIALOG_DATA) public data: ReportSignObservationDialogData,
              private reportService: ReportService,
              @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
              public dialogRef: MatDialogRef<ReportSignObservationDialogComponent, Report>,
              public fb: FormBuilder) {
    super(store, router, dialogRef);
  }

  ngAfterViewInit() {
    // this.signaturePad is now available
    this.signaturePad.set('minWidth', 2); // set szimek/signature_pad options at runtime
    this.signaturePad.clear(); // invoke functions from szimek/signature_pad API
  }

  signatureImage;
  drawComplete() {
    // will be notified of szimek/signature_pad's onEnd event
    // console.log(this.signaturePad.toDataURL());
    this.reportSign = this.signaturePad.toDataURL();
    this.signatureImage=this.signaturePad.toDataURL(); // 将图片转换成 base64 码
  }

  drawStart() {
    // will be notified of szimek/signature_pad's onBegin event
    // console.log('begin drawing');
  }

  ngOnInit(): void {
    this.buildReportSign();
    this.loadReportSign();
  }

  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
    const customErrorState = !!(control && control.invalid && this.submitted);
    return originalErrorState || customErrorState;
  }

  buildReportSign() {
    this.reportSignFormGroup = this.fb.group({  
      file: ''
    });
  }

  loadReportSign() {
    this.reportSignFormGroup = this.fb.group({  
      file: ''
    });
  }

  cancel(): void {
    this.dialogRef.close(null);
  }

  save(): void {
    this.submitted = true;
    // console.log(this.data.report.id, this.reportSign);
    this.report = this.data.report;
    this.reportService.signReport(this.report, encodeURIComponent(this.reportSign.toString())).subscribe( // 调用保存签名的 service
      (report) => {
        this.dialogRef.close(this.report);
      }
    );
  }
}

HTML 文件

<form [formGroup]="reportSignFormGroup" (ngSubmit)="save()">
    <mat-toolbar color="primary">
        <h2 translate>report.sign-report</h2>
        <span fxFlex></span>
        <button mat-icon-button (click)="cancel()" type="button">
      <mat-icon class="material-icons">close</mat-icon>
    </button>
    </mat-toolbar>
    <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
    </mat-progress-bar>
    <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
    <div mat-dialog-content>
        <signature-pad [options]="signaturePadOptions" (onBeginEvent)="drawStart()" (onEndEvent)="drawComplete()"></signature-pad>
    </div>

    <div mat-dialog-actions fxLayoutAlign="end center">
        <button *ngIf="!isReadOnly" mat-raised-button color="primary" type="submit">
      {{ 'action.save' | translate }}
    </button>
        <button mat-button color="primary" type="button" [disabled]="(isLoading$ | async)" (click)="cancel()" cdkFocusInitial>
      {{ (isReadOnly ? 'action.close' : 'action.cancel') | translate }}
    </button>
    </div>
</form>

结果展示

签名提示:
sign.png

报告结果:
sign1.png