fengxiang
2018-01-10 3517e796f650b8aed52165c1a5905456f54033ef
项目路径 配置,以适应可能的框架更新
10 files renamed
1 files deleted
11 files added
27 files modified
1906 ■■■■ changed files
src/app/app.module.ts 3 ●●●● patch | view | raw | blame | history
src/app/business/entity/data.ts 63 ●●●●● patch | view | raw | blame | history
src/app/business/entity/grid.ts 9 ●●●● patch | view | raw | blame | history
src/app/business/enum/types.enum.ts patch | view | raw | blame | history
src/app/business/pipe/pipe.module.ts 2 ●●● patch | view | raw | blame | history
src/app/business/pipe/tyep-handle.pipe.ts 4 ●●●● patch | view | raw | blame | history
src/app/business/services/http/areacode.service.ts 2 ●●● patch | view | raw | blame | history
src/app/business/services/http/monitor-point.service.ts 6 ●●●● patch | view | raw | blame | history
src/app/business/services/http/organization.service.ts 17 ●●●● patch | view | raw | blame | history
src/app/business/services/http/sensors.service.ts 39 ●●●●● patch | view | raw | blame | history
src/app/business/services/http/version.service.ts 17 ●●●● patch | view | raw | blame | history
src/app/business/services/util/date.service.ts patch | view | raw | blame | history
src/app/business/services/util/example.service.ts 45 ●●●●● patch | view | raw | blame | history
src/app/business/services/util/tools.service.ts patch | view | raw | blame | history
src/app/core/services/example.service.ts 45 ●●●●● patch | view | raw | blame | history
src/app/routes/devices/devices.module.ts 20 ●●●●● patch | view | raw | blame | history
src/app/routes/devices/monitor-point/monitor-point-edit/monitor-point-edit.component.html 80 ●●●●● patch | view | raw | blame | history
src/app/routes/devices/monitor-point/monitor-point-edit/monitor-point-edit.component.ts 118 ●●●●● patch | view | raw | blame | history
src/app/routes/devices/monitor-point/monitor-point.component.html 2 ●●● patch | view | raw | blame | history
src/app/routes/devices/monitor-point/monitor-point.component.ts 72 ●●●● patch | view | raw | blame | history
src/app/routes/devices/version/version-edit/version-edit.component.html 7 ●●●●● patch | view | raw | blame | history
src/app/routes/devices/version/version-edit/version-edit.component.ts 5 ●●●● patch | view | raw | blame | history
src/app/routes/devices/version/version-sensor-config/version-sensor-config.component.html 39 ●●●●● patch | view | raw | blame | history
src/app/routes/devices/version/version-sensor-config/version-sensor-config.component.ts 105 ●●●●● patch | view | raw | blame | history
src/app/routes/devices/version/version.component.html 4 ●●● patch | view | raw | blame | history
src/app/routes/devices/version/version.component.ts 83 ●●●● patch | view | raw | blame | history
src/app/routes/routes.module.ts 12 ●●●●● patch | view | raw | blame | history
src/app/routes/sensors/basic-info/basic-info.component.html 69 ●●●●● patch | view | raw | blame | history
src/app/routes/sensors/basic-info/basic-info.component.ts 162 ●●●●● patch | view | raw | blame | history
src/app/routes/sensors/basic-info/sensor-edit/sensor-edit.component.html 64 ●●●●● patch | view | raw | blame | history
src/app/routes/sensors/basic-info/sensor-edit/sensor-edit.component.ts 64 ●●●●● patch | view | raw | blame | history
src/app/routes/sensors/sensors.module.ts 18 ●●●● patch | view | raw | blame | history
src/app/routes/systems/account/account.component.ts 2 ●●● patch | view | raw | blame | history
src/app/routes/systems/organization/organization-config/organization-config.component.html 67 ●●●●● patch | view | raw | blame | history
src/app/routes/systems/organization/organization-config/organization-config.component.ts 24 ●●●●● patch | view | raw | blame | history
src/app/routes/systems/organization/organization-edit/organization-edit.component.html 9 ●●●●● patch | view | raw | blame | history
src/app/routes/systems/organization/organization-edit/organization-edit.component.ts 4 ●●●● patch | view | raw | blame | history
src/app/routes/systems/organization/organization-list/organization-list.component.html 66 ●●●●● patch | view | raw | blame | history
src/app/routes/systems/organization/organization-list/organization-list.component.ts 212 ●●●●● patch | view | raw | blame | history
src/app/routes/systems/organization/organization.component.html 71 ●●●●● patch | view | raw | blame | history
src/app/routes/systems/organization/organization.component.ts 227 ●●●●● patch | view | raw | blame | history
src/app/routes/systems/systems.module.ts 15 ●●●●● patch | view | raw | blame | history
src/app/routes/users/alarm-user/alarm-user.component.ts 2 ●●● patch | view | raw | blame | history
src/app/routes/users/installer/edit/edit.component.ts 4 ●●●● patch | view | raw | blame | history
src/app/routes/users/installer/installer.component.ts 2 ●●● patch | view | raw | blame | history
src/app/shared/shared.module.ts 13 ●●●●● patch | view | raw | blame | history
src/tsconfig.app.json 4 ●●● patch | view | raw | blame | history
src/tsconfig.spec.json 4 ●●● patch | view | raw | blame | history
tsconfig.json 4 ●●● patch | view | raw | blame | history
src/app/app.module.ts
@@ -1,3 +1,4 @@
import { DateService } from '@business/services/util/date.service';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, LOCALE_ID, APP_INITIALIZER, Injector } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@@ -9,7 +10,6 @@
import { AppComponent } from './app.component';
import { RoutesModule } from './routes/routes.module';
import { LayoutModule } from './layout/layout.module';
import { DateService } from './core/services/date.service';
import { StartupService } from './core/services/startup.service';
import { DefaultInterceptor } from '@core/net/default.interceptor';
import { AlainAuthModule, SimpleInterceptor } from '@delon/auth';
@@ -20,6 +20,7 @@
import { registerLocaleData } from '@angular/common';
import localeZhHans from '@angular/common/locales/zh-Hans';
registerLocaleData(localeZhHans);
// AoT requires an exported function for factories
src/app/business/entity/data.ts
New file
@@ -0,0 +1,63 @@
import { Column } from '@business/entity/grid';
export interface AreaNames {
    provinceName?: string;
    cityName?: string;
    areaName?: string;
}
// 传感器
export interface Sensor {
    description?: any|Column;
    id?: any|Column;
    key?: any|Column;
    lower?: any|Column;
    name?: any|Column;
    unit?: any|Column;
    upper?: any|Column;
  }
// 组织
export interface Organization {
    address?: Column|any;
    areaCode?: Column|any;
    cityCode?: Column|any;
    createTime?: Column|any;
    description?: Column|any;
    email?: Column|any;
    expireTime?: Column|any;
    id?: Column|any;
    isDelete?: Column|any;
    name?: Column|any;
    provinceCode?: Column|any;
    rank?: Column|any;
    telephone?: Column|any;
    areaNames?: AreaNames|any ;
  }
  // 设备型号
  export interface DeviceVersion {
    createTime?: Column|any;
    description?: Column|any;
    id?: Column|any;
    name?: Column|any;
    version?: Column|any;
  }
  // 监控点
  export interface MonitorPoint {
    address?: any|Column;
    areaCode?: any|Column;
    cityCode?: any|Column;
    description?: any|Column;
    id?: any|Column;
    isDelete?: any|Column;
    latitude?: any|Column;
    longitude?: any|Column;
    name?: any|Column;
    organizationId?: any|Column;
    provinceCode?: any|Column;
    areaNames?: AreaNames|any ;
    organization?: Organization;
  }
src/app/business/entity/grid.ts
File was renamed from src/app/core/entity/grid.ts
@@ -1,11 +1,6 @@
import { Column } from '@core/entity/grid';
import { Types } from '@core/enum/types.enum';
import { Types } from '@business/enum/types.enum';
export interface AreaNames {
    provinceName?: string;
    cityName?: string;
    areaName?: string;
}
export interface Column {
   text?: string;
   name?: string;
src/app/business/enum/types.enum.ts
src/app/business/pipe/pipe.module.ts
File was renamed from src/app/core/pipe/pipe.module.ts
@@ -1,7 +1,7 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TyepHandlePipe } from './tyep-handle.pipe';
import { DateService } from '@core/services/date.service';
import { DateService } from '@business/services/util/date.service';
@NgModule({
  imports: [
src/app/business/pipe/tyep-handle.pipe.ts
File was renamed from src/app/core/pipe/tyep-handle.pipe.ts
@@ -4,8 +4,8 @@
import { Pipe, PipeTransform } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { NzDatePipe } from 'ng-zorro-antd/src/util/nz-date.pipe';
import { DateService } from '@core/services/date.service';
import { Column } from '@core/entity/grid';
import { DateService } from '@business/services/util/date.service';
import { Column } from '@business/entity/grid';
src/app/business/services/http/areacode.service.ts
File was renamed from src/app/core/services/areacode.service.ts
@@ -1,4 +1,3 @@
import { environment } from './../../../environments/environment.prod';
import { Injectable } from '@angular/core';
import { _HttpClient } from '@delon/theme/services/http/http.client';
// Statics
@@ -7,6 +6,7 @@
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
import { environment } from 'environments/environment';
@Injectable()
export class AreacodeService {
src/app/business/services/http/monitor-point.service.ts
File was renamed from src/app/routes/devices/monitor-point/monitor-point.service.ts
@@ -1,11 +1,11 @@
import { ExampleService } from './../../../core/services/example.service';
import { PageBean } from '@business/entity/grid';
import { ExampleService } from '@business/services/util/example.service';
import { _HttpClient } from '@delon/theme';
import { environment } from './../../../../environments/environment.prod';
import { environment } from 'environments/environment';
import { RouteConfigLoadStart } from '@angular/router';
import { Injectable } from '@angular/core';
import { equal } from 'assert';
import { Observable } from 'rxjs/Observable';
import {  PageBean } from '@core/entity/grid';
@Injectable()
export class MonitorPointService {
src/app/business/services/http/organization.service.ts
File was renamed from src/app/routes/systems/organization/organization.service.ts
@@ -1,15 +1,20 @@
import { ExampleService } from './../../../core/services/example.service';
import { Organization } from '@business/entity/data';
import { ExampleService } from '@business/services/util/example.service';
import { _HttpClient } from '@delon/theme';
import { environment } from './../../../../environments/environment';
import { environment } from 'environments/environment';
import { RouteConfigLoadStart } from '@angular/router';
import { Injectable } from '@angular/core';
import { equal } from 'assert';
import { Observable } from 'rxjs/Observable';
import {  PageBean } from '@core/entity/grid';
import {  PageBean } from '@business/entity/grid';
@Injectable()
export class OrganizationService {
  handle: 'list'|'config' = 'list';
  data: Organization;
  title: '组织列表'|'组织配置' = '组织列表';
  private urls = {
      edit: environment.SERVER_BASH_URL + '/organization/page-list',
      save: environment.SERVER_BASH_URL + '/organization/add-or-modify',
@@ -21,8 +26,12 @@
    if (queryText != null && queryText !== '') {
      example.or().andLike({name: 'name', value: '%' + queryText + '%'});
    }
    let orderByClause = '';
    if ( page.getOrderByClause != null && page.getOrderByClause instanceof Function) {
      orderByClause = page.getOrderByClause();
    }
    const param: PageBean = {pageSize: page.pageSize, pageIndex: page.pageIndex, 
        queryParams: example.getSqlParam(), orderByClause: page.getOrderByClause()};
        queryParams: example.getSqlParam(), orderByClause: orderByClause};
        return this.http.get(this.urls.edit, param);
  }
  public save(data: any): Observable<any> {
src/app/business/services/http/sensors.service.ts
New file
@@ -0,0 +1,39 @@
import { ExampleService } from '@business/services/util/example.service';
import { _HttpClient } from '@delon/theme';
import { environment } from 'environments/environment';
import { RouteConfigLoadStart } from '@angular/router';
import { Injectable } from '@angular/core';
import { equal } from 'assert';
import { Observable } from 'rxjs/Observable';
import {  PageBean } from '@business/entity/grid';
@Injectable()
export class SensorsService {
  private urls = {
    edit: environment.SERVER_BASH_URL + '/sensor/page-list',
    save: environment.SERVER_BASH_URL + '/sensor/add-or-modify',
    delete: environment.SERVER_BASH_URL + '/sensor/delete-by-ids'
};
constructor(private http: _HttpClient) { }
 public getPagingList(page: PageBean, queryText: string): Observable<PageBean> {
  const example = new ExampleService();
  if (queryText != null && queryText !== '') {
    example.or().andLike({name: 'name', value: '%' + queryText + '%'});
    example.or().andEqualTo({name: 'version', value: queryText});
  }
  let orderByClause = '';
  if ( page.getOrderByClause != null && page.getOrderByClause instanceof Function) {
    orderByClause = page.getOrderByClause();
  }
  const param: PageBean = {pageSize: page.pageSize, pageIndex: page.pageIndex,
      queryParams: example.getSqlParam(), orderByClause: orderByClause};
      return this.http.get(this.urls.edit, param);
}
public save(data: any): Observable<any> {
      return this.http.post(this.urls.save, data);
}
public delete(...ids: number[]): Observable<any> {
      return this.http.post(this.urls.delete, ids);
}
}
src/app/business/services/http/version.service.ts
File was renamed from src/app/routes/devices/version/version.service.ts
@@ -1,11 +1,11 @@
import { ExampleService } from './../../../core/services/example.service';
import { ExampleService } from '@business/services/util/example.service';
import { PageBean } from '@business/entity/grid';
import { _HttpClient } from '@delon/theme';
import { environment } from './../../../../environments/environment';
import { environment } from 'environments/environment';
import { RouteConfigLoadStart } from '@angular/router';
import { Injectable } from '@angular/core';
import { equal } from 'assert';
import { Observable } from 'rxjs/Observable';
import {  PageBean } from '@core/entity/grid';
@Injectable()
@@ -13,7 +13,9 @@
  private urls = {
      edit: environment.SERVER_BASH_URL + '/device-version/page-list',
      save: environment.SERVER_BASH_URL + '/device-version/add-or-modify',
      delete: environment.SERVER_BASH_URL + '/device-version/delete-by-ids'
      delete: environment.SERVER_BASH_URL + '/device-version/delete-by-ids',
      getSensorIds: environment.SERVER_BASH_URL + '/device-version/get-sensor-ids',
      versionSensorConfig: environment.SERVER_BASH_URL + '/device-version/version-sensor-config'
  };
  constructor(private http: _HttpClient) { }
   public getPagingList(page: PageBean, queryText: string): Observable<PageBean> {
@@ -32,4 +34,11 @@
  public delete(...ids: number[]): Observable<any> {             
        return this.http.post(this.urls.delete, ids);
  }
  public getSensorIds(deviceVersionId: number): Observable< number []> {
      return this.http.get(this.urls.getSensorIds, {deviceVersionId: deviceVersionId});
  }
  public versionSensorConfig(deviceVersionId: number, sensorIds: number[]): Observable<any> {
    const url = this.urls.versionSensorConfig + '/' + deviceVersionId;
    return this.http.post(url, sensorIds);
  }
}
src/app/business/services/util/date.service.ts
src/app/business/services/util/example.service.ts
New file
@@ -0,0 +1,45 @@
import { Injectable } from '@angular/core';
export class  Criteria {
    private static CONDITION_SPLIT = '||';
     private conditions: string[] = [];
     public getConditions(): string[] {
       return this.conditions;
     }
     public addCondition(condition: string, colName:string, ...values: any[]){
        const split = Criteria.CONDITION_SPLIT; // '||'
        this.conditions.push(condition+split + colName + split + values.join(split));
     }
     public andLike(col: { name: string, value: any}): Criteria{
         this.addCondition('andLike', col.name, col.value);
         return this;
     }
     public andEqualTo(col: { name: string, value: any}): Criteria{
        this.addCondition('andEqualTo', col.name, col.value);
        return this;
     }
}
@Injectable()
export class ExampleService {
  private static OR_SPLIT = 'or|';
  private static  CRITERIA_SPLIT = '|||';
  private criterion: Criteria[] = [];
  public getSqlParam(): string {
      let whereSql = '';
     for (const cri of this.criterion){
            const conditions = cri.getConditions();
            whereSql += ExampleService.OR_SPLIT + conditions.join(ExampleService.CRITERIA_SPLIT);
     }
     return encodeURI(whereSql);
  }
  constructor() { }
    public or() {
        const cri = new Criteria();
        this.criterion.push(cri);
        return cri;
    }
}
src/app/business/services/util/tools.service.ts
src/app/core/services/example.service.ts
File was deleted
src/app/routes/devices/devices.module.ts
@@ -1,6 +1,8 @@
import { MonitorPointService } from 'app/routes/devices/monitor-point/monitor-point.service';
import { ToolsService } from './../../core/services/tools.service';
import { SensorsService } from '@business/services/http/sensors.service';
import { OrganizationService } from '@business/services/http/organization.service';
import { MonitorPointService } from '@business/services/http/monitor-point.service';
import { VersionService } from '@business/services/http/version.service';
import { AreacodeService } from '@business/services/http/areacode.service';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BasicInfoComponent } from './basic-info/basic-info.component';
@@ -8,15 +10,15 @@
import { MonitorPointComponent } from './monitor-point/monitor-point.component';
import {RouterModule, Routes} from '@angular/router';
import { SharedModule } from '@shared/shared.module';
import { VersionService } from 'app/routes/devices/version/version.service';
import { Version } from '@angular/compiler/src/util';
import { _HttpClient } from '@delon/theme/services/http/http.client';
import { PipeModule } from '@core/pipe/pipe.module';
import { PipeModule } from '@business/pipe/pipe.module';
import { FormBuilder } from '@angular/forms';
import { VersionEditComponent } from './version/version-edit/version-edit.component';
import { MonitorPointEditComponent } from './monitor-point/monitor-point-edit/monitor-point-edit.component';
import { VersionSensorConfigComponent } from './version/version-sensor-config/version-sensor-config.component';
const COMPONENTS_NOROUNT = [  VersionEditComponent, MonitorPointEditComponent  ];
const COMPONENTS_NOROUNT = [  VersionEditComponent, MonitorPointEditComponent, VersionSensorConfigComponent  ];
const routes: Routes = [
  {
@@ -41,9 +43,11 @@
    BasicInfoComponent,
    VersionComponent,
    MonitorPointComponent,
       ...COMPONENTS_NOROUNT
    ...COMPONENTS_NOROUNT
  ],
  providers: [ToolsService, VersionService, MonitorPointService, _HttpClient, FormBuilder],
  providers: [OrganizationService, VersionService,
    SensorsService, AreacodeService, MonitorPointService,
    _HttpClient, FormBuilder],
  entryComponents: COMPONENTS_NOROUNT
})
export class DevicesModule { }
src/app/routes/devices/monitor-point/monitor-point-edit/monitor-point-edit.component.html
@@ -1,3 +1,77 @@
<p>
  monitor-point-edit works!
</p>
<div class="modal-header">
  <div class="modal-title">{{ data.id != null ? '编辑' : '添加'}} - 组织</div>
</div>
<form [formGroup]="validateForm" (ngSubmit)="save($event,validateForm.value,validateForm.valid)" nz-form [nzType]="'horizontal'">
  <div nz-form-item nz-row class="mb-sm">
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label nz-form-item-required>名称</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="7" nzHasFeedback>
      <nz-input formControlName="name" maxlength="20" [nzPlaceHolder]="'监控点名称'">
      </nz-input>
    </div>
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label nz-form-item-required>组织</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="6" nzHasFeedback>
      <nz-select formControlName="organizationId" [nzPlaceHolder]="'选择 组织(输入名称搜索)'"
        nzAllowClear [nzFilter]="false" nzShowSearch
              formControlName="organizationId" (nzSearchChange)="OrgSelectChange($event)" [nzNotFoundContent]="'无法找到'" >
        <nz-option *ngFor="let option of orgOptions" [nzLabel]="option.name" [nzValue]="option.id" [nzDisabled]="option.disabled">
        </nz-option>
      </nz-select>
    </div>
  </div>
  <div nz-form-item nz-row class="mb-sm">
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label nz-form-item-required>省/市/区</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="7" nzHasFeedback>
      <nz-cascader [class.class123]="true" (nzChange)="setAreaCodes($event)" formControlName="_areas" (nzLoad)="areaLazyLoad($event)"
      [nzPlaceHolder]="'选择 省/市/区'" >
      </nz-cascader>
    </div>
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label>详细地址</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="9" nzHasFeedback>
      <nz-input formControlName="address" maxlength="20"
      [nzPlaceHolder]="'详细地址(例):人民路120号'">
      </nz-input>
    </div>
  </div>
  <div nz-form-item nz-row class="mb-sm">
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label>经度</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="5" nzHasFeedback>
        <nz-input-number [ngStyle]="{'width': '100%' }" formControlName="longitude"  [nzMin]="-180" [nzMax]="180" [nzStep]="0.000001">
          </nz-input-number>
    </div>
    <div nz-form-control nz-col [nzSpan]="2" nzHasFeedback></div>
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label>纬度</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="5" nzHasFeedback>
          <nz-input-number [ngStyle]="{'width': '100%' }" formControlName="latitude" [nzMin]="-90" [nzMax]="90" [nzStep]="0.000001">
          </nz-input-number>
    </div>
  </div>
  <div nz-form-item nz-row class="mb-sm">
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label>备注</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="16" nzHasFeedback>
      <nz-input formControlName="description" maxlength="100"></nz-input>
    </div>
  </div>
  <div class="modal-footer">
    <button nz-button type="button" (click)="close()">关闭</button>
    <button nz-button [nzType]="'primary'" [nzLoading]="isSaving">
      <span>
        保存
        <span *ngIf="isSaving">中</span>
      </span>
    </button>
  </div>
</form>
src/app/routes/devices/monitor-point/monitor-point-edit/monitor-point-edit.component.ts
@@ -1,4 +1,11 @@
import { MonitorPoint } from '@business/entity/data';
import { PageBean } from '@business/entity/grid';
import { OrganizationService } from '@business/services/http//organization.service';
import { AreacodeService } from '@business/services/http/areacode.service';
import { Component, OnInit } from '@angular/core';
import { NzMessageService, NzModalSubject } from 'ng-zorro-antd';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
import { CascaderOption } from 'ng-zorro-antd/src/cascader/nz-cascader.component';
@Component({
  selector: 'app-monitor-point-edit',
@@ -7,9 +14,116 @@
})
export class MonitorPointEditComponent implements OnInit {
  constructor() { }
   orgOptions = [];
  data: MonitorPoint;
  isSaving = false;
  validateForm: FormGroup;
  constructor(
    private subject: NzModalSubject,
    private formBuilder: FormBuilder,
    private areacodeService: AreacodeService,
    private organizationService: OrganizationService
    ) { }
  ngOnInit() {
    const data = this.data;
    const areaNames = data.areaNames;
    let _areas = null;
    if (areaNames != null) {
      _areas = {
       label: Object.values(areaNames).join('/'),
       value: data.areaCode
      };
    }
     this.OrgSelectChange(null);
     const validates: MonitorPoint|object  = {
          name: [data.name, [Validators.required] ],
          organizationId: [data.organizationId, [Validators.required]],
          longitude: [data.longitude],
          latitude: [data.latitude],
          address: [data.address ],
          _areas: [_areas,  [Validators.required]],
          description: [data.description ]
     };
     this.validateForm = this.formBuilder.group(
         validates
     );
  }
  close() {
       this.subject.destroy();
  }
  save($event, value, valid) {
      $event.preventDefault();
      if (valid) {
        for (const i in this.validateForm.controls) {
          this.validateForm.controls[ i ].disable();
        }
        this.isSaving = true;
        Object.keys(value).forEach( (key: string) => {
              // '_'为前缀的为自定义属性
              if (!key.startsWith('_') && value[key] != null) {
                   this.data[key] = value[key];
              }
        } );
        this.subject.next( this );
      }else {
        this.validate();
      }
  }
  validate() {
     for (const i in this.validateForm.controls) {
       this.validateForm.controls[ i ].markAsDirty();
     }
  }
  areaLazyLoad(event: { option: CascaderOption, index: number, resolve: (children: CascaderOption[]) => void, reject: () => void }) {
    const index = event['index'];
    const option = event.option;
    switch (index) {
       case -1:
       this.areacodeService.getProvinces().subscribe(
         (res: {label: string, value: string}[]) => {
             event.resolve( res );
         }
       ); break;
       case 0:
       this.areacodeService.getCities(option.value).subscribe(
         (res: {label: string, value: string}[]) => {
             event.resolve( res );
         }
       ); break;
       case 1:
       this.areacodeService.getAreas(option.value).subscribe(
         (res: {label: string, value: string}[]) => {
             event.resolve( res );
         }
       ); break;
    }
  }
  setAreaCodes(codes: string[]) {
      this.data.provinceCode = codes[0];
      this.data.cityCode = codes[1];
      this.data.areaCode = codes[2];
  }
  OrgSelectChange(text) {
      const pageBean: PageBean = {pageIndex: 0, pageSize: 20};
      this.organizationService.getPagingList(pageBean, text).subscribe(
        (res: PageBean) => {
             if (res != null && res.data != null) {
                 this.orgOptions = res.data;
             }
             const organization = this.data.organization;
             if (organization != null && text == null) {
                 const num: number = this.orgOptions.filter(
                     (item: any) => {
                        return item.id === organization.id;
                     }
                 ).length;
                 if ( num === 0 ) {
                    this.orgOptions.push(organization);
                 }
             }
        }
     );
  }
}
src/app/routes/devices/monitor-point/monitor-point.component.html
@@ -54,7 +54,7 @@
                    <td nz-td>
                            <a (click)="addOrModify(row)">编辑</a>
                            <span nz-table-divider></span>
                              <nz-popconfirm [nzTitle]="'确定要删除该用户吗?'" [nzOkText]="'Yes'" [nzCancelText]="'No'" (nzOnConfirm)="delete(row.id)" >
                              <nz-popconfirm [nzTitle]="'确定要删除该'+grid.title+'吗?'" [nzOkText]="'Yes'" [nzCancelText]="'No'" (nzOnConfirm)="delete(row.id)" >
                                <a nz-popconfirm>删除</a>
                              </nz-popconfirm>
                     </td>
src/app/routes/devices/monitor-point/monitor-point.component.ts
@@ -1,33 +1,19 @@
import { DataType, AreaNames } from './../../../core/entity/grid';
import {  AreaNames, MonitorPoint } from '@business/entity/data';
import { Version, ValueTransformer } from '@angular/compiler/src/util';
import { Subject } from 'rxjs/Subject';
import { ToolsService } from '@core/services/tools.service';
import { Component, OnInit } from '@angular/core';
import { NzMessageService, NzModalService } from 'ng-zorro-antd';
import { ModalHelper } from '@delon/theme';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../../../environments/environment';
import { DateService } from '@core/services/date.service';
import { VersionService } from 'app/routes/devices/version/version.service';
import { Types } from '@core/enum/types.enum';
import { Column, Grid, PageBean } from '@core/entity/grid';
import { environment } from 'environments/environment';
import { VersionService } from '@business/services/http/version.service';
import { Types } from '@business/enum/types.enum';
import { Column, Grid, PageBean, DataType } from '@business/entity/grid';
import { filter } from 'rxjs/operators/filter';
import { MonitorPointEditComponent } from 'app/routes/devices/monitor-point/monitor-point-edit/monitor-point-edit.component';
import { MonitorPointService } from 'app/routes/devices/monitor-point/monitor-point.service';
import { MonitorPointService } from '@business/services/http/monitor-point.service';
interface MonitorPoint {
  address?: any|Column;
  areaCode?: any|Column;
  cityCode?: any|Column;
  description?: any|Column;
  id?: any|Column;
  isDelete?: any|Column;
  latitude?: any|Column;
  longitude?: any|Column;
  name?: any|Column;
  organizationId?: any|Column;
  provinceCode?: any|Column;
}
@Component({
  selector: 'app-monitor-point',
  templateUrl: './monitor-point.component.html',
@@ -36,19 +22,36 @@
export class MonitorPointComponent implements OnInit {
  private organization: MonitorPoint;
  private monitorPoint: MonitorPoint;
  grid: Grid<MonitorPoint> = new Grid(null);
  queryMap = { text: '请输入名称', value: ''};
  queryTextStream: Subject<string> = new Subject<string>();
    private initPage() {
    this.organization =  {
    this.monitorPoint =  {
      name: {
        text: '名称',
        width: '120px'
        width: '300px'
      },
      address: {
        text: '地址',
        width: '300px',
        format: (value: any, col: Column, row: any) => {
              value = value == null ? '' : value ;
              if (row['areaNames'] != null) {
                return row['areaNames']['provinceName'] + row['areaNames']['cityName'] + row['areaNames']['areaName'] + value;
              } else {
                return value;
              }
        }
      },
      description: {
        text: '描述',
        width: '300px'
      }
    };
    this.grid.title = '设备型号';
    this.grid.setColumns(this.organization);
    this.grid.title = '监控点';
    this.grid.setColumns(this.monitorPoint);
    this.grid.pageSize = 10;
  }
  constructor(
@@ -99,13 +102,12 @@
    if ( d != null) {
      Object.assign(data, d);
    }
    const cols = this.organization;
    this.modalHelper.static(MonitorPointEditComponent, { cols , data }).subscribe(
    this.modalHelper.static(MonitorPointEditComponent, { data }).subscribe(
      ( ret: { data: any, close: Function} ) => {
      // 修改状态
      if (ret.data['index'] != null ) {
          const index: number = ret.data['index'] ;
          const origData = this.grid.getData()[index];
          const origData = this.grid.data[index];
          const isModified =  Object.keys(origData).some(
            (key: string) => {
                return ret.data[key] !== origData[key];    
@@ -114,7 +116,7 @@
          // 未作修改
          if (!isModified) { 
            ret.close();
            this.msgSrv.success('组织未作任何修改!');
            this.msgSrv.success(this.grid.title + '未作任何修改!');
            return;
          }
      }
@@ -123,7 +125,7 @@
            if (res.code === 1) {
              this.load();
              ret.close();
              this.msgSrv.success('组织保存成功!');
              this.msgSrv.success(this.grid.title + '保存成功!');
            }
         }
      );
@@ -135,7 +137,7 @@
      ( res: any) => {
         if (res.code === 1) {
           this.load();
           this.msgSrv.success('设备型号删除成功!');
           this.msgSrv.success(this.grid.title + '删除成功!');
         }
      }
   );
@@ -172,10 +174,4 @@
     }
     this.load();
  }
  reset(ls: any[]) {
    for (const item of ls) item.value = false;
    this.load(true);
  }
}
src/app/routes/devices/version/version-edit/version-edit.component.html
@@ -15,7 +15,7 @@
      </label>
    </div>
     <div nz-form-control nz-col [nzSpan]="3" nzHasFeedback>
         <nz-input-number formControlName="{{ cols.version.name }}" [nzMin]="1" [nzMax]="100" [nzStep]="1">
         <nz-input-number style="width: 100%;" formControlName="{{ cols.version.name }}" [nzMin]="1" [nzMax]="100" [nzStep]="1">
         </nz-input-number>
    </div>
  </div> 
@@ -23,10 +23,11 @@
      <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
            <label nz-form-item-required>{{ cols.createTime.text }}</label>
      </div>
      <div nz-form-control nz-col [nzSpan]="7" nzHasFeedback>
        <nz-datepicker nzSize="large"  style="width: 90%;" formControlName="{{ cols.createTime.name }}" nzShowTime  [nzPlaceHolder]="'选择时间'"
      <div nz-form-control nz-col [nzSpan]="6" nzHasFeedback>
        <nz-datepicker nzSize="large"  style="width: 100%;" formControlName="{{ cols.createTime.name }}" nzShowTime  [nzPlaceHolder]="'选择时间'"
        [nzFormat]="'YYYY-MM-DD HH:mm:ss'" ></nz-datepicker>
      </div>
      <div nz-form-control nz-col [nzSpan]="1" nzHasFeedback></div>
      <div nz-form-label nz-col [nzSpan]="4">
        <label>{{ cols.description.text }}</label>
      </div>
src/app/routes/devices/version/version-edit/version-edit.component.ts
@@ -1,7 +1,7 @@
import { DeviceVersion } from '@business/entity/data';
import { Component, OnInit } from '@angular/core';
import { NzMessageService, NzModalSubject } from 'ng-zorro-antd';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { DeviceVersion } from 'app/routes/devices/version/version.component';
@Component({
  selector: 'app-version-edit',
@@ -21,6 +21,9 @@
    ) { }
  ngOnInit() {
     if (this.data.createTime == null) {
      this.data.createTime = new Date().getTime();
     }
     const validates: DeviceVersion = {
          name: [this.data.name, [Validators.required] ],
          version: [this.data.version == null ? 1 : this.data.version, [Validators.required] ],
src/app/routes/devices/version/version-sensor-config/version-sensor-config.component.html
New file
@@ -0,0 +1,39 @@
<div class="modal-header">
  <div class="modal-title">配置传感器</div>
</div>
<nz-table #nzTable [nzDataSource]="grid.data"  [nzPageSize]="8"
  [nzLoading]="grid.loading" [nzShowTotal]="true" >
  <thead nz-thead>
    <tr>
      <th nz-th [nzCheckbox]="true">
        <label nz-checkbox [(ngModel)]="grid.allChecked" [nzIndeterminate]="grid.indeterminate" (ngModelChange)="grid.checkAll($event)"></label>
      </th>
      <th nz-th *ngFor="let col of grid.columns" [ngStyle]="{'width':col.width,'text-align':col['align'] === undefined?'left':col.align}">
        <span>{{ col.text }}</span>
        <nz-table-sort *ngIf="col.isSort" [(nzValue)]="col.sort" (nzValueChange)="sort(col.name,$event)"></nz-table-sort>
      </th>
    </tr>
  </thead>
  <tbody nz-tbody>
    <tr nz-tbody-tr *ngFor="let row of nzTable.data">
      <td nz-td [nzCheckbox]="true">
        <label nz-checkbox [(ngModel)]="row.checked" (ngModelChange)="grid.refreshStatus($event)"></label>
      </td>
      <td nz-td *ngFor="let col of grid.columns" [ngStyle]="{'width':col.width,'text-align':col['align'] === undefined?'left':col.align}">
        <span [ngSwitch]="col.type">
          <!-- 要使用管道,无法自动生成 -->
          <span *ngSwitchDefault> {{ row[col.name]|tyepHandle:col:row }} </span>
          <!-- 要使用管道,无法自动生成 -->
        </span>
      </td>
    </tr>
  </tbody>
</nz-table>
<div class="modal-footer">
  <button nz-button type="button" (click)="close()">关闭</button>
  <button nz-button [nzType]="'primary'" (click)="save($event)" [nzLoading]="isSaving">
         <span >
            保存<span *ngIf="isSaving" >中</span>
         </span>
  </button>
</div>
src/app/routes/devices/version/version-sensor-config/version-sensor-config.component.ts
New file
@@ -0,0 +1,105 @@
import { NzModalSubject } from 'ng-zorro-antd';
import { Component, OnInit } from '@angular/core';
import { SensorsService } from '@business/services/http/sensors.service';
import { PageBean,  Grid } from '@business/entity/grid';
import { Sensor } from '@business/entity/data';
import { Subject } from 'rxjs/Subject';
@Component({
  selector: 'app-version-sensor-config',
  templateUrl: './version-sensor-config.component.html',
  styles: []
})
export class VersionSensorConfigComponent implements OnInit {
  deviceVersionId: number;
  selectedSensorIds: number[];
  isSaving = false;
  grid: Grid<Sensor> = new Grid(null);
  private initPage() {
    const sensor: Sensor = {
      name: {
        text: '名称',
        width: '200px'
      },
      key: {
        text: '键值',
        width: '60px'
      },
      lower: {
        text: '下限值',
        width: '90px'
      },
      upper: {
        text: '上限值',
        width: '90px'
      },
      unit: {
        text: '单位',
        width: '100px'
      },
      description: {
        text: '描述'
      }
    };
    this.grid.title = '传感器';
    this.grid.setColumns(sensor);
    this.grid.pageSize = 0;
  }
  constructor(
    private subject: NzModalSubject,
    private sensorsService: SensorsService,
  ) { }
  ngOnInit() {
    this.initPage();
    this.load();
  }
  load() {
    // 延时加载避免ExpressionChangedAfterItHasBeenCheckedError
    setTimeout(() => {
      this.grid.loading = true;
    }, 1);
    this.sensorsService.getPagingList(this.grid, null).subscribe(
      (res: PageBean) => {
        this.grid.loading = true;
        if (res != null && res.data != null) {
          this.grid.initData(res);
          this.grid.data.map(
            (row: any) => {
               row['checked'] = this.selectedSensorIds.filter(
                (id: number) => {
                  return row.id === id;
                }
              ).length > 0;
            }
          );
          this.grid.refreshStatus();
          setTimeout(() => {
            this.grid.loading = false;
          }, 1);
        }
      }
    );
  }
  close() {
    this.subject.destroy();
  }
  save($event, value, valid) {
    $event.preventDefault();
    this.subject.next( this );
  }
  sort(field: string, value: string) {
    // 删除当前field
    this.grid.sorts = this.grid.sorts.filter(
      (fn: string) => {
        return fn !== field;
      }
    );
    // 如果value不为null,在排序数组最后加上filed
    if (value != null) {
      this.grid.sorts.push(field);
    }
    this.load();
  }
}
src/app/routes/devices/version/version.component.html
@@ -54,9 +54,11 @@
                    <td nz-td>
                            <a (click)="addOrModify(row)">编辑</a>
                            <span nz-table-divider></span>
                              <nz-popconfirm [nzTitle]="'确定要删除该用户吗?'" [nzOkText]="'Yes'" [nzCancelText]="'No'" (nzOnConfirm)="delete(row.id)" >
                              <nz-popconfirm [nzTitle]="'确定要删除该'+grid.title+'吗?'" [nzOkText]="'Yes'" [nzCancelText]="'No'" (nzOnConfirm)="delete(row.id)" >
                                <a nz-popconfirm>删除</a>
                              </nz-popconfirm>
                              <span nz-table-divider></span>
                              <a (click)="configSensor(row)">配置传感器</a>
                     </td>
                </tr>
              </tbody>
src/app/routes/devices/version/version.component.ts
@@ -1,26 +1,20 @@
import { DataType } from './../../../core/entity/grid';
import { VersionSensorConfigComponent } from './version-sensor-config/version-sensor-config.component';
import { DataType } from '@business/entity/grid';
import { DeviceVersion, Sensor } from '@business/entity/data';
import { Version } from '@angular/compiler/src/util';
import { Subject } from 'rxjs/Subject';
import { ToolsService } from '@core/services/tools.service';
import { ToolsService } from '@business/services/util/tools.service';
import { Component, OnInit } from '@angular/core';
import { NzMessageService, NzModalService } from 'ng-zorro-antd';
import { ModalHelper } from '@delon/theme';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../../../environments/environment';
import { DateService } from '@core/services/date.service';
import { VersionService } from 'app/routes/devices/version/version.service';
import { Types } from '@core/enum/types.enum';
import { Column, Grid, PageBean } from '@core/entity/grid';
import { filter } from 'rxjs/operators/filter';
import { DateService } from '@business/services/util/date.service';
import { VersionService } from '@business/services/http/version.service';
import { Types } from '@business/enum/types.enum';
import { Column, Grid, PageBean } from '@business/entity/grid';
import { VersionEditComponent } from 'app/routes/devices/version/version-edit/version-edit.component';
export interface DeviceVersion {
  createTime?: Column|any;
  description?: Column|any;
  id?: Column|any;
  name?: Column|any;
  version?: Column|any;
}
@Component({
  selector: 'app-version',
  templateUrl: './version.component.html',
@@ -115,7 +109,7 @@
            if (res.code === 1) {
              this.load();
              ret.close();
              this.msgSrv.success('设备型号保存成功!');
              this.msgSrv.success(this.grid.title + '保存成功!');
            }
         }
      );
@@ -127,7 +121,7 @@
      ( res: any) => {
         if (res.code === 1) {
           this.load();
           this.msgSrv.success('设备型号删除成功!');
           this.msgSrv.success(this.grid.title + '删除成功!');
         }
      }
   );
@@ -164,10 +158,55 @@
     }
     this.load();
  }
    configSensor(data) {
     const deviceVersionId = data.id;
     this.versionService.getSensorIds(deviceVersionId).subscribe(
        (selectedSensorIds: number[]) => {
          this.modalHelper.static(VersionSensorConfigComponent, { deviceVersionId, selectedSensorIds }).subscribe(
            ( ret: { grid: Grid<Sensor>, close: Function} ) => {
            // 获取选中id
            const selectedIds = ret.grid.data.filter(
              (r: any) => {
                 return r['checked'] === true;
              }
            ).map(
              (r: any) => {
                return r['id'];
             }
            );
            // 长度不一样,已经修改
            let isModified = selectedIds.length !== selectedSensorIds.length;
            // 长度一样再比较内容
            if (!isModified) {
              isModified = !selectedIds.every(
                (id: number) => {
                    // console.log('id:' + id);
                    const result = selectedSensorIds.some(
                      (sid: number) => {
                          return id === sid ;
                      }
                     );
                    // console.log('result:' + result);
                    return result;
                }
               );
            }
            if ( !isModified ) {
              this.msgSrv.success(this.grid.title + '配置未修改!');
              ret.close();
              return ;
            }
            this.versionService.versionSensorConfig(deviceVersionId, selectedIds).subscribe(
               ( res: any) => {
                  if (res.code === 1) {
                    ret.close();
                    this.msgSrv.success(this.grid.title + '配置成功!');
                  }
               }
            );
          });
        }
     );
  reset(ls: any[]) {
    for (const item of ls) item.value = false;
    this.load(true);
  }
    }
}
src/app/routes/routes.module.ts
@@ -4,14 +4,24 @@
import { RouterModule } from '@angular/router';
import { SharedModule } from '@shared/shared.module';
import { environment } from '../../environments/environment';
import { routes } from './routes';
import { DashboardV1Component } from './dashboard/v1/v1.component';
import { DashboardAnalysisComponent } from './dashboard/analysis/analysis.component';
import { DashboardMonitorComponent } from './dashboard/monitor/monitor.component';
import { DashboardWorkplaceComponent } from './dashboard/workplace/workplace.component';
import { CoreModule } from '@core/core.module';
// region: zorro modules
// Statics
import 'rxjs/add/observable/throw';
// Operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/filter';
@NgModule({
    imports: [
        SharedModule,
src/app/routes/sensors/basic-info/basic-info.component.html
@@ -1,3 +1,66 @@
<p>
  sensor basic-info works!
</p>
<pro-header [title]="grid.title"></pro-header>
<nz-card [nzBordered]="false">
        <div class="mb-md">
                <button nz-button (click)="addOrModify()" [nzType]="'primary'" [nzSize]="'large'">
                    <i class="anticon anticon-plus"></i><span>新建</span>
                </button>
                <ng-container *ngIf="grid.selectedIndexs.length > 0"> &nbsp;
                <button nz-button [nzSize]="'large'" (click)="deleteSelected()">批量删除</button>
                </ng-container>
                <nz-input [ngStyle]="{'width': '280px','float':'right'}" [(ngModel)]="queryMap.value" name=""  [nzPlaceHolder]="queryMap.text"
                (keyup)="queryTextChanged($event)" (change)="queryTextChanged($event)" ></nz-input>
        </div>
        <div class="mb-md">
                <nz-alert *ngIf="grid.selectedIndexs.length > 0" [nzType]="'info'" [nzShowIcon]="true">
                    <span alert-body>
                         已选择<strong class="text-primary">{{grid.selectedIndexs.length}}</strong>项
                    </span>
                </nz-alert>
    </div>
    <nz-table #nzTable
              [nzAjaxData]="grid.data"
              [nzTotal]="grid.total"
              [(nzPageIndex)]="grid.pageIndex"
              [(nzPageSize)]="grid.pageSize"
              [nzLoading]="grid.loading"
              [nzShowTotal]="true"
              (nzPageIndexChange)="load()">
              <thead nz-thead>
                  <tr>
                    <th nz-th [nzCheckbox]="true">
                        <label nz-checkbox [(ngModel)]="grid.allChecked" [nzIndeterminate]="grid.indeterminate" (ngModelChange)="grid.checkAll($event)"></label>
                    </th>
                    <th nz-th *ngFor="let col of grid.columns"
                     [ngStyle]="{'width':col.width,'text-align':col['align'] === undefined?'left':col.align}" >
                           <span>{{ col.text }}</span>
                           <nz-table-sort *ngIf="col.isSort"   [(nzValue)]="col.sort" (nzValueChange)="sort(col.name,$event)"></nz-table-sort>
                    </th>
                    <th nz-th><span>操作</span></th>
                  </tr>
              </thead>
              <tbody nz-tbody>
                <tr nz-tbody-tr *ngFor="let row of nzTable.data">
                    <td nz-td [nzCheckbox]="true">
                       <label nz-checkbox [(ngModel)]="row.checked" (ngModelChange)="grid.refreshStatus($event)"></label>
                    </td>
                    <td nz-td *ngFor="let col of grid.columns"
                    [ngStyle]="{'width':col.width,'text-align':col['align'] === undefined?'left':col.align}">
                        <span [ngSwitch]="col.type">
                             <!-- 要使用管道,无法自动生成 -->
                            <span *ngSwitchDefault> {{ row[col.name]|tyepHandle:col:row }} </span>
                             <!-- 要使用管道,无法自动生成 -->
                        </span>
                    </td>
                    <td nz-td>
                            <a (click)="addOrModify(row)">编辑</a>
                            <span nz-table-divider></span>
                              <nz-popconfirm [nzTitle]="'确定要删除该'+grid.title+'吗?'" [nzOkText]="'Yes'" [nzCancelText]="'No'" (nzOnConfirm)="delete(row.id)" >
                                <a nz-popconfirm>删除</a>
                              </nz-popconfirm>
                     </td>
                </tr>
              </tbody>
              </nz-table>
  </nz-card>
src/app/routes/sensors/basic-info/basic-info.component.ts
@@ -1,4 +1,14 @@
import { NzModalService, NzMessageService } from 'ng-zorro-antd';
import { PageBean } from '@business/entity/grid';
import {  Sensor } from '@business/entity/data';
import { ModalHelper } from '@delon/theme';
import { SensorsService } from '@business/services/http/sensors.service';
import { Column, Grid } from '@business/entity/grid';
import { Component, OnInit } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { SensorEditComponent } from './sensor-edit/sensor-edit.component';
@Component({
  selector: 'app-basic-info',
@@ -7,9 +17,159 @@
})
export class BasicInfoComponent implements OnInit {
  constructor() { }
  grid: Grid<Sensor> = new Grid(null);
  queryMap = { text: '请输入名称', value: ''};
  queryTextStream: Subject<string> = new Subject<string>();
    private initPage() {
    const sensor: Sensor =  {
      name: {
        text: '名称',
        width: '200px'
      },
      key: {
        text: '键值',
        width: '60px'
      },
      lower: {
        text: '下限值',
        width: '90px'
      },
      upper: {
        text: '上限值',
        width: '90px'
      },
      unit: {
        text: '单位',
        width: '100px'
      },
      description: {
        text: '描述'
      }
    };
    this.grid.title = '传感器';
    this.grid.setColumns(sensor);
    this.grid.pageSize = 10;
  }
  constructor(
    private sensorsService: SensorsService,
    private confirmServ: NzModalService,
    public msgSrv: NzMessageService,
    private modalHelper: ModalHelper,
  ) {}
  ngOnInit() {
    this.initPage();
    this.queryTextStream
    .debounceTime(500)
    .distinctUntilChanged()
    .subscribe(queryText => {
        this.load();
    });
  }
  queryTextChanged($event) {
      this.queryTextStream.next(this.queryMap.value);
  }
  load(reload: boolean = false) {
    if (reload) {
      this.grid.pageIndex = 1 ;
    }
    // 延时加载避免ExpressionChangedAfterItHasBeenCheckedError
    setTimeout(() => {
      this.grid.loading = true;
     }, 1);
    this.sensorsService.getPagingList(this.grid, this.queryMap.value).subscribe(
       (res: PageBean) => {
            this.grid.loading = true;
            if (res != null && res.data != null) {
              this.grid.initData(res);
              this.grid.refreshStatus();
              setTimeout(() => {
                this.grid.loading = false;
               }, 1);
            }
       }
    );
  }
// rowData为null时,为新增
  addOrModify(d) {
    const data = {};
    if ( d != null) {
      Object.assign(data, d);
    }
    this.modalHelper.static(SensorEditComponent, { data }).subscribe(
      ( ret: { data: any, close: Function} ) => {
      // 修改状态
      if (ret.data['index'] != null ) {
          const index: number = ret.data['index'] ;
          const origData = this.grid.getData()[index];
          const isModified =  Object.keys(origData).some(
            (key: string) => {
                return ret.data[key] !== origData[key];
            }
          );
          // 未作修改
          if (!isModified) {
            ret.close();
            this.msgSrv.success(this.grid.title + '未作任何修改!');
            return;
          }
      }
      this.sensorsService.save(ret.data).subscribe(
         ( res: any) => {
            if (res.code === 1) {
              this.load();
              ret.close();
              this.msgSrv.success(this.grid.title + '保存成功!');
            }
         }
      );
    });
  }
  delete(...id: number[]) {
    this.sensorsService.delete( ...id ).subscribe(
      ( res: any) => {
         if (res.code === 1) {
           this.load();
           this.msgSrv.success(this.grid.title + '删除成功!');
         }
      }
   );
  }
   deleteSelected() {
    this.confirmServ.confirm({
      title: '批量删除',
      content: '注意:数据一旦删除,将不可恢复!',
      okText: '确定',
      cancelText: '取消'
    }).on('onOk', () => {
       if (this.grid.selectedIndexs != null && this.grid.selectedIndexs.length > 0) {
          const ids = this.grid.selectedIndexs.map(
              (index: number) => {
                  const id = this.grid.data[index].id;
                  return Number.parseInt(id);
              }
          );
          this.delete( ...ids );
       }
    });
  }
  sort(field: string, value: string) {
    // 删除当前field
    this.grid.sorts = this.grid.sorts.filter(
       (fn: string) => {
          return fn !== field;
       }
     );
     // 如果value不为null,在排序数组最后加上filed
     if ( value != null ) {
        this.grid.sorts.push(field);
     }
     this.load();
  }
}
src/app/routes/sensors/basic-info/sensor-edit/sensor-edit.component.html
New file
@@ -0,0 +1,64 @@
<div class="modal-header">
  <div class="modal-title">{{ data.id != null ? '编辑' : '添加'}} - 组织</div>
</div>
<form [formGroup]="validateForm" (ngSubmit)="save($event,validateForm.value,validateForm.valid)" nz-form [nzType]="'horizontal'">
  <div nz-form-item nz-row class="mb-sm">
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label nz-form-item-required>名称</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="10" nzHasFeedback>
      <nz-input formControlName="name" maxlength="20" [nzPlaceHolder]="'名称'">
      </nz-input>
    </div>
  </div>
  <div nz-form-item nz-row class="mb-sm">
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label nz-form-item-required>键值</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="5" nzHasFeedback>
      <nz-input formControlName="key" maxlength="20" [nzPlaceHolder]="'键值'">
      </nz-input>
    </div>
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label>单位</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="7" nzHasFeedback>
      <nz-input formControlName="unit" maxlength="20" [nzPlaceHolder]="'单位'">
      </nz-input>
    </div>
  </div>
  <div nz-form-item nz-row class="mb-sm">
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label>下限</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="5" nzHasFeedback>
        <nz-input-number [ngStyle]="{'width': '100%' }" formControlName="lower" >
          </nz-input-number>
    </div>
    <div nz-form-control nz-col [nzSpan]="2" nzHasFeedback></div>
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label>上限</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="5" nzHasFeedback>
          <nz-input-number [ngStyle]="{'width': '100%' }" formControlName="upper" >
          </nz-input-number>
    </div>
  </div>
  <div nz-form-item nz-row class="mb-sm">
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label>备注</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="16" nzHasFeedback>
      <nz-input formControlName="description" maxlength="100"></nz-input>
    </div>
  </div>
  <div class="modal-footer">
    <button nz-button type="button" (click)="close()">关闭</button>
    <button nz-button [nzType]="'primary'" [nzLoading]="isSaving">
      <span>
        保存
        <span *ngIf="isSaving">中</span>
      </span>
    </button>
  </div>
</form>
src/app/routes/sensors/basic-info/sensor-edit/sensor-edit.component.ts
New file
@@ -0,0 +1,64 @@
import { NzModalSubject } from 'ng-zorro-antd';
import { Sensor } from '@business/entity/data';
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { SensorsService } from '@business/services/http/sensors.service';
@Component({
  selector: 'app-sensor-edit',
  templateUrl: './sensor-edit.component.html',
  styles: []
})
export class SensorEditComponent implements OnInit {
  data: Sensor;
  isSaving = false;
  validateForm: FormGroup;
  constructor(
    private subject: NzModalSubject,
    private formBuilder: FormBuilder,
    private organizationService: SensorsService
    ) { }
  ngOnInit() {
     const data = this.data;
     const validates: Sensor  = {
          name: [data.name, [Validators.required] ],
          key: [data.key, [Validators.required] ],
          lower: [data.lower ],
          upper: [data.upper ],
          unit: [data.unit ],
          description: [data.description ]
     };
     this.validateForm = this.formBuilder.group(
         validates
     );
  }
  close() {
       this.subject.destroy();
  }
  save($event, value, valid) {
      $event.preventDefault();
      if (valid) {
        for (const i in this.validateForm.controls) {
          this.validateForm.controls[ i ].disable();
        }
        this.isSaving = true;
        Object.keys(value).forEach( (key: string) => {
              // '_'为前缀的为自定义属性
              if (!key.startsWith('_') && value[key] != null) {
                   this.data[key] = value[key];
              }
        } );
        this.subject.next( this );
      }else {
        this.validate();
      }
  }
  validate() {
     for (const i in this.validateForm.controls) {
       this.validateForm.controls[ i ].markAsDirty();
     }
  }
}
src/app/routes/sensors/sensors.module.ts
@@ -1,7 +1,13 @@
import { SensorsService } from '@business/services/http/sensors.service';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BasicInfoComponent } from './basic-info/basic-info.component';
import {RouterModule, Routes} from '@angular/router';
import { PipeModule } from '@business/pipe/pipe.module';
import { SharedModule } from '@shared/shared.module';
import { _HttpClient } from '@delon/theme/services/http/http.client';
import { FormBuilder } from '@angular/forms';
import { SensorEditComponent } from './basic-info/sensor-edit/sensor-edit.component';
const routes: Routes = [
  {
@@ -11,12 +17,20 @@
    ]
  }
];
const COMPONENTS_NOROUNT = [SensorEditComponent];
@NgModule({
  imports: [
    // 管道模块必须当前模块导入
    PipeModule,
    CommonModule,
    SharedModule,
    RouterModule.forChild(routes)
  ],
  declarations: [BasicInfoComponent]
  declarations: [
    BasicInfoComponent,
    SensorEditComponent
  ],
  providers: [SensorsService, _HttpClient, FormBuilder],
  entryComponents: COMPONENTS_NOROUNT
})
export class SensorsModule { }
src/app/routes/systems/account/account.component.ts
@@ -3,7 +3,7 @@
import {ModalHelper} from '@delon/theme';
import {HttpClient} from '@angular/common/http';
import {environment} from '../../../../environments/environment';
import {DateService} from '../../../core/services/date.service';
import {DateService} from '@business/services/util/date.service';
import {AccountEditComponent} from './account-edit/account-edit.component';
@Component({
  selector: 'app-account',
src/app/routes/systems/organization/organization-config/organization-config.component.html
New file
@@ -0,0 +1,67 @@
<form>
    <p>111111111111111111111111111<br/>
      222222222222222222222222222<br/>
      333333333333333333333333333<br/>
   </p>
   <p>111111111111111111111111111<br/>
       222222222222222222222222222<br/>
       333333333333333333333333333<br/>
    </p>
    <p>111111111111111111111111111<br/>
      222222222222222222222222222<br/>
      333333333333333333333333333<br/>
   </p>
   <p>111111111111111111111111111<br/>
       222222222222222222222222222<br/>
       333333333333333333333333333<br/>
    </p>
    <p>111111111111111111111111111<br/>
      222222222222222222222222222<br/>
      333333333333333333333333333<br/>
   </p>
   <p>111111111111111111111111111<br/>
       222222222222222222222222222<br/>
       333333333333333333333333333<br/>
    </p>
    <p>111111111111111111111111111<br/>
      222222222222222222222222222<br/>
      333333333333333333333333333<br/>
   </p>
   <p>111111111111111111111111111<br/>
       222222222222222222222222222<br/>
       333333333333333333333333333<br/>
    </p>
    <p>111111111111111111111111111<br/>
      222222222222222222222222222<br/>
      333333333333333333333333333<br/>
   </p>
   <p>111111111111111111111111111<br/>
       222222222222222222222222222<br/>
       333333333333333333333333333<br/>
    </p>
    <p>111111111111111111111111111<br/>
      222222222222222222222222222<br/>
      333333333333333333333333333<br/>
   </p>
   <p>111111111111111111111111111<br/>
       222222222222222222222222222<br/>
       333333333333333333333333333<br/>
    </p>
    <p>111111111111111111111111111<br/>
      222222222222222222222222222<br/>
      333333333333333333333333333<br/>
   </p>
   <p>111111111111111111111111111<br/>
       222222222222222222222222222<br/>
       333333333333333333333333333<br/>
    </p>
   <footer-toolbar errorCollect>
        <button nz-button type="button" (click)="backToList()">返回</button>
        <button nz-button [nzType]="'primary'" [nzLoading]="isSaving">
          <span>
            保存
            <span *ngIf="isSaving">中</span>
          </span>
        </button>
    </footer-toolbar>
</form>
src/app/routes/systems/organization/organization-config/organization-config.component.ts
New file
@@ -0,0 +1,24 @@
import { Organization } from '@business/entity/data';
import { Component, OnInit } from '@angular/core';
import { OrganizationService } from '@business/services/http/organization.service';
@Component({
  selector: 'app-organization-config',
  templateUrl: './organization-config.component.html',
  styles: []
})
export class OrganizationConfigComponent implements OnInit {
  organization: Organization;
  constructor(
    private organizationService: OrganizationService
  ) {
      console.log(this.organizationService.data);
  }
  ngOnInit() {
  }
  backToList() {
    this.organizationService.handle = 'list';
    this.organizationService.title = '组织列表';
  }
}
src/app/routes/systems/organization/organization-edit/organization-edit.component.html
@@ -14,7 +14,7 @@
      <label>级别</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="4" nzHasFeedback>
      <nz-select style="width: 120px;" formControlName="rank" [nzPlaceHolder]="'选择 级别'">
      <nz-select style="width:100%;" formControlName="rank" [nzPlaceHolder]="'选择 级别'">
        <nz-option *ngFor="let option of rankOptions" [nzLabel]="option.label" [nzValue]="option.value" [nzDisabled]="option.disabled">
        </nz-option>
      </nz-select>
@@ -58,15 +58,16 @@
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label nz-form-item-required>创建时间</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="7" nzHasFeedback>
      <nz-datepicker nzSize="large"  style="width: 90%;" formControlName="createTime" nzShowTime  [nzPlaceHolder]="'选择时间'"
    <div nz-form-control nz-col [nzSpan]="6" nzHasFeedback>
      <nz-datepicker nzSize="large"  style="width:100%;" formControlName="createTime" nzShowTime  [nzPlaceHolder]="'选择时间'"
      [nzFormat]="'YYYY-MM-DD HH:mm:ss'" ></nz-datepicker>
    </div>
    <div nz-form-control nz-col [nzSpan]="1" nzHasFeedback></div>
    <div nz-form-label nz-col [nzSm]="4" [nzXs]="24">
      <label nz-form-item-required>到期时间</label>
    </div>
    <div nz-form-control nz-col [nzSpan]="7" nzHasFeedback>
      <nz-datepicker nzSize="large"  style="width: 90%;" formControlName="expireTime" nzShowTime  [nzPlaceHolder]="'选择时间'"
      <nz-datepicker nzSize="large"  style="width:100%;" formControlName="expireTime" nzShowTime  [nzPlaceHolder]="'选择时间'"
      [nzFormat]="'YYYY-MM-DD HH:mm:ss'" ></nz-datepicker>
    </div>
  </div>
src/app/routes/systems/organization/organization-edit/organization-edit.component.ts
@@ -1,5 +1,5 @@
import { Organization } from './../organization.component';
import { AreacodeService } from './../../../../core/services/areacode.service';
import { Organization } from '@business/entity/data';
import { AreacodeService } from '@business/services/http/areacode.service';
import { Component, OnInit } from '@angular/core';
import { NzMessageService, NzModalSubject } from 'ng-zorro-antd';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
src/app/routes/systems/organization/organization-list/organization-list.component.html
New file
@@ -0,0 +1,66 @@
<nz-card [nzBordered]="false">
        <div class="mb-md">
                <button nz-button (click)="addOrModify()" [nzType]="'primary'" [nzSize]="'large'">
                    <i class="anticon anticon-plus"></i><span>新建</span>
                </button>
                <ng-container *ngIf="grid.selectedIndexs.length > 0"> &nbsp;
                <button nz-button [nzSize]="'large'" (click)="deleteSelected()">批量删除</button>
                </ng-container>
                <nz-input [ngStyle]="{'width': '280px','float':'right'}" [(ngModel)]="queryMap.value" name=""  [nzPlaceHolder]="queryMap.text"
                (keyup)="queryTextChanged($event)" (change)="queryTextChanged($event)" ></nz-input>
        </div>
        <div class="mb-md">
                <nz-alert *ngIf="grid.selectedIndexs.length > 0" [nzType]="'info'" [nzShowIcon]="true">
                    <span alert-body>
                         已选择<strong class="text-primary">{{grid.selectedIndexs.length}}</strong>项
                    </span>
                </nz-alert>
    </div>
    <nz-table #nzTable
              [nzAjaxData]="grid.data"
              [nzTotal]="grid.total"
              [(nzPageIndex)]="grid.pageIndex"
              [(nzPageSize)]="grid.pageSize"
              [nzLoading]="grid.loading"
              [nzShowTotal]="true"
              (nzPageIndexChange)="load()">
              <thead nz-thead>
                  <tr>
                    <th nz-th [nzCheckbox]="true">
                        <label nz-checkbox [(ngModel)]="grid.allChecked" [nzIndeterminate]="grid.indeterminate" (ngModelChange)="grid.checkAll($event)"></label>
                    </th>
                    <th nz-th *ngFor="let col of grid.columns"
                     [ngStyle]="{'width':col.width,'text-align':col['align'] === undefined?'left':col.align}" >
                           <span>{{ col.text }}</span>
                           <nz-table-sort *ngIf="col.isSort"   [(nzValue)]="col.sort" (nzValueChange)="sort(col.name,$event)"></nz-table-sort>
                    </th>
                    <th nz-th><span>操作</span></th>
                  </tr>
              </thead>
              <tbody nz-tbody>
                <tr nz-tbody-tr *ngFor="let row of nzTable.data">
                    <td nz-td [nzCheckbox]="true">
                       <label nz-checkbox [(ngModel)]="row.checked" (ngModelChange)="grid.refreshStatus($event)"></label>
                    </td>
                    <td nz-td *ngFor="let col of grid.columns"
                    [ngStyle]="{'width':col.width,'text-align':col['align'] === undefined?'left':col.align}">
                        <span [ngSwitch]="col.type">
                             <!-- 要使用管道,无法自动生成 -->
                            <span *ngSwitchDefault> {{ row[col.name]|tyepHandle:col:row }} </span>
                             <!-- 要使用管道,无法自动生成 -->
                        </span>
                    </td>
                    <td nz-td>
                            <a (click)="config(row)">配置</a>
                            <span nz-table-divider></span>
                            <a (click)="addOrModify(row)">编辑</a>
                            <span nz-table-divider></span>
                              <nz-popconfirm [nzTitle]="'确定要删除该'+grid.title+'吗?'" [nzOkText]="'Yes'" [nzCancelText]="'No'" (nzOnConfirm)="delete(row.id)" >
                                <a nz-popconfirm>删除</a>
                              </nz-popconfirm>
                     </td>
                </tr>
              </tbody>
              </nz-table>
  </nz-card>
src/app/routes/systems/organization/organization-list/organization-list.component.ts
New file
@@ -0,0 +1,212 @@
import { Router } from '@angular/router';
import { ModalHelper } from '@delon/theme';
import { NzModalService, NzMessageService } from 'ng-zorro-antd';
import { OrganizationService } from '@business/services/http/organization.service';
import { Grid, Column, PageBean } from '@business/entity/grid';
import { Organization } from '@business/entity/data';
import { Component, OnInit } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Types } from '@business/enum/types.enum';
import { OrganizationEditComponent } from 'app/routes/systems/organization/organization-edit/organization-edit.component';
@Component({
  selector: 'app-organization-list',
  templateUrl: './organization-list.component.html',
  styles: []
})
export class OrganizationListComponent implements OnInit {
  private organization: Organization;
  grid: Grid<Organization> = new Grid(null);
  queryMap = { text: '请输入名称', value: ''};
  queryTextStream: Subject<string> = new Subject<string>();
    private initPage() {
    this.organization =  {
      name: {
        text: '名称',
        width: '120px'
      },
      rank: {
        text: '级别',
        width: '80px',
        format: (value: any, col: Column, row: any) => {
          const item = [
            { value: 0, label: '企业' },
            { value: 1, label: '乡镇科级' },
            { value: 2, label: '县处级' },
            { value: 3, label: '司厅局级' },
            { value: 4, label: '省部级' },
            { value: 5, label: '国家级' }
          ].filter( (rankItem: {value: number, label: string }) => {
            return rankItem.value === value;
          });
          return item != null && item.length === 1 ? item[0].label : '';
        }
      },
      telephone: {
        text: '电话',
        width: '120px'
      },
      address: {
        text: '地址',
        width: '300px',
        format: (value: any, col: Column, row: any) => {
              value = value == null ? '' : value ;
              if (row['areaNames'] != null) {
                return row['areaNames']['provinceName'] + row['areaNames']['cityName'] + row['areaNames']['areaName'] + value;
              } else {
                return value;
              }
        }
      },
      createTime: {
        text: '创建时间',
        width: '100px',
        type: {
         name: Types.Date,
         format: 'YYYY-MM-DD HH:mm:ss'
        },
         isSort: true
      },
      expireTime: {
        text: '到期时间',
        width: '100px',
        type: {
         name: Types.Date,
         format: 'YYYY-MM-DD HH:mm:ss'
        },
         isSort: true
      }
    };
    this.grid.title = '组织';
    this.grid.setColumns(this.organization);
    this.grid.pageSize = 10;
  }
  constructor(
    private organizationService: OrganizationService,
    private confirmServ: NzModalService,
    public msgSrv: NzMessageService,
    private modalHelper: ModalHelper,
    private router: Router,
  ) {}
  ngOnInit() {
    this.initPage();
    this.queryTextStream
    .debounceTime(500)
    .distinctUntilChanged()
    .subscribe(queryText => {
        this.load();
    });
  }
  queryTextChanged($event) {
      this.queryTextStream.next(this.queryMap.value);
  }
  load(reload: boolean = false) {
    if (reload) {
      this.grid.pageIndex = 1 ;
    }
    // 延时加载避免ExpressionChangedAfterItHasBeenCheckedError
    setTimeout(() => {
      this.grid.loading = true;
     }, 1);
    this.organizationService.getPagingList(this.grid, this.queryMap.value).subscribe(
       (res: PageBean) => {
            this.grid.loading = true;
            if (res != null && res.data != null) {
              this.grid.initData(res);
              this.grid.refreshStatus();
              setTimeout(() => {
                this.grid.loading = false;
               }, 1);
            }
       }
    );
  }
// rowData为null时,为新增
  addOrModify(d) {
    const data = {};
    if ( d != null) {
      Object.assign(data, d);
    }
    const cols = this.organization;
    this.modalHelper.static(OrganizationEditComponent, { cols , data }).subscribe(
      ( ret: { data: any, close: Function} ) => {
      // 修改状态
      if (ret.data['index'] != null ) {
          const index: number = ret.data['index'] ;
          const origData = this.grid.getData()[index];
          const isModified =  Object.keys(origData).some(
            (key: string) => {
                return ret.data[key] !== origData[key];
            }
          );
          // 未作修改
          if (!isModified) {
            ret.close();
            this.msgSrv.success('组织未作任何修改!');
            return;
          }
      }
      this.organizationService.save(ret.data).subscribe(
         ( res: any) => {
            if (res.code === 1) {
              this.load();
              ret.close();
              this.msgSrv.success('组织保存成功!');
            }
         }
      );
    });
  }
  delete(...id: number[]) {
    this.organizationService.delete( ...id ).subscribe(
      ( res: any) => {
         if (res.code === 1) {
           this.load();
           this.msgSrv.success('设备型号删除成功!');
         }
      }
   );
  }
   deleteSelected() {
    this.confirmServ.confirm({
      title: '批量删除',
      content: '注意:数据一旦删除,将不可恢复!',
      okText: '确定',
      cancelText: '取消'
    }).on('onOk', () => {
       if (this.grid.selectedIndexs != null && this.grid.selectedIndexs.length > 0) {
          const ids = this.grid.selectedIndexs.map(
              (index: number) => {
                  const id = this.grid.data[index].id;
                  return Number.parseInt(id);
              }
          );
          this.delete( ...ids );
       }
    });
  }
  sort(field: string, value: string) {
    // 删除当前field
    this.grid.sorts = this.grid.sorts.filter(
       (fn: string) => {
          return fn !== field;
       }
     );
     // 如果value不为null,在排序数组最后加上filed
     if ( value != null ) {
        this.grid.sorts.push(field);
     }
     this.load();
  }
  config(row) {
      this.organizationService.handle = 'config' ;
      this.organizationService.data = row;
      this.organizationService.title = '组织配置';
  }
}
src/app/routes/systems/organization/organization.component.html
@@ -1,65 +1,8 @@
<pro-header [title]="grid.title"></pro-header>
<nz-card [nzBordered]="false">
        <div class="mb-md">
                <button nz-button (click)="addOrModify()" [nzType]="'primary'" [nzSize]="'large'">
                    <i class="anticon anticon-plus"></i><span>新建</span>
                </button>
                <ng-container *ngIf="grid.selectedIndexs.length > 0"> &nbsp;
                <button nz-button [nzSize]="'large'" (click)="deleteSelected()">批量删除</button>
                </ng-container>
                <nz-input [ngStyle]="{'width': '280px','float':'right'}" [(ngModel)]="queryMap.value" name=""  [nzPlaceHolder]="queryMap.text"
                (keyup)="queryTextChanged($event)" (change)="queryTextChanged($event)" ></nz-input>
        </div>
        <div class="mb-md">
                <nz-alert *ngIf="grid.selectedIndexs.length > 0" [nzType]="'info'" [nzShowIcon]="true">
                    <span alert-body>
                         已选择<strong class="text-primary">{{grid.selectedIndexs.length}}</strong>项
                    </span>
                </nz-alert>
    </div>
    <nz-table #nzTable
              [nzAjaxData]="grid.data"
              [nzTotal]="grid.total"
              [(nzPageIndex)]="grid.pageIndex"
              [(nzPageSize)]="grid.pageSize"
              [nzLoading]="grid.loading"
              [nzShowTotal]="true"
              (nzPageIndexChange)="load()">
              <thead nz-thead>
                  <tr>
                    <th nz-th [nzCheckbox]="true">
                        <label nz-checkbox [(ngModel)]="grid.allChecked" [nzIndeterminate]="grid.indeterminate" (ngModelChange)="grid.checkAll($event)"></label>
                    </th>
                    <th nz-th *ngFor="let col of grid.columns"
                     [ngStyle]="{'width':col.width,'text-align':col['align'] === undefined?'left':col.align}" >
                           <span>{{ col.text }}</span>
                           <nz-table-sort *ngIf="col.isSort"   [(nzValue)]="col.sort" (nzValueChange)="sort(col.name,$event)"></nz-table-sort>
                    </th>
                    <th nz-th><span>操作</span></th>
                  </tr>
              </thead>
              <tbody nz-tbody>
                <tr nz-tbody-tr *ngFor="let row of nzTable.data">
                    <td nz-td [nzCheckbox]="true">
                       <label nz-checkbox [(ngModel)]="row.checked" (ngModelChange)="grid.refreshStatus($event)"></label>
                    </td>
                    <td nz-td *ngFor="let col of grid.columns"
                    [ngStyle]="{'width':col.width,'text-align':col['align'] === undefined?'left':col.align}">
                        <span [ngSwitch]="col.type">
                             <!-- 要使用管道,无法自动生成 -->
                            <span *ngSwitchDefault> {{ row[col.name]|tyepHandle:col:row }} </span>
                             <!-- 要使用管道,无法自动生成 -->
                        </span>
                    </td>
                    <td nz-td>
                            <a (click)="addOrModify(row)">编辑</a>
                            <span nz-table-divider></span>
                              <nz-popconfirm [nzTitle]="'确定要删除该用户吗?'" [nzOkText]="'Yes'" [nzCancelText]="'No'" (nzOnConfirm)="delete(row.id)" >
                                <a nz-popconfirm>删除</a>
                              </nz-popconfirm>
                     </td>
                </tr>
              </tbody>
              </nz-table>
  </nz-card>
<pro-header [title]="organizationService.title"></pro-header>
<nz-card [nzBordered]="false" [nzNoHovering]="true">
        <ng-template #body>
            <app-organization-list *ngIf="organizationService.handle=='list'"></app-organization-list>
            <app-organization-config *ngIf="organizationService.handle=='config'"></app-organization-config>
        </ng-template>
</nz-card>
  
src/app/routes/systems/organization/organization.component.ts
@@ -1,36 +1,7 @@
import { DataType, AreaNames } from './../../../core/entity/grid';
import { Version, ValueTransformer } from '@angular/compiler/src/util';
import { Subject } from 'rxjs/Subject';
import { ToolsService } from '@core/services/tools.service';
import { OrganizationService } from '@business/services/http/organization.service';
import { Component, OnInit } from '@angular/core';
import { NzMessageService, NzModalService } from 'ng-zorro-antd';
import { ModalHelper } from '@delon/theme';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../../../environments/environment';
import { DateService } from '@core/services/date.service';
import { VersionService } from 'app/routes/devices/version/version.service';
import { Types } from '@core/enum/types.enum';
import { Column, Grid, PageBean } from '@core/entity/grid';
import { filter } from 'rxjs/operators/filter';
import { OrganizationEditComponent } from './organization-edit/organization-edit.component';
import { OrganizationService } from 'app/routes/systems/organization/organization.service';
export interface Organization {
  address?: Column|any;
  areaCode?: Column|any;
  cityCode?: Column|any;
  createTime?: Column|any;
  description?: Column|any;
  email?: Column|any;
  expireTime?: Column|any;
  id?: Column|any;
  isDelete?: Column|any;
  name?: Column|any;
  provinceCode?: Column|any;
  rank?: Column|any;
  telephone?: Column|any;
  areaNames?: AreaNames|any ;
}
@Component({
  selector: 'app-organization',
@@ -38,198 +9,10 @@
  styles: []
})
export class OrganizationComponent implements OnInit {
  private organization: Organization;
  grid: Grid<Organization> = new Grid(null);
  queryMap = { text: '请输入名称', value: ''};
  queryTextStream: Subject<string> = new Subject<string>();
    private initPage() {
    this.organization =  {
      name: {
        text: '名称',
        width: '120px'
      },
      rank: {
        text: '级别',
        width: '80px',
        format: (value: any, col: Column, row: any) => {
          const item = [
            { value: 0, label: '企业' },
            { value: 1, label: '乡镇科级' },
            { value: 2, label: '县处级' },
            { value: 3, label: '司厅局级' },
            { value: 4, label: '省部级' },
            { value: 5, label: '国家级' }
          ].filter( (rankItem: {value: number, label: string }) => {
            return rankItem.value === value;
          });
          return item != null && item.length === 1 ? item[0].label : '';
        }
      },
      telephone: {
        text: '电话',
        width: '180px'
      },
      address: {
        text: '地址',
        width: '300px',
        format: (value: any, col: Column, row: any) => {
              value = value == null ? '' : value ;
              if (row['areaNames'] != null) {
                return row['areaNames']['provinceName'] + row['areaNames']['cityName'] + row['areaNames']['areaName'] + value;
              } else {
                return value;
              }
        }
      },
      createTime: {
        text: '创建时间',
        width: '100px',
        type: {
         name: Types.Date,
         format: 'YYYY-MM-DD HH:mm:ss'
        },
         isSort: true
      },
      expireTime: {
        text: '到期时间',
        width: '100px',
        type: {
         name: Types.Date,
         format: 'YYYY-MM-DD HH:mm:ss'
        },
         isSort: true
      }
    };
    this.grid.title = '设备型号';
    this.grid.setColumns(this.organization);
    this.grid.pageSize = 10;
  ngOnInit(): void {
  }
  constructor(
    private organizationService: OrganizationService,
    private confirmServ: NzModalService,
    public msgSrv: NzMessageService,
    private modalHelper: ModalHelper,
    private organizationService: OrganizationService
  ) {}
  ngOnInit() {
    this.initPage();
    this.queryTextStream
    .debounceTime(500)
    .distinctUntilChanged()
    .subscribe(queryText => {
        this.load();
    });
  }
  queryTextChanged($event) {
      this.queryTextStream.next(this.queryMap.value);
  }
  load(reload: boolean = false) {
    if (reload) {
      this.grid.pageIndex = 1 ;
    }
    // 延时加载避免ExpressionChangedAfterItHasBeenCheckedError
    setTimeout(() => {
      this.grid.loading = true;
     }, 1);
    this.organizationService.getPagingList(this.grid, this.queryMap.value).subscribe(
       (res: PageBean) => {
            this.grid.loading = true;
            if (res != null && res.data != null) {
              this.grid.initData(res);
              this.grid.refreshStatus();
              setTimeout(() => {
                this.grid.loading = false;
               }, 1);
            }
       }
    );
  }
// rowData为null时,为新增
  addOrModify(d) {
    const data = {};
    if ( d != null) {
      Object.assign(data, d);
    }
    const cols = this.organization;
    this.modalHelper.static(OrganizationEditComponent, { cols , data }).subscribe(
      ( ret: { data: any, close: Function} ) => {
      // 修改状态
      if (ret.data['index'] != null ) {
          const index: number = ret.data['index'] ;
          const origData = this.grid.getData()[index];
          const isModified =  Object.keys(origData).some(
            (key: string) => {
                return ret.data[key] !== origData[key];
            }
          );
          // 未作修改
          if (!isModified) {
            ret.close();
            this.msgSrv.success('组织未作任何修改!');
            return;
          }
      }
      this.organizationService.save(ret.data).subscribe(
         ( res: any) => {
            if (res.code === 1) {
              this.load();
              ret.close();
              this.msgSrv.success('组织保存成功!');
            }
         }
      );
    });
  }
  delete(...id: number[]) {
    this.organizationService.delete( ...id ).subscribe(
      ( res: any) => {
         if (res.code === 1) {
           this.load();
           this.msgSrv.success('设备型号删除成功!');
         }
      }
   );
  }
   deleteSelected() {
    this.confirmServ.confirm({
      title: '批量删除',
      content: '注意:数据一旦删除,将不可恢复!',
      okText: '确定',
      cancelText: '取消'
    }).on('onOk', () => {
       if (this.grid.selectedIndexs != null && this.grid.selectedIndexs.length > 0) {
          const ids = this.grid.selectedIndexs.map(
              (index: number) => {
                  const id = this.grid.data[index].id;
                  return Number.parseInt(id);
              }
          );
          this.delete( ...ids );
       }
    });
  }
  sort(field: string, value: string) {
    // 删除当前field
    this.grid.sorts = this.grid.sorts.filter(
       (fn: string) => {
          return fn !== field;
       }
     );
     // 如果value不为null,在排序数组最后加上filed
     if ( value != null ) {
        this.grid.sorts.push(field);
     }
     this.load();
  }
  reset(ls: any[]) {
    for (const item of ls) item.value = false;
    this.load(true);
  }
}
src/app/routes/systems/systems.module.ts
@@ -5,15 +5,16 @@
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { SharedModule } from '@shared/shared.module';
import { AccountComponent } from './account/account.component';
import { AccountEditComponent } from './account/account-edit/account-edit.component';
import { OrganizationComponent } from './organization/organization.component';
import { PipeModule } from '@core/pipe/pipe.module';
import { PipeModule } from '@business/pipe/pipe.module';
import { OrganizationEditComponent } from './organization/organization-edit/organization-edit.component';
import { ToolsService } from '@core/services/tools.service';
import { OrganizationService } from 'app/routes/systems/organization/organization.service';
import { AreacodeService } from '@core/services/areacode.service';
import { ToolsService } from '@business/services/util/tools.service';
import { OrganizationService } from '@business/services/http/organization.service';
import { AreacodeService } from '@business/services/http/areacode.service';
import { OrganizationConfigComponent } from './organization/organization-config/organization-config.component';
import { OrganizationListComponent } from './organization/organization-list/organization-list.component';
const routes: Routes = [
  {
@@ -39,7 +40,9 @@
    ...COMPONENTS_NOROUNT,
    AccountComponent,
    AccountEditComponent,
    OrganizationComponent
    OrganizationComponent,
    OrganizationConfigComponent,
    OrganizationListComponent
  ],
  providers: [ToolsService, OrganizationService, _HttpClient, FormBuilder, AreacodeService],
  entryComponents: COMPONENTS_NOROUNT
src/app/routes/users/alarm-user/alarm-user.component.ts
@@ -3,7 +3,7 @@
import {ModalHelper} from '@delon/theme';
import {HttpClient} from '@angular/common/http';
import {environment} from '../../../../environments/environment';
import {DateService} from '../../../core/services/date.service';
import {DateService} from '@business/services/util/date.service';
import {AlarmUserEditComponent} from './alarm-user-edit/alarm-user-edit.component';
@Component({
src/app/routes/users/installer/edit/edit.component.ts
@@ -2,8 +2,8 @@
import {Component, OnInit} from '@angular/core';
import {ModalHelper} from '@delon/theme';
import {HttpClient} from '@angular/common/http';
import {environment} from '../../../../../environments/environment';
import {DateService} from '../../../../core/services/date.service';
import {environment} from 'environments/environment';
import {DateService} from '@business/services/util/date.service';
import {FormGroup, FormBuilder, FormControl, Validators} from '@angular/forms';
@Component({
src/app/routes/users/installer/installer.component.ts
@@ -4,7 +4,7 @@
import {HttpClient} from '@angular/common/http';
import {UserInstallerEditComponent} from './edit/edit.component';
import {environment} from '../../../../environments/environment';
import {DateService} from '../../../core/services/date.service';
import {DateService} from '@business/services/util/date.service';
@Component({
  selector: 'app-installer',
src/app/shared/shared.module.ts
@@ -16,19 +16,6 @@
import { TranslateModule } from '@ngx-translate/core';
import { I18NService } from '@core/i18n/i18n.service';
// region: zorro modules
// Statics
import 'rxjs/add/observable/throw';
// Operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/filter';
import {
    // LoggerModule,
    // NzLocaleModule,
src/tsconfig.app.json
@@ -9,7 +9,9 @@
      "@shared": [ "app/shared" ],
      "@shared/*": [ "app/shared/*" ],
      "@core": [ "app/core/" ],
      "@core/*": [ "app/core/*" ]
      "@core/*": [ "app/core/*" ],
      "@business": [ "app/business/" ],
      "@business/*": [ "app/business/*" ]
    }
  },
  "exclude": [
src/tsconfig.spec.json
@@ -13,7 +13,9 @@
      "@shared": [ "app/shared" ],
      "@shared/*": [ "app/shared/*" ],
      "@core": [ "app/core/" ],
      "@core/*": [ "app/core/*" ]
      "@core/*": [ "app/core/*" ],
      "@business": [ "app/business/" ],
      "@business/*": [ "app/business/*" ]
    }
  },
  "files": [
tsconfig.json
@@ -22,7 +22,9 @@
        "@shared": [ "app/shared" ],
        "@shared/*": [ "app/shared/*" ],
        "@core": [ "app/core/" ],
        "@core/*": [ "app/core/*" ]
        "@core/*": [ "app/core/*" ],
        "@business": [ "app/business/" ],
        "@business/*": [ "app/business/*" ]
    }
  }
}