#0 添加一个菜单

《thingsBoard添加菜单》.

#1 复制原有页面替换空页

例如创建类似 src/app/modules/home/pages/customer (客户)的一个列表页面.

复制整个 customer 目录到新建的目录替换所有文件.(图1)

xxx-table-config.resolver.ts // 列表页面

xxx-tabs.component.html/.scss/.ts // 详情页标签

xxx-.component.html/.scss/.ts // 弹出的详情页

复制到对应目录

图1 复制文件到新目录

图2 一个增删改查的列表页面需要复制的路由

图3 修正引入的文件

  1. 复制路由 xxx-routing.module.ts中的路由到新建菜单时新建的 路由模块中.(按需求复制一般是第一个数组元素) 见 图2

  2. 添加 xxxTableConfigResolver 到路由模块文件.

    @NgModule({
      imports: [RouterModule.forChild(routes)],
      exports: [RouterModule],
      providers: [
        performanceMetricManagementTableConfigResolver //这个
      ],
    })
  3. 删除 xxx-routing.module.ts和xxx.module.ts (因为已经有新建菜单的时候路由配置模块和页面配置模块了),

  4. 重命名所有文件的文件名和类名.(需要仔细修改全部)

  5. 重新修正一下 所有文件的引入模块.(看目录层级不一定需要修改.)

  6. scss也有一个修改. xxx.component.scss 的16行. 加 ../ 不然报错找不到.

// 复制到新模块的路由中 改引入的组件路径
// computing-management-routing.module.ts
const routes: Routes = [
  {
    path: "computingManagement",
    data: {
      breadcrumb: {
        label: "customMenu.computingManagement",
        icon: "devices_other",
      },
    },
    children: [
      {
        path: "",
        data: {
          auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
          redirectTo: "/computingManagement/performanceMetricManagement",
        },
      },
      {
        path: 'performanceMetricManagement',
        component: EntitiesTableComponent,
        data: {
          auth: [Authority.TENANT_ADMIN],
          title: 'customer.customers'
        },
        resolve: {
          entitiesTableConfig: CustomersTableConfigResolver
        }
      },
      {
        path: ':entityId',
        component: EntityDetailsPageComponent,
        canDeactivate: [ConfirmOnExitGuard],
        data: {
          breadcrumb: {
            labelFunction: entityDetailsPageBreadcrumbLabelFunction,
            icon: 'supervisor_account'
          } as BreadCrumbConfig<EntityDetailsPageComponent>,
          auth: [Authority.TENANT_ADMIN],
          title: 'customer.customers'
        },
        resolve: {
          entitiesTableConfig: CustomersTableConfigResolver
        }
      },
      // {
      //   path: "performanceMetricManagement",
      //   component: PerformanceMetricManagementComponent,
      //   data: {
      //     auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
      //     breadcrumb: {
      //       label: "customMenu.performanceMetricManagement",
      //       icon: "touch_app",
      //     },
      //   },
      // },
        {
          path: "performanceMonitoringManagement",
          component: PerformanceMonitoringManagementComponent,
          data: {
            auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
            breadcrumb: {
              label: "customMenu.performanceMonitoringManagement",
              icon: "touch_app",
            },
          },
        },
        {
          path: "maintenanceKnowledgeBase",
          component: MaintenanceKnowledgeBaseComponent,
          data: {
            auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
            breadcrumb: {
              label: "customMenu.maintenanceKnowledgeBase",
              icon: "touch_app",
            },
          },
        },
     
    ],
  },
];

// 修改完之后的路由文件

import { Injectable, NgModule } from "@angular/core";
import { Resolve, RouterModule, Routes } from "@angular/router";
import { ConfirmOnExitGuard } from "@app/core/guards/confirm-on-exit.guard";
import { OAuth2Service } from "@app/core/public-api";
import { Authority } from "@app/shared/public-api";
import { Observable } from "rxjs";

import { EntitiesTableComponent } from "../../components/entity/entities-table.component";
import { EntityDetailsPageComponent } from "@home/components/entity/entity-details-page.component";
import { entityDetailsPageBreadcrumbLabelFunction } from "@home/pages/home-pages.models";
import { BreadCrumbConfig } from "@shared/components/breadcrumb";
import { ApiUsageComponent } from "../api-usage/api-usage.component";

import {} from "./performance-monitoring-management/performance-monitoring-management.component";
import { PerformanceMonitoringManagementComponent } from "./performance-monitoring-management/performance-monitoring-management.component";
import { MaintenanceKnowledgeBaseComponent } from "./maintenance-knowledge-base/maintenance-knowledge-base.component";
import { CustomersTableConfigResolver } from "../customer/customers-table-config.resolver";

const routes: Routes = [
  {
    path: "computingManagement",
    data: {
      breadcrumb: {
        label: "customMenu.computingManagement",
        icon: "devices_other",
      },
    },
    children: [
      {
        path: "",
        data: {
          auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
          redirectTo: "/computingManagement/performanceMetricManagement",
        },
      },
      {
        path: "performanceMetricManagement",
        data: {
          breadcrumb: {
            label: "customMenu.performanceMetricManagement",
            icon: "domain",
          },
        },
        children: [
          {
            path: "",
            component: EntitiesTableComponent,
            data: {
              auth: [Authority.TENANT_ADMIN],
              title: "customMenu.performanceMetricManagement",
            },
            resolve: {
              entitiesTableConfig: CustomersTableConfigResolver,
            },
          },
          {
            path: ":entityId",
            component: EntityDetailsPageComponent,
            canDeactivate: [ConfirmOnExitGuard],
            data: {
              breadcrumb: {
                labelFunction: entityDetailsPageBreadcrumbLabelFunction,
                icon: "supervisor_account",
              } as BreadCrumbConfig<EntityDetailsPageComponent>,
              auth: [Authority.TENANT_ADMIN],
              title: "customMenu.performanceMetricManagement",
            },
            resolve: {
              entitiesTableConfig: CustomersTableConfigResolver,
            },
          },
        ],
      },

      // {
      //   path: "performanceMetricManagement",
      //   component: PerformanceMetricManagementComponent,
      //   data: {
      //     auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
      //     breadcrumb: {
      //       label: "customMenu.performanceMetricManagement",
      //       icon: "touch_app",
      //     },
      //   },
      // },
      {
        path: "performanceMonitoringManagement",
        component: PerformanceMonitoringManagementComponent,
        data: {
          auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
          breadcrumb: {
            label: "customMenu.performanceMonitoringManagement",
            icon: "touch_app",
          },
        },
      },
      {
        path: "maintenanceKnowledgeBase",
        component: MaintenanceKnowledgeBaseComponent,
        data: {
          auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
          breadcrumb: {
            label: "customMenu.maintenanceKnowledgeBase",
            icon: "touch_app",
          },
        },
      },
    ],
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
  providers: [
performanceMetricManagementTableConfigResolver
],
})
export class computingManagementRoutingModule {}

#2 复制本地化文件 并 修改为需要的翻译文本

打开 src/assets/locale/locale.constant-zh_CN.json 文件.

  1. 复制一份 customer 的配置.

  2. 修改为需要的字段.(例如: 『客户』替换为『指标』,『新增客户』替换为『新增指标』)

  3. 替换文件中的有本地化配置的地方.

  • xxx.resolver.ts 文件:

// 第一种需要替换格式
 new EntityTableColumn<Customer>('title', 'customer.title', '25%'),
 // 替换
 new EntityTableColumn<Customer>('title', 'performanceMetricManagement.title', '25%'),


// 第二种需要替换的格式
this.translate.instant('customer.manage-customer-users')
// 替换
this.translate.instant('performanceMetricManagement.manage-customer-users')

// 
  • xxx.component.html 文件:

<!--

    Copyright © 2016-2022 The Thingsboard Authors

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<div class="tb-details-buttons" fxLayout.xs="column">
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'open')"
          [fxShow]="!isEdit && !isDetailsPage">
    {{'common.open-details-page' | translate }}
  </button>
  <!-- <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageUsers')"
          [fxShow]="!isEdit && !isPublic">
    {{'customer.manage-users' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageAssets')"
          [fxShow]="!isEdit">
    {{'customer.manage-assets' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageDevices')"
          [fxShow]="!isEdit">
    {{'customer.manage-devices' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageDashboards')"
          [fxShow]="!isEdit">
    {{'customer.manage-dashboards' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageEdges')"
          [fxShow]="!isEdit"
          *ngIf="edgesSupportEnabled()">
    {{'customer.manage-edges' | translate }}
  </button> -->
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'delete')"
          [fxShow]="!hideDelete() && !isEdit && !isPublic">
    {{'customer.delete' | translate }}
  </button>
  <div fxLayout="row" fxLayout.xs="column">
    <button mat-raised-button
            ngxClipboard
            (cbOnSuccess)="onCustomerIdCopied($event)"
            [cbContent]="entity?.id?.id"
            [fxShow]="!isEdit">
      <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
      <span translate>customer.copyId</span>
    </button>
  </div>
</div>
<div class="mat-padding" fxLayout="column">
  <form [formGroup]="entityForm">
    <fieldset [fxShow]="!isPublic" [disabled]="(isLoading$ | async) || !isEdit">
      <mat-form-field class="mat-block">
        <mat-label translate>customer.title</mat-label>
        <input matInput formControlName="title" required/>
        <mat-error *ngIf="entityForm.get('title').hasError('required')">
          {{ 'customer.title-required' | translate }}
        </mat-error>
        <mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
          {{ 'customer.title-max-length' | translate }}
        </mat-error>
      </mat-form-field>
      <div formGroupName="additionalInfo" fxLayout="column">
        <mat-form-field class="mat-block">
          <mat-label translate>customer.description</mat-label>
          <textarea matInput formControlName="description" rows="2"></textarea>
        </mat-form-field>
        <section class="tb-default-dashboard" fxFlex fxLayout="column" *ngIf="entity?.id">
          <section fxFlex fxLayout="column" fxLayout.gt-sm="row">
            <tb-dashboard-autocomplete
              fxFlex
              placeholder="{{ 'dashboard.home-dashboard' | translate }}"
              formControlName="homeDashboardId"
              [dashboardsScope]="'customer'"
              [customerId]="entity?.id.id"
              [selectFirstDashboard]="false"
            ></tb-dashboard-autocomplete>
            <mat-checkbox fxFlex formControlName="homeDashboardHideToolbar">
              {{ 'dashboard.home-dashboard-hide-toolbar' | translate }}
            </mat-checkbox>
          </section>
        </section>
      </div>
      <tb-contact [parentForm]="entityForm" [isEdit]="isEdit"></tb-contact>
    </fieldset>
  </form>
</div>

全部 customer 配置 替换成 配置的本地化.

<!--

    Copyright © 2016-2022 The Thingsboard Authors

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<!-- 这是详情上面的按钮 隐藏无用的按钮 -->
<div class="tb-details-buttons" fxLayout.xs="column">
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'open')"
          [fxShow]="!isEdit && !isDetailsPage">
    {{'common.open-details-page' | translate }}
  </button>
  <!-- <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageUsers')"
          [fxShow]="!isEdit && !isPublic">
    {{'customer.manage-users' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageAssets')"
          [fxShow]="!isEdit">
    {{'customer.manage-assets' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageDevices')"
          [fxShow]="!isEdit">
    {{'customer.manage-devices' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageDashboards')"
          [fxShow]="!isEdit">
    {{'customer.manage-dashboards' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageEdges')"
          [fxShow]="!isEdit"
          *ngIf="edgesSupportEnabled()">
    {{'customer.manage-edges' | translate }}
  </button> -->
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'delete')"
          [fxShow]="!hideDelete() && !isEdit && !isPublic">
    {{'performanceMetricManagement.delete' | translate }}
  </button>
  <!-- <div fxLayout="row" fxLayout.xs="column">
    <button mat-raised-button
            ngxClipboard
            (cbOnSuccess)="onCustomerIdCopied($event)"
            [cbContent]="entity?.id?.id"
            [fxShow]="!isEdit">
      <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
      <span translate>performanceMetricManagement.copyId</span>
    </button>
  </div> -->
</div>
<div class="mat-padding" fxLayout="column">
  <form [formGroup]="entityForm">
    <fieldset [fxShow]="!isPublic" [disabled]="(isLoading$ | async) || !isEdit">
      <mat-form-field class="mat-block">
        <mat-label translate>performanceMetricManagement.title</mat-label>
        <input matInput formControlName="title" required/>
        <mat-error *ngIf="entityForm.get('title').hasError('required')">
          {{ 'performanceMetricManagement.title-required' | translate }}
        </mat-error>
        <mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
          {{ 'performanceMetricManagement.title-max-length' | translate }}
        </mat-error>
      </mat-form-field>
      <div formGroupName="additionalInfo" fxLayout="column">
        <mat-form-field class="mat-block">
          <mat-label translate>performanceMetricManagement.description</mat-label>
          <textarea matInput formControlName="description" rows="2"></textarea>
        </mat-form-field>
        <section class="tb-default-dashboard" fxFlex fxLayout="column" *ngIf="entity?.id">
          <section fxFlex fxLayout="column" fxLayout.gt-sm="row">
            <tb-dashboard-autocomplete
              fxFlex
              placeholder="{{ 'dashboard.home-dashboard' | translate }}"
              formControlName="homeDashboardId"
              [dashboardsScope]="'customer'"
              [customerId]="entity?.id.id"
              [selectFirstDashboard]="false"
            ></tb-dashboard-autocomplete>
            <mat-checkbox fxFlex formControlName="homeDashboardHideToolbar">
              {{ 'dashboard.home-dashboard-hide-toolbar' | translate }}
            </mat-checkbox>
          </section>
        </section>
      </div>
      <tb-contact [parentForm]="entityForm" [isEdit]="isEdit"></tb-contact>
    </fieldset>
  </form>
</div>

修改 src/app/shared/models/entity-type.models.ts 文件.
复制一份 customer的配置.

以及 『entity』 内的本地化翻译 一并需要修改.

 /// 1.第一个地方 添加一个 PERFORMANCEMETRICMANAGEMENT = 'PERFORMANCEMETRICMANAGEMENT'
export enum EntityType 



[
      EntityType.CUSTOMER,
      {
        type: 'entity.type-customer',
        typePlural: 'entity.type-customers',
        list: 'entity.list-of-customers',
        nameStartsWith: 'entity.customer-name-starts-with',
        details: 'customer.customer-details',
        add: 'customer.add',
        noEntities: 'customer.no-customers-text',
        search: 'customer.search',
        selectedEntities: 'customer.selected-customers'
      }
    ],
  [
    EntityType.PERFORMANCEMETRICMANAGEMENT,
    {
      type: "entity.type-performanceMetricManagement",
      typePlural: "entity.type-performanceMetricManagements",
      list: "entity.list-of-performanceMetricManagements",
      nameStartsWith: "entity.performanceMetricManagement-name-starts-with",
      details: "performanceMetricManagement.customer-details",
      add: "performanceMetricManagement.add",
      noEntities: "performanceMetricManagement.no-customers-text",
      search: "performanceMetricManagement.search",
      selectedEntities: "performanceMetricManagement.selected-customers",
    },
  ],

然后 xxxx-table-config.resolver.ts文件内替换所有

EntityType.CUSTOMER

替换为新建的

EntityType.PERFORMANCEMETRICMANAGEMENT

然后 替换 列表的显示字段.

    this.config.columns.push(
      new DateEntityTableColumn<Customer>('createdTime', 'common.created-time', this.datePipe, '150px'),
      new EntityTableColumn<Customer>('title', 'performanceMetricManagement.title', '25%'),
      new EntityTableColumn<Customer>('checkvalue', 'performanceMetricManagement.checkvalue', '25%'),
      new EntityTableColumn<Customer>('remark', 'performanceMetricManagement.remark', '50%'),
      // new EntityTableColumn<Customer>('city', 'contact.city', '25%')
    );

TIPS:普通简单页面替换完成,其他复杂页面可能还有其他位置,全局搜索替换即可.

页面静态基本完成,继续替换动态配置部分.

#3 替换引入的组件和模块

  1. xxx-table-config.resolver.ts 文件

目前引入还是复制之前的组件,现在全部替换为改完的组件.没有的model和service两个文件到对应路径新建,内容复制 costomer 的即可.

Customer 和 CustomerService 替换完,三个页面文件的 对应位置都要修改.

<Customer> 替换 <performanceMetricManagement>(就是复制的customer.model)

还有 Service 文件 继续替换, 接口的数据格式(实体类也要改)

import { performanceMetricManagement } from '@shared/models/performanceMetricManagement.model';
// import { Customer } from '@shared/models/customer.model';

两个组件替换,修改的地方

this.config.entityComponent
this.config.entityTabsComponent
    this.config.entityComponent = PerformanceMetricManagementComponent;
    this.config.entityTabsComponent = PerformanceMetricManagementTabsComponent;

然后如果报错,那就是缺少组件引用,到 模块文件,引入所有用的组件.

 	SharedModule, //必要引入
    HomeComponentsModule,//必要引入
    HomeDialogsModule,//必要引入

引入完成后:

最新的模块文件.

import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { SharedModule } from "@shared/shared.module";

import { computingManagementRoutingModule } from "./computing-management-routing.module";

import { HomeComponentsModule } from "@modules/home/components/home-components.module";
import { AssetTabsComponent } from "@home/pages/asset/asset-tabs.component";
import { HomeDialogsModule } from "../../dialogs/home-dialogs.module";
import { MaintenanceKnowledgeBaseComponent } from "./maintenance-knowledge-base/maintenance-knowledge-base.component";
import { PerformanceMonitoringManagementComponent } from "./performance-monitoring-management/performance-monitoring-management.component";
import { PerformanceMetricManagementComponent } from "./performance-metric-management/performance-metric-management.component";
import { PerformanceMetricManagementTabsComponent } from "./performance-metric-management/performance-metric-management-tabs.component";

@NgModule({
  declarations: [
    MaintenanceKnowledgeBaseComponent,
    PerformanceMonitoringManagementComponent,
    PerformanceMetricManagementComponent,
    PerformanceMetricManagementTabsComponent, //新增的组件引入
  ],
  imports: [
    CommonModule,
    SharedModule, //必要引入
    HomeComponentsModule,//必要引入
    HomeDialogsModule,//必要引入
    computingManagementRoutingModule,
  ],
})
export class computingManagementModule {}

#4 修改详情页/新增页

修改 src/app/modules/home/pages/computingManagement/performance-metric-management/performance-metric-management.component.ts
文件中的 buildEntityForm 和 updateEntityForm 方法.

全部按照实际需要的字段(也就是实体类声明的那些)修改即可.

 // buildEntityForm
  buildEntityForm(entity: performanceMetricManagement): FormGroup {
    return this.fb.group(
      {
        title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
        name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
        checkvalue: [entity ? entity.checkvalue : '', [Validators.required, Validators.maxLength(255)]],
        remark: [entity ? entity.remark : '', ['', Validators.maxLength(255)]],
      }
    );
  }
 // updateEntityForm
  updateEntityForm(entity: performanceMetricManagement) {
     this.entityForm.patchValue({title: entity.title});
    this.entityForm.patchValue({name: entity.name});
    this.entityForm.patchValue({checkvalue: entity.checkvalue});
    this.entityForm.patchValue({remark: entity.remark});
  }  

然后按照需求修改 对应html文件.

<!--

    Copyright © 2016-2022 The Thingsboard Authors

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<!-- 这是详情上面的按钮 隐藏无用的按钮 -->
<div class="tb-details-buttons" fxLayout.xs="column">
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'open')"
          [fxShow]="!isEdit && !isDetailsPage">
    {{'common.open-details-page' | translate }}
  </button>
  <!-- <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageUsers')"
          [fxShow]="!isEdit && !isPublic">
    {{'customer.manage-users' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageAssets')"
          [fxShow]="!isEdit">
    {{'customer.manage-assets' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageDevices')"
          [fxShow]="!isEdit">
    {{'customer.manage-devices' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageDashboards')"
          [fxShow]="!isEdit">
    {{'customer.manage-dashboards' | translate }}
  </button>
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'manageEdges')"
          [fxShow]="!isEdit"
          *ngIf="edgesSupportEnabled()">
    {{'customer.manage-edges' | translate }}
  </button> -->
  <button mat-raised-button color="primary"
          [disabled]="(isLoading$ | async)"
          (click)="onEntityAction($event, 'delete')"
          [fxShow]="!hideDelete() && !isEdit && !isPublic">
    {{'performanceMetricManagement.delete' | translate }}
  </button>
  <!-- <div fxLayout="row" fxLayout.xs="column">
    <button mat-raised-button
            ngxClipboard
            (cbOnSuccess)="onCustomerIdCopied($event)"
            [cbContent]="entity?.id?.id"
            [fxShow]="!isEdit">
      <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
      <span translate>performanceMetricManagement.copyId</span>
    </button>
  </div> -->
</div>
<div class="mat-padding" fxLayout="column">
  <form [formGroup]="entityForm">
    <fieldset [fxShow]="!isPublic" [disabled]="(isLoading$ | async) || !isEdit">
      <mat-form-field class="mat-block">
        <mat-label translate>performanceMetricManagement.title</mat-label>
        <input matInput formControlName="name" required/>
        <mat-error *ngIf="entityForm.get('title').hasError('required')">
          {{ 'performanceMetricManagement.title-required' | translate }}
        </mat-error>
        <mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
          {{ 'performanceMetricManagement.title-max-length' | translate }}
        </mat-error>
      </mat-form-field>
    
        <mat-form-field class="mat-block">
          <mat-label translate>performanceMetricManagement.checkvalue</mat-label>
          <input matInput formControlName="checkvalue" rows="2" />
        </mat-form-field>

        <mat-form-field class="mat-block">
          <mat-label translate>performanceMetricManagement.description</mat-label>
          <textarea matInput formControlName="remark" rows="2"></textarea>
        </mat-form-field>
 
    </fieldset>
  </form>
</div>

#5 完成 检查一下增删改查功能吧

GWO